diff --git a/.ci/license-header.sh b/.ci/license-header.sh index 6b19f91185..fcc2c1f583 100755 --- a/.ci/license-header.sh +++ b/.ci/license-header.sh @@ -7,7 +7,7 @@ EXCLUDE_FILES="CPM.cmake CPMUtil.cmake GetSCMRev.cmake renderdoc_app.h tools/cpm tools/shellcheck.sh tools/update-cpm.sh tools/windows/vcvarsall.sh externals/stb externals/glad externals/getopt externals/gamemode externals/FidelityFX-FSR externals/demangle externals/bc_decoder externals/cmake-modules" # license header constants, please change when needed :)))) -YEAR=2026 +YEAR=$(date "+%Y") HOLDER="Eden Emulator Project" LICENSE="GPL-3.0-or-later" @@ -112,10 +112,10 @@ for file in $FILES; do [ "$excluded" = "true" ] && continue case "$file" in - *.cmake|*.sh|*CMakeLists.txt) + *.cmake|*.sh|*.ps1|*.py|*.rb|*.perl|*.pl|*.nix|*CMakeLists.txt) begin="#" ;; - *.kt*|*.cpp|*.h|*.qml) + *.kt|*.kts|*.cpp|*.h|*.qml|*.c|*.hpp|*.hxx|*.cxx|*.h.in|*.inc) begin="//" ;; *) @@ -185,11 +185,12 @@ if [ "$UPDATE" = "true" ]; then for file in $SRC_FILES $OTHER_FILES; do case $(basename -- "$file") in - *.cmake|*CMakeLists.txt) + # Windows Powershell wont use shebangs + *.cmake|*.ps1|*CMakeLists.txt) begin="#" shell="false" ;; - *.sh) + *.sh|*.py|*.rb|*.perl|*.pl|*.nix) begin="#" shell=true ;; diff --git a/.ci/source.sh b/.ci/source.sh deleted file mode 100755 index cbdacd1cd7..0000000000 --- a/.ci/source.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash -ex - -# git-archive-all -export PATH="$PATH:/home/$USER/.local/bin" - -GITDATE="`git show -s --date=short --format='%ad' | sed 's/-//g'`" -GITREV="`git show -s --format='%h'`" -REV_NAME="eden-unified-source-${GITDATE}-${GITREV}" - -COMPAT_LIST='dist/compatibility_list/compatibility_list.json' - -mkdir artifacts - -touch "${COMPAT_LIST}" -git describe --abbrev=0 --always HEAD > GIT-COMMIT -git describe --tags HEAD > GIT-TAG || echo 'unknown' > GIT-TAG -git-archive-all --include "${COMPAT_LIST}" --include GIT-COMMIT --include GIT-TAG --force-submodules artifacts/"${REV_NAME}.tar" - -cd artifacts/ -xz -T0 -9 "${REV_NAME}.tar" -sha256sum "${REV_NAME}.tar.xz" > "${REV_NAME}.tar.xz.sha256sum" -cd .. diff --git a/.forgejo/workflows/translations.yml b/.forgejo/workflows/translations.yml index 4a2d52e745..fe2e82dff3 100644 --- a/.forgejo/workflows/translations.yml +++ b/.forgejo/workflows/translations.yml @@ -52,7 +52,7 @@ jobs: } EOF - curl -X 'POST' \ + curl -XPOST \ 'https://git.eden-emu.dev/api/v1/repos/eden-emu/eden/pulls' \ -H 'accept: application/json' \ -H 'Authorization: Bearer ${{ secrets.CI_FJ_TOKEN }}' \ diff --git a/.forgejo/workflows/update-deps.yml b/.forgejo/workflows/update-deps.yml index 154328f906..638fcd4bdb 100644 --- a/.forgejo/workflows/update-deps.yml +++ b/.forgejo/workflows/update-deps.yml @@ -1,4 +1,4 @@ -name: update-deps +name: Update Dependencies on: # saturday at noon @@ -7,7 +7,7 @@ on: workflow_dispatch: jobs: - tx-update: + update-deps: runs-on: source steps: - uses: actions/checkout@v4 @@ -24,18 +24,21 @@ jobs: git remote set-url origin ci:eden-emu/eden.git DATE=$(date +"%b %d") + TIMESTAMP=$(date +"%s") echo "DATE=$DATE" >> "$GITHUB_ENV" + echo "TIMESTAMP=$TIMESTAMP" >> "$GITHUB_ENV" - git switch -c update-deps-$DATE + git switch -c update-deps-$TIMESTAMP tools/cpmutil.sh package update -ac git push - name: Create PR run: | + set -x TITLE="[externals] Dependency update for $DATE" BODY="$(git show -s --format='%b')" BASE=master - HEAD=update-deps-$DATE + HEAD=update-deps-$TIMESTAMP cat << EOF > data.json { @@ -46,7 +49,7 @@ jobs: } EOF - curl -X 'POST' \ + curl -XPOST \ 'https://git.eden-emu.dev/api/v1/repos/eden-emu/eden/pulls' \ -H 'accept: application/json' \ -H 'Authorization: Bearer ${{ secrets.CI_FJ_TOKEN }}' \ diff --git a/.gitignore b/.gitignore index 67bdd8adf4..75e728babe 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,6 @@ # Build directory /[Bb]uild*/ doc-build/ -out/ AppDir/ uruntime diff --git a/.patch/httplib/0002-fix-zstd.patch b/.patch/httplib/0002-fix-zstd.patch deleted file mode 100644 index f54485ea53..0000000000 --- a/.patch/httplib/0002-fix-zstd.patch +++ /dev/null @@ -1,89 +0,0 @@ -From 509be32bbfa6eb95014860f7c9ea6d45c8ddaa56 Mon Sep 17 00:00:00 2001 -From: crueter -Date: Sun, 8 Mar 2026 15:11:12 -0400 -Subject: [PATCH] [cmake] Simplify zstd find logic, and support pre-existing - zstd target - -Some deduplication work on the zstd required/if-available logic. Also -adds support for pre-existing `zstd::libzstd` which is useful for -projects that bundle their own zstd in a way that doesn't get caught by -`CONFIG` - -Signed-off-by: crueter ---- - CMakeLists.txt | 46 ++++++++++++++++++++++++++-------------------- - 1 file changed, 26 insertions(+), 20 deletions(-) - -diff --git a/CMakeLists.txt b/CMakeLists.txt -index 1874e36be0..8d31198006 100644 ---- a/CMakeLists.txt -+++ b/CMakeLists.txt -@@ -241,28 +241,34 @@ endif() - # NOTE: - # zstd < 1.5.6 does not provide the CMake imported target `zstd::libzstd`. - # Older versions must be consumed via their pkg-config file. --if(HTTPLIB_REQUIRE_ZSTD) -- find_package(zstd 1.5.6 CONFIG) -- if(NOT zstd_FOUND) -- find_package(PkgConfig REQUIRED) -- pkg_check_modules(zstd REQUIRED IMPORTED_TARGET libzstd) -- add_library(zstd::libzstd ALIAS PkgConfig::zstd) -- endif() -- set(HTTPLIB_IS_USING_ZSTD TRUE) --elseif(HTTPLIB_USE_ZSTD_IF_AVAILABLE) -- find_package(zstd 1.5.6 CONFIG QUIET) -- if(NOT zstd_FOUND) -- find_package(PkgConfig QUIET) -- if(PKG_CONFIG_FOUND) -- pkg_check_modules(zstd QUIET IMPORTED_TARGET libzstd) -- -- if(TARGET PkgConfig::zstd) -+if (HTTPLIB_REQUIRE_ZSTD) -+ set(HTTPLIB_ZSTD_REQUESTED ON) -+ set(HTTPLIB_ZSTD_REQUIRED REQUIRED) -+elseif (HTTPLIB_USE_ZSTD_IF_AVAILABLE) -+ set(HTTPLIB_ZSTD_REQUESTED ON) -+ set(HTTPLIB_ZSTD_REQUIRED QUIET) -+endif() -+ -+if (HTTPLIB_ZSTD_REQUESTED) -+ if (TARGET zstd::libzstd) -+ set(HTTPLIB_IS_USING_ZSTD TRUE) -+ else() -+ find_package(zstd 1.5.6 CONFIG QUIET) -+ -+ if (NOT zstd_FOUND) -+ find_package(PkgConfig ${HTTPLIB_ZSTD_REQUIRED}) -+ pkg_check_modules(zstd ${HTTPLIB_ZSTD_REQUIRED} IMPORTED_TARGET libzstd) -+ -+ if (TARGET PkgConfig::zstd) - add_library(zstd::libzstd ALIAS PkgConfig::zstd) - endif() - endif() -+ -+ # This will always be true if zstd is required. -+ # If zstd *isn't* found when zstd is set to required, -+ # CMake will error out earlier in this block. -+ set(HTTPLIB_IS_USING_ZSTD ${zstd_FOUND}) - endif() -- # Both find_package and PkgConf set a XXX_FOUND var -- set(HTTPLIB_IS_USING_ZSTD ${zstd_FOUND}) - endif() - - # Used for default, common dirs that the end-user can change (if needed) -@@ -317,13 +323,13 @@ if(HTTPLIB_COMPILE) - $ - $ - ) -- -+ - # Add C++20 module support if requested - # Include from separate file to prevent parse errors on older CMake versions - if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.28") - include(cmake/modules.cmake) - endif() -- -+ - set_target_properties(${PROJECT_NAME} - PROPERTIES - VERSION ${${PROJECT_NAME}_VERSION} diff --git a/.patch/openssl-cmake/0001-cpmutil-compat.patch b/.patch/openssl-cmake/0001-cpmutil-compat.patch new file mode 100644 index 0000000000..41f51f4940 --- /dev/null +++ b/.patch/openssl-cmake/0001-cpmutil-compat.patch @@ -0,0 +1,214 @@ +From ec4c1fdf526cb9ad045abf59b29ee495bbf5023a Mon Sep 17 00:00:00 2001 +From: crueter +Date: Sat, 30 May 2026 20:56:35 -0400 +Subject: [PATCH] cpmutil compat + +--- + CMakeLists.txt | 31 ++++++++----------- + cmake/FetchOpenSSL.cmake | 64 ---------------------------------------- + cmake/GetCPM.cmake | 5 ---- + 3 files changed, 13 insertions(+), 87 deletions(-) + delete mode 100644 cmake/FetchOpenSSL.cmake + delete mode 100644 cmake/GetCPM.cmake + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 5420ecc..9ffd5a0 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -19,9 +19,7 @@ include(FetchContent) + include(ProcessorCount) + include(cmake/ConfigureOpenSSL.cmake) + include(cmake/DetectTargetPlatform.cmake) +-include(cmake/FetchOpenSSL.cmake) + include(cmake/FindVcvarsall.cmake) +-include(cmake/GetCPM.cmake) + + # Custom options + option(OPENSSL_BUILD_VERBOSE "Enable verbose output from build" OFF) +@@ -47,9 +45,6 @@ if("${OPENSSL_TARGET_PLATFORM}" STREQUAL "") + detect_target_platform(OPENSSL_TARGET_PLATFORM) + endif() + +-# Fetch OpenSSL source +-fetch_openssl() +- + # Apply patches + foreach(patch IN LISTS OPENSSL_PATCH) + if(EXISTS "${patch}" AND NOT IS_DIRECTORY "${patch}") +@@ -59,13 +54,13 @@ foreach(patch IN LISTS OPENSSL_PATCH) + + execute_process( + COMMAND git init +- WORKING_DIRECTORY ${openssl_SOURCE_DIR} ++ WORKING_DIRECTORY ${OpenSSL_SOURCE_DIR} + OUTPUT_QUIET + ERROR_QUIET + ) + execute_process( + COMMAND git apply ${patch} +- WORKING_DIRECTORY ${openssl_SOURCE_DIR} ++ WORKING_DIRECTORY ${OpenSSL_SOURCE_DIR} + OUTPUT_QUIET + ERROR_QUIET + ) +@@ -161,8 +156,8 @@ list(PREPEND OPENSSL_CONFIGURE_OPTIONS ${OPENSSL_TARGET_PLATFORM}) + # Configure OpenSSL + configure_openssl( + COMMAND ${VCVARSALL_COMMAND} +- FILE ${openssl_SOURCE_DIR}/Configure +- BUILD_DIR ${openssl_BINARY_DIR} ++ FILE ${OpenSSL_SOURCE_DIR}/Configure ++ BUILD_DIR ${OpenSSL_BINARY_DIR} + OPTIONS ${OPENSSL_CONFIGURE_OPTIONS} + ) + +@@ -203,8 +198,8 @@ endif() + # Parse Makefile + parse_makefile(${OPENSSL_MAKEFILE} "INSTALL_LIBS" OPENSSL_STATIC_LIBS) + parse_makefile(${OPENSSL_MAKEFILE} "INSTALL_SHLIBS" OPENSSL_SHARED_LIBS) +-list(TRANSFORM OPENSSL_STATIC_LIBS PREPEND "${openssl_BINARY_DIR}/") +-list(TRANSFORM OPENSSL_SHARED_LIBS PREPEND "${openssl_BINARY_DIR}/") ++list(TRANSFORM OPENSSL_STATIC_LIBS PREPEND "${OpenSSL_BINARY_DIR}/") ++list(TRANSFORM OPENSSL_SHARED_LIBS PREPEND "${OpenSSL_BINARY_DIR}/") + + foreach(LIBRARY IN LISTS OPENSSL_STATIC_LIBS) + if(LIBRARY MATCHES "crypto") +@@ -239,14 +234,14 @@ endif() + + # Provide same targets and variables as FindOpenSSL module + set(OPENSSL_FOUND ON CACHE BOOL "Override FindOpenSSL variables" FORCE) +-set(OPENSSL_INCLUDE_DIR ${openssl_SOURCE_DIR}/include ${openssl_BINARY_DIR}/include CACHE STRING "Override FindOpenSSL variables" FORCE) ++set(OPENSSL_INCLUDE_DIR ${OpenSSL_SOURCE_DIR}/include ${OpenSSL_BINARY_DIR}/include CACHE STRING "Override FindOpenSSL variables" FORCE) + set(OPENSSL_CRYPTO_LIBRARY ${OPENSSL_${OPENSSL_LIBRARY_TYPE}_CRYPTO_LIBRARY} CACHE STRING "Override FindOpenSSL variables" FORCE) + set(OPENSSL_CRYPTO_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY} ${OPENSSL_DEPENDENCIES} CACHE STRING "Override FindOpenSSL variables" FORCE) + set(OPENSSL_SSL_LIBRARY ${OPENSSL_${OPENSSL_LIBRARY_TYPE}_SSL_LIBRARY} CACHE STRING "Override FindOpenSSL variables" FORCE) + set(OPENSSL_SSL_LIBRARIES ${OPENSSL_SSL_LIBRARY} ${OPENSSL_DEPENDENCIES} CACHE STRING "Override FindOpenSSL variables" FORCE) + set(OPENSSL_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY} ${OPENSSL_SSL_LIBRARY} ${OPENSSL_DEPENDENCIES} CACHE STRING "Override FindOpenSSL variables" FORCE) + set(OPENSSL_VERSION ${OPENSSL_CONFIGURED_VERSION} CACHE STRING "Override FindOpenSSL variables" FORCE) +-set(OPENSSL_APPLINK_SOURCE ${openssl_SOURCE_DIR}/ms/applink.c CACHE STRING "Override FindOpenSSL variables" FORCE) ++set(OPENSSL_APPLINK_SOURCE ${OpenSSL_SOURCE_DIR}/ms/applink.c CACHE STRING "Override FindOpenSSL variables" FORCE) + + add_library(OpenSSL::Crypto ${OPENSSL_LIBRARY_TYPE} IMPORTED GLOBAL) + add_library(OpenSSL::SSL ${OPENSSL_LIBRARY_TYPE} IMPORTED GLOBAL) +@@ -308,8 +303,8 @@ if(ANDROID) + endif() + + file(GLOB_RECURSE OPENSSL_SOURCES +- ${openssl_SOURCE_DIR}/*.[ch] +- ${openssl_SOURCE_DIR}/*.[ch].in ++ ${OpenSSL_SOURCE_DIR}/*.[ch] ++ ${OpenSSL_SOURCE_DIR}/*.[ch].in + ) + + set(OPENSSL_BUILD_OUTPUT +@@ -322,7 +317,7 @@ add_custom_command( + OUTPUT ${OPENSSL_BUILD_OUTPUT} + COMMAND ${OPENSSL_BUILD_COMMAND} + DEPENDS ${OPENSSL_SOURCES} +- WORKING_DIRECTORY ${openssl_BINARY_DIR} ++ WORKING_DIRECTORY ${OpenSSL_BINARY_DIR} + VERBATIM + ) + +@@ -341,7 +336,7 @@ if(OPENSSL_TEST AND NOT CMAKE_CROSSCOMPILING) + add_test( + NAME openssl-test + COMMAND ${OPENSSL_BUILD_TOOL} test VERBOSE_FAILURE=yes HARNESS_JOBS=${NUMBER_OF_THREADS} +- WORKING_DIRECTORY ${openssl_BINARY_DIR} ++ WORKING_DIRECTORY ${OpenSSL_BINARY_DIR} + ) + endif() + +@@ -356,7 +351,7 @@ if(OPENSSL_INSTALL) + install(CODE + "execute_process( + COMMAND ${OPENSSL_INSTALL_COMMAND} +- WORKING_DIRECTORY \"${openssl_BINARY_DIR}\" ++ WORKING_DIRECTORY \"${OpenSSL_BINARY_DIR}\" + )" + ) + endif() +diff --git a/cmake/FetchOpenSSL.cmake b/cmake/FetchOpenSSL.cmake +deleted file mode 100644 +index a43505d..0000000 +--- a/cmake/FetchOpenSSL.cmake ++++ /dev/null +@@ -1,64 +0,0 @@ +-function(fetch_openssl) +- if(EXISTS "${OPENSSL_SOURCE}" AND IS_DIRECTORY "${OPENSSL_SOURCE}") +- # Fetch the local OpenSSL source +- if(NOT IS_ABSOLUTE "${OPENSSL_SOURCE}") +- string(PREPEND OPENSSL_SOURCE ${CMAKE_SOURCE_DIR}/) +- endif() +- +- string(REPLACE "\\" "/" openssl-source_SOURCE_DIR "${OPENSSL_SOURCE}") +- set(openssl-source_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/openssl-source-build) +- else() +- set(CPM_OPTIONS +- NAME openssl-source +- DOWNLOAD_ONLY ON +- ) +- +- if(NOT OPENSSL_CONFIGURE_VERBOSE) +- list(APPEND CPM_OPTIONS QUIET) +- endif() +- +- if("${OPENSSL_SOURCE}" MATCHES "^http") +- # Download OpenSSL source from the internet +- list(APPEND CPM_OPTIONS URL ${OPENSSL_SOURCE}) +- else() +- # Download OpenSSL source from the official website +- if("${OPENSSL_TARGET_VERSION}" STREQUAL "") +- set(OPENSSL_TARGET_VERSION ${PROJECT_VERSION}) +- endif() +- +- if(OPENSSL_TARGET_VERSION VERSION_EQUAL PROJECT_VERSION) +- list(APPEND CPM_OPTIONS URL_HASH SHA256=aaf51a1fe064384f811daeaeb4ec4dce7340ec8bd893027eee676af31e83a04f) +- endif() +- +- if(OPENSSL_TARGET_VERSION MATCHES "^1\.1\.1[a-w]$") +- string(REPLACE "." "_" OPENSSL_TAGGED_VERSION ${OPENSSL_TARGET_VERSION}) +- list(APPEND CPM_OPTIONS URL https://github.com/openssl/openssl/releases/download/OpenSSL_${OPENSSL_TAGGED_VERSION}/openssl-${OPENSSL_TARGET_VERSION}.tar.gz) +- else() +- list(APPEND CPM_OPTIONS URL https://github.com/openssl/openssl/releases/download/openssl-${OPENSSL_TARGET_VERSION}/openssl-${OPENSSL_TARGET_VERSION}.tar.gz) +- endif() +- endif() +- +- CPMAddPackage(${CPM_OPTIONS}) +- endif() +- +- # Clean build directory if source directory has changed +- if(DEFINED CACHE{openssl-source_SOURCE_DIR_OLD} AND NOT openssl-source_SOURCE_DIR STREQUAL openssl-source_SOURCE_DIR_OLD) +- set(openssl-source_SOURCE_DIR_OLD ${openssl-source_SOURCE_DIR} CACHE INTERNAL "Previously fetched OpenSSL source") +- +- if(IS_DIRECTORY ${openssl-source_BINARY_DIR}) +- file(REMOVE_RECURSE ${openssl-source_BINARY_DIR}) +- file(MAKE_DIRECTORY ${openssl-source_BINARY_DIR}) +- endif() +- endif() +- +- # Override the FindOpenSSL module +- FetchContent_Declare( +- OpenSSL +- SOURCE_DIR ${openssl-source_SOURCE_DIR} +- BINARY_DIR ${openssl-source_BINARY_DIR} +- OVERRIDE_FIND_PACKAGE +- ) +- FetchContent_MakeAvailable(OpenSSL) +- +- return(PROPAGATE openssl_SOURCE_DIR openssl_BINARY_DIR) +-endfunction() +diff --git a/cmake/GetCPM.cmake b/cmake/GetCPM.cmake +deleted file mode 100644 +index bfc50f5..0000000 +--- a/cmake/GetCPM.cmake ++++ /dev/null +@@ -1,5 +0,0 @@ +-file( +- DOWNLOAD https://github.com/cpm-cmake/CPM.cmake/releases/latest/download/get_cpm.cmake +- ${CMAKE_CURRENT_BINARY_DIR}/get_cpm.cmake +-) +-include(${CMAKE_CURRENT_BINARY_DIR}/get_cpm.cmake) +-- +2.54.0 + diff --git a/.patch/openssl-cmake/0002-use-ccache.patch b/.patch/openssl-cmake/0002-use-ccache.patch new file mode 100644 index 0000000000..a753d4b6f1 --- /dev/null +++ b/.patch/openssl-cmake/0002-use-ccache.patch @@ -0,0 +1,54 @@ +From d46675fbb61eb6d51e478023ce4075e545ad4cfd Mon Sep 17 00:00:00 2001 +From: crueter +Date: Sat, 30 May 2026 21:11:55 -0400 +Subject: [PATCH] use ccache + +--- + CMakeLists.txt | 1 - + cmake/ConfigureOpenSSL.cmake | 12 +++--------- + 2 files changed, 3 insertions(+), 10 deletions(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 9ffd5a0..9ff14c8 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -28,7 +28,6 @@ option(OPENSSL_ENABLE_PARALLEL "Build and test in parallel if possible" ON) + option(OPENSSL_INSTALL "Install OpenSSL components to the directory" OFF) + option(OPENSSL_INSTALL_CERT "Install cert.pem to the directory" OFF) + option(OPENSSL_TEST "Enable testing and build OpenSSL self tests" OFF) +-option(OPENSSL_USE_CCACHE "Use ccache if available" ON) + + if("${OPENSSL_BUILD_TARGET}" STREQUAL "") + # Makefile target for build +diff --git a/cmake/ConfigureOpenSSL.cmake b/cmake/ConfigureOpenSSL.cmake +index 211c18b..3d8cbed 100644 +--- a/cmake/ConfigureOpenSSL.cmake ++++ b/cmake/ConfigureOpenSSL.cmake +@@ -69,15 +69,9 @@ function(apply_ccache FILE) + message(FATAL_ERROR "Couldn't find Makefile") + endif() + +- if(OPENSSL_USE_CCACHE) +- find_program(CCACHE ccache) +- +- if(NOT CCACHE) +- return() +- endif() +- ++ if(USE_CCACHE) + file(READ ${FILE} MAKEFILE) +- string(REPLACE "\nCC=" "\nCC=ccache " MAKEFILE "${MAKEFILE}") ++ string(REPLACE "\nCC=" "\nCC=${CCACHE_BINARY} " MAKEFILE "${MAKEFILE}") + + if(MSVC) + string(REPLACE "/Zi /Fdossl_static.pdb " "" MAKEFILE "${MAKEFILE}") +@@ -171,4 +165,4 @@ function(configure_openssl) + string(REPLACE "/W3" "/W0" MAKEFILE "${MAKEFILE}") + file(WRITE ${OPENSSL_MAKEFILE} "${MAKEFILE}") + endif() +-endfunction() +\ No newline at end of file ++endfunction() +-- +2.54.0 + diff --git a/.patch/openssl-cmake/0003-use-cmake-compiler-flags.patch b/.patch/openssl-cmake/0003-use-cmake-compiler-flags.patch new file mode 100644 index 0000000000..bbfa963743 --- /dev/null +++ b/.patch/openssl-cmake/0003-use-cmake-compiler-flags.patch @@ -0,0 +1,28 @@ +From 4a3cc92a7abad403529ed1cb4255ca63d9252de4 Mon Sep 17 00:00:00 2001 +From: crueter +Date: Sat, 30 May 2026 21:48:42 -0400 +Subject: [PATCH 2/2] use cmake compiler flags + +--- + cmake/ConfigureOpenSSL.cmake | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/cmake/ConfigureOpenSSL.cmake b/cmake/ConfigureOpenSSL.cmake +index 3d8cbed..3012e05 100644 +--- a/cmake/ConfigureOpenSSL.cmake ++++ b/cmake/ConfigureOpenSSL.cmake +@@ -135,7 +135,10 @@ function(configure_openssl) + endif() + + execute_process( +- COMMAND ${CONFIGURE_COMMAND} ++ COMMAND ${CMAKE_COMMAND} -E env ++ "CFLAGS=${CMAKE_C_FLAGS}" ++ "CXXFLAGS=${CMAKE_CXX_FLAGS}" ++ ${CONFIGURE_COMMAND} + WORKING_DIRECTORY ${CONFIGURE_BUILD_DIR} + ${VERBOSE_OPTION} + COMMAND_ERROR_IS_FATAL ANY +-- +2.54.0 + diff --git a/.patch/openssl-cmake/0004-use-shell-wrapper.patch b/.patch/openssl-cmake/0004-use-shell-wrapper.patch new file mode 100644 index 0000000000..76a19644df --- /dev/null +++ b/.patch/openssl-cmake/0004-use-shell-wrapper.patch @@ -0,0 +1,39 @@ +--- a/CMakeLists.txt 2026-06-01 23:53:16.498043856 -0400 ++++ b/CMakeLists.txt 2026-06-01 23:53:23.910543615 -0400 +@@ -312,13 +312,29 @@ + ${OPENSSL_SHARED_CRYPTO_LIBRARY} + ${OPENSSL_SHARED_SSL_LIBRARY} + ) +-add_custom_command( +- OUTPUT ${OPENSSL_BUILD_OUTPUT} +- COMMAND ${OPENSSL_BUILD_COMMAND} +- DEPENDS ${OPENSSL_SOURCES} +- WORKING_DIRECTORY ${OpenSSL_BINARY_DIR} +- VERBATIM +-) ++if (WIN32) ++ add_custom_command( ++ OUTPUT ${OPENSSL_BUILD_OUTPUT} ++ COMMAND ${OPENSSL_BUILD_COMMAND} ++ DEPENDS ${OPENSSL_SOURCES} ++ WORKING_DIRECTORY ${OpenSSL_BINARY_DIR} ++ VERBATIM) ++else() ++ set(_openssl_build_script "${CMAKE_CURRENT_BINARY_DIR}/BuildOpenSSL.cmake") ++ file(WRITE ${_openssl_build_script} ++ "execute_process(\n" ++ " COMMAND ${OPENSSL_BUILD_COMMAND}\n" ++ " WORKING_DIRECTORY ${OpenSSL_BINARY_DIR}\n" ++ " RESULT_VARIABLE _r)\n" ++ "if(_r)\n" ++ " message(FATAL_ERROR \"OpenSSL build failed: \${_r}\")\n" ++ "endif()\n") ++ add_custom_command( ++ OUTPUT ${OPENSSL_BUILD_OUTPUT} ++ COMMAND ${CMAKE_COMMAND} -P ${_openssl_build_script} ++ DEPENDS ${OPENSSL_SOURCES} ++ VERBATIM) ++endif() + + if(PROJECT_IS_TOP_LEVEL) + add_custom_target(openssl-build ALL DEPENDS ${OPENSSL_BUILD_OUTPUT}) diff --git a/.patch/openssl/0001-add-bundled-cert.patch b/.patch/openssl/0001-add-bundled-cert.patch new file mode 100644 index 0000000000..f30c43115f --- /dev/null +++ b/.patch/openssl/0001-add-bundled-cert.patch @@ -0,0 +1,3665 @@ +From 70eec0292dcd2778be40190c1642a45c71ddaa30 Mon Sep 17 00:00:00 2001 +From: crueter +Date: Sat, 30 May 2026 21:15:41 -0400 +Subject: [PATCH] add bundled cert + +`curl -LO https://raw.githubusercontent.com/crueter-ci/OpenSSL/refs/heads/master/cert.h` +--- + include/openssl/cert.h | 3645 ++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 3645 insertions(+) + create mode 100644 include/openssl/cert.h + +diff --git a/include/openssl/cert.h b/include/openssl/cert.h +new file mode 100644 +index 0000000..5a3f98f +--- /dev/null ++++ b/include/openssl/cert.h +@@ -0,0 +1,3645 @@ ++#ifndef CERT_H_ ++#define CERT_H_ ++ ++inline constexpr char kCert[] { ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UE\n" ++ "AwwJQUNDVlJBSVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQsw\n" ++ "CQYDVQQGEwJFUzAeFw0xMTA1MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQ\n" ++ "BgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwHUEtJQUNDVjENMAsGA1UECgwEQUND\n" ++ "VjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCb\n" ++ "qau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gMjmoY\n" ++ "HtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWo\n" ++ "G2ioPej0RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpA\n" ++ "lHPrzg5XPAOBOp0KoVdDaaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhr\n" ++ "IA8wKFSVf+DuzgpmndFALW4ir50awQUZ0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/\n" ++ "0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDGWuzndN9wrqODJerWx5eH\n" ++ "k6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs78yM2x/47\n" ++ "4KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMO\n" ++ "m3WR5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpa\n" ++ "cXpkatcnYGMN285J9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPl\n" ++ "uUsXQA+xtrn13k/c4LOsOxFwYIRKQ26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYI\n" ++ "KwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRwOi8vd3d3LmFjY3YuZXMvZmls\n" ++ "ZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEuY3J0MB8GCCsG\n" ++ "AQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2\n" ++ "VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeT\n" ++ "VfZW6oHlNsyMHj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIG\n" ++ "CCsGAQUFBwICMIIBFB6CARAAQQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUA\n" ++ "cgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBhAO0AegAgAGQAZQAgAGwAYQAgAEEA\n" ++ "QwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUAYwBuAG8AbABvAGcA\n" ++ "7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBjAHQA\n" ++ "cgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAA\n" ++ "QwBQAFMAIABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUA\n" ++ "czAwBggrBgEFBQcCARYkaHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2Mu\n" ++ "aHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRt\n" ++ "aW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2MV9kZXIuY3JsMA4GA1Ud\n" ++ "DwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZIhvcNAQEF\n" ++ "BQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdp\n" ++ "D70ER9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gU\n" ++ "JyCpZET/LtZ1qmxNYEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+m\n" ++ "AM/EKXMRNt6GGT6d7hmKG9Ww7Y49nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepD\n" ++ "vV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJTS+xJlsndQAJxGJ3KQhfnlms\n" ++ "tn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3sCPdK6jT2iWH\n" ++ "7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h\n" ++ "I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szA\n" ++ "h1xA2syVP1XgNce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xF\n" ++ "d3+YJ5oyXSrjhO7FmGYvliAd3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2H\n" ++ "pPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3pEfbRD0tVNEYqi4Y7\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFgzCCA2ugAwIBAgIPXZONMGc2yAYdGsdUhGkHMA0GCSqGSIb3DQEBCwUAMDsx\n" ++ "CzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJ\n" ++ "WiBGTk1ULVJDTTAeFw0wODEwMjkxNTU5NTZaFw0zMDAxMDEwMDAwMDBaMDsxCzAJ\n" ++ "BgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBG\n" ++ "Tk1ULVJDTTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALpxgHpMhm5/\n" ++ "yBNtwMZ9HACXjywMI7sQmkCpGreHiPibVmr75nuOi5KOpyVdWRHbNi63URcfqQgf\n" ++ "BBckWKo3Shjf5TnUV/3XwSyRAZHiItQDwFj8d0fsjz50Q7qsNI1NOHZnjrDIbzAz\n" ++ "WHFctPVrbtQBULgTfmxKo0nRIBnuvMApGGWn3v7v3QqQIecaZ5JCEJhfTzC8PhxF\n" ++ "tBDXaEAUwED653cXeuYLj2VbPNmaUtu1vZ5Gzz3rkQUCwJaydkxNEJY7kvqcfw+Z\n" ++ "374jNUUeAlz+taibmSXaXvMiwzn15Cou08YfxGyqxRxqAQVKL9LFwag0Jl1mpdIC\n" ++ "IfkYtwb1TplvqKtMUejPUBjFd8g5CSxJkjKZqLsXF3mwWsXmo8RZZUc1g16p6DUL\n" ++ "mbvkzSDGm0oGObVo/CK67lWMK07q87Hj/LaZmtVC+nFNCM+HHmpxffnTtOmlcYF7\n" ++ "wk5HlqX2doWjKI/pgG6BU6VtX7hI+cL5NqYuSf+4lsKMB7ObiFj86xsc3i1w4peS\n" ++ "MKGJ47xVqCfWS+2QrYv6YyVZLag13cqXM7zlzced0ezvXg5KkAYmY6252TUtB7p2\n" ++ "ZSysV4999AeU14ECll2jB0nVetBX+RvnU0Z1qrB5QstocQjpYL05ac70r8NWQMet\n" ++ "UqIJ5G+GR4of6ygnXYMgrwTJbFaai0b1AgMBAAGjgYMwgYAwDwYDVR0TAQH/BAUw\n" ++ "AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFPd9xf3E6Jobd2Sn9R2gzL+H\n" ++ "YJptMD4GA1UdIAQ3MDUwMwYEVR0gADArMCkGCCsGAQUFBwIBFh1odHRwOi8vd3d3\n" ++ "LmNlcnQuZm5tdC5lcy9kcGNzLzANBgkqhkiG9w0BAQsFAAOCAgEAB5BK3/MjTvDD\n" ++ "nFFlm5wioooMhfNzKWtN/gHiqQxjAb8EZ6WdmF/9ARP67Jpi6Yb+tmLSbkyU+8B1\n" ++ "RXxlDPiyN8+sD8+Nb/kZ94/sHvJwnvDKuO+3/3Y3dlv2bojzr2IyIpMNOmqOFGYM\n" ++ "LVN0V2Ue1bLdI4E7pWYjJ2cJj+F3qkPNZVEI7VFY/uY5+ctHhKQV8Xa7pO6kO8Rf\n" ++ "77IzlhEYt8llvhjho6Tc+hj507wTmzl6NLrTQfv6MooqtyuGC2mDOL7Nii4LcK2N\n" ++ "JpLuHvUBKwrZ1pebbuCoGRw6IYsMHkCtA+fdZn71uSANA+iW+YJF1DngoABd15jm\n" ++ "fZ5nc8OaKveri6E6FO80vFIOiZiaBECEHX5FaZNXzuvO+FB8TxxuBEOb+dY7Ixjp\n" ++ "6o7RTUaN8Tvkasq6+yO3m/qZASlaWFot4/nUbQ4mrcFuNLwy+AwF+mWj2zs3gyLp\n" ++ "1txyM/1d8iC9djwj2ij3+RvrWWTV3F9yfiD8zYm1kGdNYno/Tq0dwzn+evQoFt9B\n" ++ "9kiABdcPUXmsEKvU7ANm5mqwujGSQkBqvjrTcuFqN1W8rB2Vt2lh8kORdOag0wok\n" ++ "RqEIr9baRRmW1FMdW4R58MD3R++Lj8UGrp1MYp3/RgT408m2ECVAdf4WqslKYIYv\n" ++ "uu8wd+RU4riEmViAqhOLUTpPSPaLtrM=\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIICbjCCAfOgAwIBAgIQYvYybOXE42hcG2LdnC6dlTAKBggqhkjOPQQDAzB4MQsw\n" ++ "CQYDVQQGEwJFUzERMA8GA1UECgwIRk5NVC1SQ00xDjAMBgNVBAsMBUNlcmVzMRgw\n" ++ "FgYDVQRhDA9WQVRFUy1RMjgyNjAwNEoxLDAqBgNVBAMMI0FDIFJBSVogRk5NVC1S\n" ++ "Q00gU0VSVklET1JFUyBTRUdVUk9TMB4XDTE4MTIyMDA5MzczM1oXDTQzMTIyMDA5\n" ++ "MzczM1oweDELMAkGA1UEBhMCRVMxETAPBgNVBAoMCEZOTVQtUkNNMQ4wDAYDVQQL\n" ++ "DAVDZXJlczEYMBYGA1UEYQwPVkFURVMtUTI4MjYwMDRKMSwwKgYDVQQDDCNBQyBS\n" ++ "QUlaIEZOTVQtUkNNIFNFUlZJRE9SRVMgU0VHVVJPUzB2MBAGByqGSM49AgEGBSuB\n" ++ "BAAiA2IABPa6V1PIyqvfNkpSIeSX0oNnnvBlUdBeh8dHsVnyV0ebAAKTRBdp20LH\n" ++ "sbI6GA60XYyzZl2hNPk2LEnb80b8s0RpRBNm/dfF/a82Tc4DTQdxz69qBdKiQ1oK\n" ++ "Um8BA06Oi6NCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD\n" ++ "VR0OBBYEFAG5L++/EYZg8k/QQW6rcx/n0m5JMAoGCCqGSM49BAMDA2kAMGYCMQCu\n" ++ "SuMrQMN0EfKVrRYj3k4MGuZdpSRea0R7/DjiT8ucRRcRTBQnJlU5dUoDzBOQn5IC\n" ++ "MQD6SmxgiHPz7riYYqnOK8LZiqZwMR2vsJRM60/G49HzYqc8/5MuB1xJAWdpEgJy\n" ++ "v+c=\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIF7zCCA9egAwIBAgIIDdPjvGz5a7EwDQYJKoZIhvcNAQELBQAwgYQxEjAQBgNV\n" ++ "BAUTCUc2MzI4NzUxMDELMAkGA1UEBhMCRVMxJzAlBgNVBAoTHkFORiBBdXRvcmlk\n" ++ "YWQgZGUgQ2VydGlmaWNhY2lvbjEUMBIGA1UECxMLQU5GIENBIFJhaXoxIjAgBgNV\n" ++ "BAMTGUFORiBTZWN1cmUgU2VydmVyIFJvb3QgQ0EwHhcNMTkwOTA0MTAwMDM4WhcN\n" ++ "MzkwODMwMTAwMDM4WjCBhDESMBAGA1UEBRMJRzYzMjg3NTEwMQswCQYDVQQGEwJF\n" ++ "UzEnMCUGA1UEChMeQU5GIEF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uMRQwEgYD\n" ++ "VQQLEwtBTkYgQ0EgUmFpejEiMCAGA1UEAxMZQU5GIFNlY3VyZSBTZXJ2ZXIgUm9v\n" ++ "dCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANvrayvmZFSVgpCj\n" ++ "cqQZAZ2cC4Ffc0m6p6zzBE57lgvsEeBbphzOG9INgxwruJ4dfkUyYA8H6XdYfp9q\n" ++ "yGFOtibBTI3/TO80sh9l2Ll49a2pcbnvT1gdpd50IJeh7WhM3pIXS7yr/2WanvtH\n" ++ "2Vdy8wmhrnZEE26cLUQ5vPnHO6RYPUG9tMJJo8gN0pcvB2VSAKduyK9o7PQUlrZX\n" ++ "H1bDOZ8rbeTzPvY1ZNoMHKGESy9LS+IsJJ1tk0DrtSOOMspvRdOoiXsezx76W0OL\n" ++ "zc2oD2rKDF65nkeP8Nm2CgtYZRczuSPkdxl9y0oukntPLxB3sY0vaJxizOBQ+OyR\n" ++ "p1RMVwnVdmPF6GUe7m1qzwmd+nxPrWAI/VaZDxUse6mAq4xhj0oHdkLePfTdsiQz\n" ++ "W7i1o0TJrH93PB0j7IKppuLIBkwC/qxcmZkLLxCKpvR/1Yd0DVlJRfbwcVw5Kda/\n" ++ "SiOL9V8BY9KHcyi1Swr1+KuCLH5zJTIdC2MKF4EA/7Z2Xue0sUDKIbvVgFHlSFJn\n" ++ "LNJhiQcND85Cd8BEc5xEUKDbEAotlRyBr+Qc5RQe8TZBAQIvfXOn3kLMTOmJDVb3\n" ++ "n5HUA8ZsyY/b2BzgQJhdZpmYgG4t/wHFzstGH6wCxkPmrqKEPMVOHj1tyRRM4y5B\n" ++ "u8o5vzY8KhmqQYdOpc5LMnndkEl/AgMBAAGjYzBhMB8GA1UdIwQYMBaAFJxf0Gxj\n" ++ "o1+TypOYCK2Mh6UsXME3MB0GA1UdDgQWBBScX9BsY6Nfk8qTmAitjIelLFzBNzAO\n" ++ "BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC\n" ++ "AgEATh65isagmD9uw2nAalxJUqzLK114OMHVVISfk/CHGT0sZonrDUL8zPB1hT+L\n" ++ "9IBdeeUXZ701guLyPI59WzbLWoAAKfLOKyzxj6ptBZNscsdW699QIyjlRRA96Gej\n" ++ "rw5VD5AJYu9LWaL2U/HANeQvwSS9eS9OICI7/RogsKQOLHDtdD+4E5UGUcjohybK\n" ++ "pFtqFiGS3XNgnhAY3jyB6ugYw3yJ8otQPr0R4hUDqDZ9MwFsSBXXiJCZBMXM5gf0\n" ++ "vPSQ7RPi6ovDj6MzD8EpTBNO2hVWcXNyglD2mjN8orGoGjR0ZVzO0eurU+AagNjq\n" ++ "OknkJjCb5RyKqKkVMoaZkgoQI1YS4PbOTOK7vtuNknMBZi9iPrJyJ0U27U1W45eZ\n" ++ "/zo1PqVUSlJZS2Db7v54EX9K3BR5YLZrZAPbFYPhor72I5dQ8AkzNqdxliXzuUJ9\n" ++ "2zg/LFis6ELhDtjTO0wugumDLmsx2d1Hhk9tl5EuT+IocTUW0fJz/iUrB0ckYyfI\n" ++ "+PbZa/wSMVYIwFNCr5zQM378BvAxRAMU8Vjq8moNqRGyg77FGr8H6lnco4g175x2\n" ++ "MjxNBiLOFeXdntiP2t7SxDnlF4HPOEfrf4htWRvfn0IUrn7PqLBmZdo3r5+qPeoo\n" ++ "tt7VMVgWglvquxl1AnMaykgaIZOQCo6ThKd9OyMYkomgjaw=\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UE\n" ++ "BhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8w\n" ++ "MzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290\n" ++ "IENBMB4XDTExMDkyMjExMjIwMloXDTMwMDkyMjExMjIwMlowazELMAkGA1UEBhMC\n" ++ "SVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1\n" ++ "ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENB\n" ++ "MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bEpSmkLO/lGMWwUKNv\n" ++ "UTufClrJwkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW1V8IbInX\n" ++ "4ay8IMKx4INRimlNAJZaby/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9\n" ++ "KK3giq0itFZljoZUj5NDKd45RnijMCO6zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/\n" ++ "gCczWw63igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1fYVEiVRvjRuPjPdA1Yprb\n" ++ "rxTIW6HMiRvhMCb8oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2oxgkg4YQ\n" ++ "51Q+qDp2JE+BIcXjDwL4k5RHILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2F\n" ++ "be8lEfKXGkJh90qX6IuxEAf6ZYGyojnP9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxe\n" ++ "KF+w6D9Fz8+vm2/7hNN3WpVvrJSEnu68wEqPSpP4RCHiMUVhUE4Q2OM1fEwZtN4F\n" ++ "v6MGn8i1zeQf1xcGDXqVdFUNaBr8EBtiZJ1t4JWgw5QHVw0U5r0F+7if5t+L4sbn\n" ++ "fpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5jF66CyCU3nuDuP/jVo23Eek7\n" ++ "jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLYiDrIn3hm7Ynz\n" ++ "ezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbt\n" ++ "ifN7OHCUyQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAL\n" ++ "e3KHwGCmSUyIWOYdiPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70\n" ++ "jsNjLiNmsGe+b7bAEzlgqqI0JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDz\n" ++ "WochcYBNy+A4mz+7+uAwTc+G02UQGRjRlwKxK3JCaKygvU5a2hi/a5iB0P2avl4V\n" ++ "SM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2rykOLpn7VU+Xlff1ANATIGk0k9j\n" ++ "pwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2Tlf05fbsq4/aC4yyX\n" ++ "X04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst842/6+Ok\n" ++ "fcvHlXHo2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7R\n" ++ "K4X9p2jIugErsWx0Hbhzlefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btU\n" ++ "ZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXemOR/qnuOf0GZvBeyqdn6/axag67XH/JJU\n" ++ "LysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9vwGYT7JZVEc+NHt4bVaT\n" ++ "LnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg==\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UE\n" ++ "BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz\n" ++ "dCBDb21tZXJjaWFsMB4XDTEwMDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDEL\n" ++ "MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp\n" ++ "cm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC\n" ++ "AQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6EqdbDuKP\n" ++ "Hx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yr\n" ++ "ba0F8PrVC8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPAL\n" ++ "MeIrJmqbTFeurCA+ukV6BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1\n" ++ "yHp52UKqK39c/s4mT6NmgTWvRLpUHhwwMmWd5jyTXlBOeuM61G7MGvv50jeuJCqr\n" ++ "VwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNVHQ4EFgQUnZPGU4teyq8/\n" ++ "nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ\n" ++ "KoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYG\n" ++ "XUPGhi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNj\n" ++ "vbz4YYCanrHOQnDiqX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivt\n" ++ "Z8SOyUOyXGsViQK8YvxO8rUzqrJv0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9g\n" ++ "N53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0khsUlHRUe072o0EclNmsxZt9YC\n" ++ "nlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8=\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UE\n" ++ "BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz\n" ++ "dCBOZXR3b3JraW5nMB4XDTEwMDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDEL\n" ++ "MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp\n" ++ "cm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC\n" ++ "AQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SEHi3y\n" ++ "YJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbua\n" ++ "kCNrmreIdIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRL\n" ++ "QESxG9fhwoXA3hA/Pe24/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp\n" ++ "6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gbh+0t+nvujArjqWaJGctB+d1ENmHP4ndG\n" ++ "yH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNVHQ4EFgQUBx/S55zawm6i\n" ++ "QLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ\n" ++ "KoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfO\n" ++ "tDIuUFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzu\n" ++ "QY0x2+c06lkh1QF612S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZ\n" ++ "Lgo/bNjR9eUJtGxUAArgFU2HdW23WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4u\n" ++ "olu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9/ZFvgrG+CJPbFEfxojfHRZ48\n" ++ "x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s=\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UE\n" ++ "BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVz\n" ++ "dCBQcmVtaXVtMB4XDTEwMDEyOTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkG\n" ++ "A1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1U\n" ++ "cnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxBLf\n" ++ "qV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtnBKAQ\n" ++ "JG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ\n" ++ "+jjeRFcV5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrS\n" ++ "s8PhaJyJ+HoAVt70VZVs+7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5\n" ++ "HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmdGPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d7\n" ++ "70O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5Rp9EixAqnOEhss/n/fauG\n" ++ "V+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NIS+LI+H+S\n" ++ "qHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S\n" ++ "5u046uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4Ia\n" ++ "C1nEWTJ3s7xgaVY5/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TX\n" ++ "OwF0lkLgAOIua+rF7nKsu7/+6qqo+Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYE\n" ++ "FJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/\n" ++ "BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByvMiPIs0laUZx2\n" ++ "KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg\n" ++ "Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B\n" ++ "8OWycvpEgjNC6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQ\n" ++ "MKSOyARiqcTtNd56l+0OOF6SL5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc\n" ++ "0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK+4w1IX2COPKpVJEZNZOUbWo6xbLQ\n" ++ "u4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmVBtWVyuEklut89pMF\n" ++ "u+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFgIxpH\n" ++ "YoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8\n" ++ "GKa1qF60g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaO\n" ++ "RtGdFNrHF+QFlozEJLUbzxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6e\n" ++ "KeC2uAloGRwYQw==\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMC\n" ++ "VVMxFDASBgNVBAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQ\n" ++ "cmVtaXVtIEVDQzAeFw0xMDAxMjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJ\n" ++ "BgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1UcnVzdDEgMB4GA1UEAwwXQWZmaXJt\n" ++ "VHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQNMF4bFZ0D\n" ++ "0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQN8O9\n" ++ "ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0G\n" ++ "A1UdDgQWBBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4G\n" ++ "A1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/Vs\n" ++ "aobgxCd05DhT1wV/GzTjxi+zygk8N53X57hG8f2h4nECMEJZh0PUUd+60wkyWs6I\n" ++ "flc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKMeQ==\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF\n" ++ "ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6\n" ++ "b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL\n" ++ "MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv\n" ++ "b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj\n" ++ "ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM\n" ++ "9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw\n" ++ "IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6\n" ++ "VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L\n" ++ "93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm\n" ++ "jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC\n" ++ "AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA\n" ++ "A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI\n" ++ "U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs\n" ++ "N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv\n" ++ "o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU\n" ++ "5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy\n" ++ "rqXRfboQnoZsG4q5WTP468SQvvG5\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFQTCCAymgAwIBAgITBmyf0pY1hp8KD+WGePhbJruKNzANBgkqhkiG9w0BAQwF\n" ++ "ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6\n" ++ "b24gUm9vdCBDQSAyMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTEL\n" ++ "MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv\n" ++ "b3QgQ0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK2Wny2cSkxK\n" ++ "gXlRmeyKy2tgURO8TW0G/LAIjd0ZEGrHJgw12MBvIITplLGbhQPDW9tK6Mj4kHbZ\n" ++ "W0/jTOgGNk3Mmqw9DJArktQGGWCsN0R5hYGCrVo34A3MnaZMUnbqQ523BNFQ9lXg\n" ++ "1dKmSYXpN+nKfq5clU1Imj+uIFptiJXZNLhSGkOQsL9sBbm2eLfq0OQ6PBJTYv9K\n" ++ "8nu+NQWpEjTj82R0Yiw9AElaKP4yRLuH3WUnAnE72kr3H9rN9yFVkE8P7K6C4Z9r\n" ++ "2UXTu/Bfh+08LDmG2j/e7HJV63mjrdvdfLC6HM783k81ds8P+HgfajZRRidhW+me\n" ++ "z/CiVX18JYpvL7TFz4QuK/0NURBs+18bvBt+xa47mAExkv8LV/SasrlX6avvDXbR\n" ++ "8O70zoan4G7ptGmh32n2M8ZpLpcTnqWHsFcQgTfJU7O7f/aS0ZzQGPSSbtqDT6Zj\n" ++ "mUyl+17vIWR6IF9sZIUVyzfpYgwLKhbcAS4y2j5L9Z469hdAlO+ekQiG+r5jqFoz\n" ++ "7Mt0Q5X5bGlSNscpb/xVA1wf+5+9R+vnSUeVC06JIglJ4PVhHvG/LopyboBZ/1c6\n" ++ "+XUyo05f7O0oYtlNc/LMgRdg7c3r3NunysV+Ar3yVAhU/bQtCSwXVEqY0VThUWcI\n" ++ "0u1ufm8/0i2BWSlmy5A5lREedCf+3euvAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMB\n" ++ "Af8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSwDPBMMPQFWAJI/TPlUq9LhONm\n" ++ "UjANBgkqhkiG9w0BAQwFAAOCAgEAqqiAjw54o+Ci1M3m9Zh6O+oAA7CXDpO8Wqj2\n" ++ "LIxyh6mx/H9z/WNxeKWHWc8w4Q0QshNabYL1auaAn6AFC2jkR2vHat+2/XcycuUY\n" ++ "+gn0oJMsXdKMdYV2ZZAMA3m3MSNjrXiDCYZohMr/+c8mmpJ5581LxedhpxfL86kS\n" ++ "k5Nrp+gvU5LEYFiwzAJRGFuFjWJZY7attN6a+yb3ACfAXVU3dJnJUH/jWS5E4ywl\n" ++ "7uxMMne0nxrpS10gxdr9HIcWxkPo1LsmmkVwXqkLN1PiRnsn/eBG8om3zEK2yygm\n" ++ "btmlyTrIQRNg91CMFa6ybRoVGld45pIq2WWQgj9sAq+uEjonljYE1x2igGOpm/Hl\n" ++ "urR8FLBOybEfdF849lHqm/osohHUqS0nGkWxr7JOcQ3AWEbWaQbLU8uz/mtBzUF+\n" ++ "fUwPfHJ5elnNXkoOrJupmHN5fLT0zLm4BwyydFy4x2+IoZCn9Kr5v2c69BoVYh63\n" ++ "n749sSmvZ6ES8lgQGVMDMBu4Gon2nL2XA46jCfMdiyHxtN/kHNGfZQIG6lzWE7OE\n" ++ "76KlXIx3KadowGuuQNKotOrN8I1LOJwZmhsoVLiJkO/KdYE+HvJkJMcYr07/R54H\n" ++ "9jVlpNMKVv/1F2Rs76giJUmTtt8AF9pYfl3uxRuw0dFfIRDH+fO6AgonB8Xx1sfT\n" ++ "4PsJYGw=\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5\n" ++ "MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g\n" ++ "Um9vdCBDQSAzMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG\n" ++ "A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg\n" ++ "Q0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZBf8ANm+gBG1bG8lKl\n" ++ "ui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjrZt6j\n" ++ "QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSr\n" ++ "ttvXBp43rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkr\n" ++ "BqWTrBqYaGFy+uGh0PsceGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteM\n" ++ "YyRIHN8wfdVoOw==\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5\n" ++ "MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g\n" ++ "Um9vdCBDQSA0MB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG\n" ++ "A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg\n" ++ "Q0EgNDB2MBAGByqGSM49AgEGBSuBBAAiA2IABNKrijdPo1MN/sGKe0uoe0ZLY7Bi\n" ++ "9i0b2whxIdIA6GO9mif78DluXeo9pcmBqqNbIJhFXRbb/egQbeOc4OO9X4Ri83Bk\n" ++ "M6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYDVR0TAQH/BAUwAwEB\n" ++ "/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNPsxzplbszh2naaVvuc84ZtV+WB\n" ++ "MAoGCCqGSM49BAMDA2gAMGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlw\n" ++ "CkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1AE47xDqUEpHJWEadIRNyp4iciuRMStuW\n" ++ "1KyLa2tJElMzrdfkviT8tQp21KW8EA==\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UE\n" ++ "AwwVQXRvcyBUcnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQG\n" ++ "EwJERTAeFw0xMTA3MDcxNDU4MzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMM\n" ++ "FUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsGA1UECgwEQXRvczELMAkGA1UEBhMC\n" ++ "REUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCVhTuXbyo7LjvPpvMp\n" ++ "Nb7PGKw+qtn4TaA+Gke5vJrf8v7MPkfoepbCJI419KkM/IL9bcFyYie96mvr54rM\n" ++ "VD6QUM+A1JX76LWC1BTFtqlVJVfbsVD2sGBkWXppzwO3bw2+yj5vdHLqqjAqc2K+\n" ++ "SZFhyBH+DgMq92og3AIVDV4VavzjgsG1xZ1kCWyjWZgHJ8cblithdHFsQ/H3NYkQ\n" ++ "4J7sVaE3IqKHBAUsR320HLliKWYoyrfhk/WklAOZuXCFteZI6o1Q/NnezG8HDt0L\n" ++ "cp2AMBYHlT8oDv3FdU9T1nSatCQujgKRz3bFmx5VdJx4IbHwLfELn8LVlhgf8FQi\n" ++ "eowHAgMBAAGjfTB7MB0GA1UdDgQWBBSnpQaxLKYJYO7Rl+lwrrw7GWzbITAPBgNV\n" ++ "HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKelBrEspglg7tGX6XCuvDsZbNshMBgG\n" ++ "A1UdIAQRMA8wDQYLKwYBBAGwLQMEAQEwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3\n" ++ "DQEBCwUAA4IBAQAmdzTblEiGKkGdLD4GkGDEjKwLVLgfuXvTBznk+j57sj1O7Z8j\n" ++ "vZfza1zv7v1Apt+hk6EKhqzvINB5Ab149xnYJDE0BAGmuhWawyfc2E8PzBhj/5kP\n" ++ "DpFrdRbhIfzYJsdHt6bPWHJxfrrhTZVHO8mvbaG0weyJ9rQPOLXiZNwlz6bb65pc\n" ++ "maHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a961qn8FYiqTxlVMYVqL2Gns2D\n" ++ "lmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G3mB/ufNPRJLv\n" ++ "KrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIICFTCCAZugAwIBAgIQPZg7pmY9kGP3fiZXOATvADAKBggqhkjOPQQDAzBMMS4w\n" ++ "LAYDVQQDDCVBdG9zIFRydXN0ZWRSb290IFJvb3QgQ0EgRUNDIFRMUyAyMDIxMQ0w\n" ++ "CwYDVQQKDARBdG9zMQswCQYDVQQGEwJERTAeFw0yMTA0MjIwOTI2MjNaFw00MTA0\n" ++ "MTcwOTI2MjJaMEwxLjAsBgNVBAMMJUF0b3MgVHJ1c3RlZFJvb3QgUm9vdCBDQSBF\n" ++ "Q0MgVExTIDIwMjExDTALBgNVBAoMBEF0b3MxCzAJBgNVBAYTAkRFMHYwEAYHKoZI\n" ++ "zj0CAQYFK4EEACIDYgAEloZYKDcKZ9Cg3iQZGeHkBQcfl+3oZIK59sRxUM6KDP/X\n" ++ "tXa7oWyTbIOiaG6l2b4siJVBzV3dscqDY4PMwL502eCdpO5KTlbgmClBk1IQ1SQ4\n" ++ "AjJn8ZQSb+/Xxd4u/RmAo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR2\n" ++ "KCXWfeBmmnoJsmo7jjPXNtNPojAOBgNVHQ8BAf8EBAMCAYYwCgYIKoZIzj0EAwMD\n" ++ "aAAwZQIwW5kp85wxtolrbNa9d+F851F+uDrNozZffPc8dz7kUK2o59JZDCaOMDtu\n" ++ "CCrCp1rIAjEAmeMM56PDr9NJLkaCI2ZdyQAUEv049OGYa3cpetskz2VAv9LcjBHo\n" ++ "9H1/IISpQuQo\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFZDCCA0ygAwIBAgIQU9XP5hmTC/srBRLYwiqipDANBgkqhkiG9w0BAQwFADBM\n" ++ "MS4wLAYDVQQDDCVBdG9zIFRydXN0ZWRSb290IFJvb3QgQ0EgUlNBIFRMUyAyMDIx\n" ++ "MQ0wCwYDVQQKDARBdG9zMQswCQYDVQQGEwJERTAeFw0yMTA0MjIwOTIxMTBaFw00\n" ++ "MTA0MTcwOTIxMDlaMEwxLjAsBgNVBAMMJUF0b3MgVHJ1c3RlZFJvb3QgUm9vdCBD\n" ++ "QSBSU0EgVExTIDIwMjExDTALBgNVBAoMBEF0b3MxCzAJBgNVBAYTAkRFMIICIjAN\n" ++ "BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtoAOxHm9BYx9sKOdTSJNy/BBl01Z\n" ++ "4NH+VoyX8te9j2y3I49f1cTYQcvyAh5x5en2XssIKl4w8i1mx4QbZFc4nXUtVsYv\n" ++ "Ye+W/CBGvevUez8/fEc4BKkbqlLfEzfTFRVOvV98r61jx3ncCHvVoOX3W3WsgFWZ\n" ++ "kmGbzSoXfduP9LVq6hdKZChmFSlsAvFr1bqjM9xaZ6cF4r9lthawEO3NUDPJcFDs\n" ++ "GY6wx/J0W2tExn2WuZgIWWbeKQGb9Cpt0xU6kGpn8bRrZtkh68rZYnxGEFzedUln\n" ++ "nkL5/nWpo63/dgpnQOPF943HhZpZnmKaau1Fh5hnstVKPNe0OwANwI8f4UDErmwh\n" ++ "3El+fsqyjW22v5MvoVw+j8rtgI5Y4dtXz4U2OLJxpAmMkokIiEjxQGMYsluMWuPD\n" ++ "0xeqqxmjLBvk1cbiZnrXghmmOxYsL3GHX0WelXOTwkKBIROW1527k2gV+p2kHYzy\n" ++ "geBYBr3JtuP2iV2J+axEoctr+hbxx1A9JNr3w+SH1VbxT5Aw+kUJWdo0zuATHAR8\n" ++ "ANSbhqRAvNncTFd+rrcztl524WWLZt+NyteYr842mIycg5kDcPOvdO3GDjbnvezB\n" ++ "c6eUWsuSZIKmAMFwoW4sKeFYV+xafJlrJaSQOoD0IJ2azsct+bJLKZWD6TWNp0lI\n" ++ "pw9MGZHQ9b8Q4HECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU\n" ++ "dEmZ0f+0emhFdcN+tNzMzjkz2ggwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB\n" ++ "DAUAA4ICAQAjQ1MkYlxt/T7Cz1UAbMVWiLkO3TriJQ2VSpfKgInuKs1l+NsW4AmS\n" ++ "4BjHeJi78+xCUvuppILXTdiK/ORO/auQxDh1MoSf/7OwKwIzNsAQkG8dnK/haZPs\n" ++ "o0UvFJ/1TCplQ3IM98P4lYsU84UgYt1UU90s3BiVaU+DR3BAM1h3Egyi61IxHkzJ\n" ++ "qM7F78PRreBrAwA0JrRUITWXAdxfG/F851X6LWh3e9NpzNMOa7pNdkTWwhWaJuyw\n" ++ "xfW70Xp0wmzNxbVe9kzmWy2B27O3Opee7c9GslA9hGCZcbUztVdF5kJHdWoOsAgM\n" ++ "rr3e97sPWD2PAzHoPYJQyi9eDF20l74gNAf0xBLh7tew2VktafcxBPTy+av5EzH4\n" ++ "AXcOPUIjJsyacmdRIXrMPIWo6iFqO9taPKU0nprALN+AnCng33eU0aKAQv9qTFsR\n" ++ "0PXNor6uzFFcw9VUewyu1rkGd4Di7wcaaMxZUa1+XGdrudviB0JbuAEFWDlN5LuY\n" ++ "o7Ey7Nmj1m+UI/87tyll5gfp77YZ6ufCOB0yiJA8EytuzO+rdwY0d4RPcuSBhPm5\n" ++ "dDTedk+SKlOxJTnbPP/lPqYO5Wue/9vsL3SD3460s6neFE3/MaNFcyT6lSnMEpcE\n" ++ "oji2jbDwN/zIIX8/syQbPYtuzE2wFg2WHYMfRsCbvUOZ58SWLs5fyQ==\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIGFDCCA/ygAwIBAgIIG3Dp0v+ubHEwDQYJKoZIhvcNAQELBQAwUTELMAkGA1UE\n" ++ "BhMCRVMxQjBABgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1h\n" ++ "cHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2ODAeFw0xNDA5MjMxNTIyMDdaFw0zNjA1\n" ++ "MDUxNTIyMDdaMFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUg\n" ++ "Q2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjgwggIi\n" ++ "MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDDUtd9\n" ++ "thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQM\n" ++ "cas9UX4PB99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefG\n" ++ "L9ItWY16Ck6WaVICqjaY7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15i\n" ++ "NA9wBj4gGFrO93IbJWyTdBSTo3OxDqqHECNZXyAFGUftaI6SEspd/NYrspI8IM/h\n" ++ "X68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyIplD9amML9ZMWGxmPsu2b\n" ++ "m8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctXMbScyJCy\n" ++ "Z/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirja\n" ++ "EbsXLZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/T\n" ++ "KI8xWVvTyQKmtFLKbpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF\n" ++ "6NkBiDkal4ZkQdU7hwxu+g/GvUgUvzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVh\n" ++ "OSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMB0GA1UdDgQWBBRlzeurNR4APn7VdMAc\n" ++ "tHNHDhpkLzASBgNVHRMBAf8ECDAGAQH/AgEBMIGmBgNVHSAEgZ4wgZswgZgGBFUd\n" ++ "IAAwgY8wLwYIKwYBBQUHAgEWI2h0dHA6Ly93d3cuZmlybWFwcm9mZXNpb25hbC5j\n" ++ "b20vY3BzMFwGCCsGAQUFBwICMFAeTgBQAGEAcwBlAG8AIABkAGUAIABsAGEAIABC\n" ++ "AG8AbgBhAG4AbwB2AGEAIAA0ADcAIABCAGEAcgBjAGUAbABvAG4AYQAgADAAOAAw\n" ++ "ADEANzAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQELBQADggIBAHSHKAIrdx9m\n" ++ "iWTtj3QuRhy7qPj4Cx2Dtjqn6EWKB7fgPiDL4QjbEwj4KKE1soCzC1HA01aajTNF\n" ++ "Sa9J8OA9B3pFE1r/yJfY0xgsfZb43aJlQ3CTkBW6kN/oGbDbLIpgD7dvlAceHabJ\n" ++ "hfa9NPhAeGIQcDq+fUs5gakQ1JZBu/hfHAsdCPKxsIl68veg4MSPi3i1O1ilI45P\n" ++ "Vf42O+AMt8oqMEEgtIDNrvx2ZnOorm7hfNoD6JQg5iKj0B+QXSBTFCZX2lSX3xZE\n" ++ "EAEeiGaPcjiT3SC3NL7X8e5jjkd5KAb881lFJWAiMxujX6i6KtoaPc1A6ozuBRWV\n" ++ "1aUsIC+nmCjuRfzxuIgALI9C2lHVnOUTaHFFQ4ueCyE8S1wF3BqfmI7avSKecs2t\n" ++ "CsvMo2ebKHTEm9caPARYpoKdrcd7b/+Alun4jWq9GJAd/0kakFI3ky88Al2CdgtR\n" ++ "5xbHV/g4+afNmyJU72OwFW1TZQNKXkqgsqeOSQBZONXH9IBk9W6VULgRfhVwOEqw\n" ++ "f9DEMnDAGf/JOC0ULGb0QkTmVXYbgBVX/8Cnp6o5qtjTcNAuuuuUavpfNIbnYrX9\n" ++ "ivAwhZTJryQCL2/W3Wf+47BVTwSYT6RBVuKT0Gro1vP7ZeDOdcQxWQzugsgMYDNK\n" ++ "GbqEZycPvEJdvSRUDewdcAZfpLz6IHxV\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFdDCCA1ygAwIBAgIQVW9l47TZkGobCdFsPsBsIDANBgkqhkiG9w0BAQsFADBU\n" ++ "MQswCQYDVQQGEwJDTjEmMCQGA1UECgwdQkVJSklORyBDRVJUSUZJQ0FURSBBVVRI\n" ++ "T1JJVFkxHTAbBgNVBAMMFEJKQ0EgR2xvYmFsIFJvb3QgQ0ExMB4XDTE5MTIxOTAz\n" ++ "MTYxN1oXDTQ0MTIxMjAzMTYxN1owVDELMAkGA1UEBhMCQ04xJjAkBgNVBAoMHUJF\n" ++ "SUpJTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZMR0wGwYDVQQDDBRCSkNBIEdsb2Jh\n" ++ "bCBSb290IENBMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAPFmCL3Z\n" ++ "xRVhy4QEQaVpN3cdwbB7+sN3SJATcmTRuHyQNZ0YeYjjlwE8R4HyDqKYDZ4/N+AZ\n" ++ "spDyRhySsTphzvq3Rp4Dhtczbu33RYx2N95ulpH3134rhxfVizXuhJFyV9xgw8O5\n" ++ "58dnJCNPYwpj9mZ9S1WnP3hkSWkSl+BMDdMJoDIwOvqfwPKcxRIqLhy1BDPapDgR\n" ++ "at7GGPZHOiJBhyL8xIkoVNiMpTAK+BcWyqw3/XmnkRd4OJmtWO2y3syJfQOcs4ll\n" ++ "5+M7sSKGjwZteAf9kRJ/sGsciQ35uMt0WwfCyPQ10WRjeulumijWML3mG90Vr4Tq\n" ++ "nMfK9Q7q8l0ph49pczm+LiRvRSGsxdRpJQaDrXpIhRMsDQa4bHlW/KNnMoH1V6XK\n" ++ "V0Jp6VwkYe/iMBhORJhVb3rCk9gZtt58R4oRTklH2yiUAguUSiz5EtBP6DF+bHq/\n" ++ "pj+bOT0CFqMYs2esWz8sgytnOYFcuX6U1WTdno9uruh8W7TXakdI136z1C2OVnZO\n" ++ "z2nxbkRs1CTqjSShGL+9V/6pmTW12xB3uD1IutbB5/EjPtffhZ0nPNRAvQoMvfXn\n" ++ "jSXWgXSHRtQpdaJCbPdzied9v3pKH9MiyRVVz99vfFXQpIsHETdfg6YmV6YBW37+\n" ++ "WGgHqel62bno/1Afq8K0wM7o6v0PvY1NuLxxAgMBAAGjQjBAMB0GA1UdDgQWBBTF\n" ++ "7+3M2I0hxkjk49cULqcWk+WYATAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE\n" ++ "AwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAUoKsITQfI/Ki2Pm4rzc2IInRNwPWaZ+4\n" ++ "YRC6ojGYWUfo0Q0lHhVBDOAqVdVXUsv45Mdpox1NcQJeXyFFYEhcCY5JEMEE3Kli\n" ++ "awLwQ8hOnThJdMkycFRtwUf8jrQ2ntScvd0g1lPJGKm1Vrl2i5VnZu69mP6u775u\n" ++ "+2D2/VnGKhs/I0qUJDAnyIm860Qkmss9vk/Ves6OF8tiwdneHg56/0OGNFK8YT88\n" ++ "X7vZdrRTvJez/opMEi4r89fO4aL/3Xtw+zuhTaRjAv04l5U/BXCga99igUOLtFkN\n" ++ "SoxUnMW7gZ/NfaXvCyUeOiDbHPwfmGcCCtRzRBPbUYQaVQNW4AB+dAb/OMRyHdOo\n" ++ "P2gxXdMJxy6MW2Pg6Nwe0uxhHvLe5e/2mXZgLR6UcnHGCyoyx5JO1UbXHfmpGQrI\n" ++ "+pXObSOYqgs4rZpWDW+N8TEAiMEXnM0ZNjX+VVOg4DwzX5Ze4jLp3zO7Bkqp2IRz\n" ++ "znfSxqxx4VyjHQy7Ct9f4qNx2No3WqB4K/TUfet27fJhcKVlmtOJNBir+3I+17Q9\n" ++ "eVzYH6Eze9mCUAyTF6ps3MKCuwJXNq+YJyo5UOGwifUll35HaBC07HPKs5fRJNz2\n" ++ "YqAo07WjuGS3iGJCz51TzZm+ZGiPTx4SSPfSKcOYKMryMguTjClPPGAyzQWWYezy\n" ++ "r/6zcCwupvI=\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIICJTCCAaugAwIBAgIQLBcIfWQqwP6FGFkGz7RK6zAKBggqhkjOPQQDAzBUMQsw\n" ++ "CQYDVQQGEwJDTjEmMCQGA1UECgwdQkVJSklORyBDRVJUSUZJQ0FURSBBVVRIT1JJ\n" ++ "VFkxHTAbBgNVBAMMFEJKQ0EgR2xvYmFsIFJvb3QgQ0EyMB4XDTE5MTIxOTAzMTgy\n" ++ "MVoXDTQ0MTIxMjAzMTgyMVowVDELMAkGA1UEBhMCQ04xJjAkBgNVBAoMHUJFSUpJ\n" ++ "TkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZMR0wGwYDVQQDDBRCSkNBIEdsb2JhbCBS\n" ++ "b290IENBMjB2MBAGByqGSM49AgEGBSuBBAAiA2IABJ3LgJGNU2e1uVCxA/jlSR9B\n" ++ "IgmwUVJY1is0j8USRhTFiy8shP8sbqjV8QnjAyEUxEM9fMEsxEtqSs3ph+B99iK+\n" ++ "+kpRuDCK/eHeGBIK9ke35xe/J4rUQUyWPGCWwf0VHKNCMEAwHQYDVR0OBBYEFNJK\n" ++ "sVF/BvDRgh9Obl+rg/xI1LCRMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD\n" ++ "AgEGMAoGCCqGSM49BAMDA2gAMGUCMBq8W9f+qdJUDkpd0m2xQNz0Q9XSSpkZElaA\n" ++ "94M04TVOSG0ED1cxMDAtsaqdAzjbBgIxAMvMh1PLet8gUXOQwKhbYdDFUDn9hf7B\n" ++ "43j4ptZLvZuHjw/l1lOWqzzIQNph91Oj9w==\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ\n" ++ "RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD\n" ++ "VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX\n" ++ "DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y\n" ++ "ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy\n" ++ "VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr\n" ++ "mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr\n" ++ "IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK\n" ++ "mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu\n" ++ "XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy\n" ++ "dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye\n" ++ "jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1\n" ++ "BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3\n" ++ "DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92\n" ++ "9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx\n" ++ "jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0\n" ++ "Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz\n" ++ "ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS\n" ++ "R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd\n" ++ "MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg\n" ++ "Q2xhc3MgMiBSb290IENBMB4XDTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1ow\n" ++ "TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw\n" ++ "HgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB\n" ++ "BQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1g1Lr\n" ++ "6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPV\n" ++ "L4O2fuPn9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC91\n" ++ "1K2GScuVr1QGbNgGE41b/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHx\n" ++ "MlAQTn/0hpPshNOOvEu/XAFOBz3cFIqUCqTqc/sLUegTBxj6DvEr0VQVfTzh97QZ\n" ++ "QmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeffawrbD02TTqigzXsu8lkB\n" ++ "arcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgIzRFo1clr\n" ++ "Us3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLi\n" ++ "FRhnBkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRS\n" ++ "P/TizPJhk9H9Z2vXUq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN\n" ++ "9SG9dKpN6nIDSdvHXx1iY8f93ZHsM+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxP\n" ++ "AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMmAd+BikoL1Rpzz\n" ++ "uvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAU18h\n" ++ "9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s\n" ++ "A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3t\n" ++ "OluwlN5E40EIosHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo\n" ++ "+fsicdl9sz1Gv7SEr5AcD48Saq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7\n" ++ "KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYdDnkM/crqJIByw5c/8nerQyIKx+u2\n" ++ "DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWDLfJ6v9r9jv6ly0Us\n" ++ "H8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0oyLQ\n" ++ "I+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK7\n" ++ "5t98biGCwWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h\n" ++ "3PFaTWwyI0PurKju7koSCTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPz\n" ++ "Y11aWOIv4x3kqdbQCtCev9eBCfHJxyYNrJgWVqA=\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd\n" ++ "MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg\n" ++ "Q2xhc3MgMyBSb290IENBMB4XDTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFow\n" ++ "TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw\n" ++ "HgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB\n" ++ "BQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRHsJ8Y\n" ++ "ZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3E\n" ++ "N3coTRiR5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9\n" ++ "tznDDgFHmV0ST9tD+leh7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX\n" ++ "0DJq1l1sDPGzbjniazEuOQAnFN44wOwZZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c\n" ++ "/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH2xc519woe2v1n/MuwU8X\n" ++ "KhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV/afmiSTY\n" ++ "zIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvS\n" ++ "O1UQRwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D\n" ++ "34xFMFbG02SrZvPAXpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgP\n" ++ "K9Dx2hzLabjKSWJtyNBjYt1gD1iqj6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3\n" ++ "AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFEe4zf/lb+74suwv\n" ++ "Tg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAACAj\n" ++ "QTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV\n" ++ "cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXS\n" ++ "IGrs/CIBKM+GuIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2\n" ++ "HJLw5QY33KbmkJs4j1xrG0aGQ0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsa\n" ++ "O5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8ZORK15FTAaggiG6cX0S5y2CBNOxv\n" ++ "033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2KSb12tjE8nVhz36u\n" ++ "dmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz6MkE\n" ++ "kbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg41\n" ++ "3OEMXbugUZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvD\n" ++ "u79leNKGef9JOxqDDPDeeOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq\n" ++ "4/g7u9xN12TyUb7mqqta6THuBrxzvxNiCp/HuZc=\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNV\n" ++ "BAYTAlNLMRMwEQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMu\n" ++ "MRkwFwYDVQQDExBDQSBEaXNpZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQy\n" ++ "MDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sxEzARBgNVBAcTCkJyYXRpc2xhdmEx\n" ++ "EzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERpc2lnIFJvb3QgUjIw\n" ++ "ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbCw3Oe\n" ++ "NcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNH\n" ++ "PWSb6WiaxswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3I\n" ++ "x2ymrdMxp7zo5eFm1tL7A7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbe\n" ++ "QTg06ov80egEFGEtQX6sx3dOy1FU+16SGBsEWmjGycT6txOgmLcRK7fWV8x8nhfR\n" ++ "yyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqVg8NTEQxzHQuyRpDRQjrO\n" ++ "QG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa5Beny912\n" ++ "H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJ\n" ++ "QfYEkoopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUD\n" ++ "i/ZnWejBBhG93c+AAk9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORs\n" ++ "nLMOPReisjQS1n6yqEm70XooQL6iFh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1\n" ++ "rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud\n" ++ "DwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5uQu0wDQYJKoZI\n" ++ "hvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM\n" ++ "tCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqf\n" ++ "GopTpti72TVVsRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkb\n" ++ "lvdhuDvEK7Z4bLQjb/D907JedR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka\n" ++ "+elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W81k/BfDxujRNt+3vrMNDcTa/F1bal\n" ++ "TFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjxmHHEt38OFdAlab0i\n" ++ "nSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01utI3\n" ++ "gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18Dr\n" ++ "G5gPcFw0sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3Os\n" ++ "zMOl6W8KjptlwlCFtaOgUxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8x\n" ++ "L4ysEr3vQCj8KWefshNPZiTEUxnpHikV7+ZtsH8tZ/3zbBt1RqPlShfppNcL\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFjTCCA3WgAwIBAgIEGErM1jANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJD\n" ++ "TjEwMC4GA1UECgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9y\n" ++ "aXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJPT1QwHhcNMTIwODA4MDMwNzAxWhcNMjkx\n" ++ "MjMxMDMwNzAxWjBWMQswCQYDVQQGEwJDTjEwMC4GA1UECgwnQ2hpbmEgRmluYW5j\n" ++ "aWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJP\n" ++ "T1QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXXWvNED8fBVnVBU03\n" ++ "sQ7smCuOFR36k0sXgiFxEFLXUWRwFsJVaU2OFW2fvwwbwuCjZ9YMrM8irq93VCpL\n" ++ "TIpTUnrD7i7es3ElweldPe6hL6P3KjzJIx1qqx2hp/Hz7KDVRM8Vz3IvHWOX6Jn5\n" ++ "/ZOkVIBMUtRSqy5J35DNuF++P96hyk0g1CXohClTt7GIH//62pCfCqktQT+x8Rgp\n" ++ "7hZZLDRJGqgG16iI0gNyejLi6mhNbiyWZXvKWfry4t3uMCz7zEasxGPrb382KzRz\n" ++ "EpR/38wmnvFyXVBlWY9ps4deMm/DGIq1lY+wejfeWkU7xzbh72fROdOXW3NiGUgt\n" ++ "hxwG+3SYIElz8AXSG7Ggo7cbcNOIabla1jj0Ytwli3i/+Oh+uFzJlU9fpy25IGvP\n" ++ "a931DfSCt/SyZi4QKPaXWnuWFo8BGS1sbn85WAZkgwGDg8NNkt0yxoekN+kWzqot\n" ++ "aK8KgWU6cMGbrU1tVMoqLUuFG7OA5nBFDWteNfB/O7ic5ARwiRIlk9oKmSJgamNg\n" ++ "TnYGmE69g60dWIolhdLHZR4tjsbftsbhf4oEIRUpdPA+nJCdDC7xij5aqgwJHsfV\n" ++ "PKPtl8MeNPo4+QgO48BdK4PRVmrJtqhUUy54Mmc9gn900PvhtgVguXDbjgv5E1hv\n" ++ "cWAQUhC5wUEJ73IfZzF4/5YFjQIDAQABo2MwYTAfBgNVHSMEGDAWgBTj/i39KNAL\n" ++ "tbq2osS/BqoFjJP7LzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAd\n" ++ "BgNVHQ4EFgQU4/4t/SjQC7W6tqLEvwaqBYyT+y8wDQYJKoZIhvcNAQELBQADggIB\n" ++ "ACXGumvrh8vegjmWPfBEp2uEcwPenStPuiB/vHiyz5ewG5zz13ku9Ui20vsXiObT\n" ++ "ej/tUxPQ4i9qecsAIyjmHjdXNYmEwnZPNDatZ8POQQaIxffu2Bq41gt/UP+TqhdL\n" ++ "jOztUmCypAbqTuv0axn96/Ua4CUqmtzHQTb3yHQFhDmVOdYLO6Qn+gjYXB74BGBS\n" ++ "ESgoA//vU2YApUo0FmZ8/Qmkrp5nGm9BC2sGE5uPhnEFtC+NiWYzKXZUmhH4J/qy\n" ++ "P5Hgzg0b8zAarb8iXRvTvyUFTeGSGn+ZnzxEk8rUQElsgIfXBDrDMlI1Dlb4pd19\n" ++ "xIsNER9Tyx6yF7Zod1rg1MvIB671Oi6ON7fQAUtDKXeMOZePglr4UeWJoBjnaH9d\n" ++ "Ci77o0cOPaYjesYBx4/IXr9tgFa+iiS6M+qf4TIRnvHST4D2G0CvOJ4RUHlzEhLN\n" ++ "5mydLIhyPDCBBpEi6lmt2hkuIsKNuYyH4Ga8cyNfIWRjgEj1oDwYPZTISEEdQLpe\n" ++ "/v5WOaHIz16eGWRGENoXkbcFgKyLmZJ956LYBws2J+dIeWCKw9cTXPhyQN9Ky8+Z\n" ++ "AAoACxGV2lZFA4gKn2fQ1XmxqI1AbQ3CekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ\n" ++ "5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCB\n" ++ "gTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G\n" ++ "A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNV\n" ++ "BAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEyMDEwMDAw\n" ++ "MDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3Jl\n" ++ "YXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01P\n" ++ "RE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0\n" ++ "aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3\n" ++ "UcEbVASY06m/weaKXTuH+7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI\n" ++ "2GqGd0S7WWaXUF601CxwRM/aN5VCaTwwxHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8\n" ++ "Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV4EajcNxo2f8ESIl33rXp\n" ++ "+2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA1KGzqSX+\n" ++ "DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5O\n" ++ "nKVIrLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW\n" ++ "/zAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6g\n" ++ "PKA6hjhodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9u\n" ++ "QXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOCAQEAPpiem/Yb6dc5t3iuHXIY\n" ++ "SdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CPOGEIqB6BCsAv\n" ++ "IC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/\n" ++ "RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4\n" ++ "zJVSk/BwJVmcIGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5dd\n" ++ "BA6+C4OmF4O5MBKgxTMVBbkN+8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IB\n" ++ "ZQ==\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTEL\n" ++ "MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE\n" ++ "BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMT\n" ++ "IkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwMzA2MDAw\n" ++ "MDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdy\n" ++ "ZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09N\n" ++ "T0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlv\n" ++ "biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSR\n" ++ "FtSrYpn1PlILBs5BAH+X4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0J\n" ++ "cfRK9ChQtP6IHG4/bC8vCVlbpVsLM5niwz2J+Wos77LTBumjQjBAMB0GA1UdDgQW\n" ++ "BBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/\n" ++ "BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VGFAkK+qDm\n" ++ "fQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdv\n" ++ "GDeAU/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY=\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCB\n" ++ "hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G\n" ++ "A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV\n" ++ "BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMTE5\n" ++ "MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgT\n" ++ "EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR\n" ++ "Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNh\n" ++ "dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR\n" ++ "6FSS0gpWsawNJN3Fz0RndJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8X\n" ++ "pz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZFGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC\n" ++ "9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+5eNu/Nio5JIk2kNrYrhV\n" ++ "/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pGx8cgoLEf\n" ++ "Zd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z\n" ++ "+pUX2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7w\n" ++ "qP/0uK3pN/u6uPQLOvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZah\n" ++ "SL0896+1DSJMwBGB7FY79tOi4lu3sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVIC\n" ++ "u9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+CGCe01a60y1Dma/RMhnEw6abf\n" ++ "Fobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5WdYgGq/yapiq\n" ++ "crxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E\n" ++ "FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB\n" ++ "/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvl\n" ++ "wFTPoCWOAvn9sKIN9SCYPBMtrFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM\n" ++ "4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+nq6PK7o9mfjYcwlYRm6mnPTXJ9OV\n" ++ "2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSgtZx8jb8uk2Intzna\n" ++ "FxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwWsRqZ\n" ++ "CuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiK\n" ++ "boHGhfKppC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmcke\n" ++ "jkk9u+UJueBPSZI9FoJAzMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yL\n" ++ "S0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHqZJx64SIDqZxubw5lT2yHh17zbqD5daWb\n" ++ "QOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk527RH89elWsn2/x20Kk4yl\n" ++ "0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7ILaZRfyHB\n" ++ "NVOFBkpdn627G190\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIB9zCCAX2gAwIBAgIQBiUzsUcDMydc+Y2aub/M+DAKBggqhkjOPQQDAzA9MQsw\n" ++ "CQYDVQQGEwJVUzESMBAGA1UEChMJQ2VydGFpbmx5MRowGAYDVQQDExFDZXJ0YWlu\n" ++ "bHkgUm9vdCBFMTAeFw0yMTA0MDEwMDAwMDBaFw00NjA0MDEwMDAwMDBaMD0xCzAJ\n" ++ "BgNVBAYTAlVTMRIwEAYDVQQKEwlDZXJ0YWlubHkxGjAYBgNVBAMTEUNlcnRhaW5s\n" ++ "eSBSb290IEUxMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE3m/4fxzf7flHh4axpMCK\n" ++ "+IKXgOqPyEpeKn2IaKcBYhSRJHpcnqMXfYqGITQYUBsQ3tA3SybHGWCA6TS9YBk2\n" ++ "QNYphwk8kXr2vBMj3VlOBF7PyAIcGFPBMdjaIOlEjeR2o0IwQDAOBgNVHQ8BAf8E\n" ++ "BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU8ygYy2R17ikq6+2uI1g4\n" ++ "hevIIgcwCgYIKoZIzj0EAwMDaAAwZQIxALGOWiDDshliTd6wT99u0nCK8Z9+aozm\n" ++ "ut6Dacpps6kFtZaSF4fC0urQe87YQVt8rgIwRt7qy12a7DLCZRawTDBcMPPaTnOG\n" ++ "BtjOiQRINzf43TNRnXCve1XYAS59BWQOhriR\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFRzCCAy+gAwIBAgIRAI4P+UuQcWhlM1T01EQ5t+AwDQYJKoZIhvcNAQELBQAw\n" ++ "PTELMAkGA1UEBhMCVVMxEjAQBgNVBAoTCUNlcnRhaW5seTEaMBgGA1UEAxMRQ2Vy\n" ++ "dGFpbmx5IFJvb3QgUjEwHhcNMjEwNDAxMDAwMDAwWhcNNDYwNDAxMDAwMDAwWjA9\n" ++ "MQswCQYDVQQGEwJVUzESMBAGA1UEChMJQ2VydGFpbmx5MRowGAYDVQQDExFDZXJ0\n" ++ "YWlubHkgUm9vdCBSMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANA2\n" ++ "1B/q3avk0bbm+yLA3RMNansiExyXPGhjZjKcA7WNpIGD2ngwEc/csiu+kr+O5MQT\n" ++ "vqRoTNoCaBZ0vrLdBORrKt03H2As2/X3oXyVtwxwhi7xOu9S98zTm/mLvg7fMbed\n" ++ "aFySpvXl8wo0tf97ouSHocavFwDvA5HtqRxOcT3Si2yJ9HiG5mpJoM610rCrm/b0\n" ++ "1C7jcvk2xusVtyWMOvwlDbMicyF0yEqWYZL1LwsYpfSt4u5BvQF5+paMjRcCMLT5\n" ++ "r3gajLQ2EBAHBXDQ9DGQilHFhiZ5shGIXsXwClTNSaa/ApzSRKft43jvRl5tcdF5\n" ++ "cBxGX1HpyTfcX35pe0HfNEXgO4T0oYoKNp43zGJS4YkNKPl6I7ENPT2a/Z2B7yyQ\n" ++ "wHtETrtJ4A5KVpK8y7XdeReJkd5hiXSSqOMyhb5OhaRLWcsrxXiOcVTQAjeZjOVJ\n" ++ "6uBUcqQRBi8LjMFbvrWhsFNunLhgkR9Za/kt9JQKl7XsxXYDVBtlUrpMklZRNaBA\n" ++ "2CnbrlJ2Oy0wQJuK0EJWtLeIAaSHO1OWzaMWj/Nmqhexx2DgwUMFDO6bW2BvBlyH\n" ++ "Wyf5QBGenDPBt+U1VwV/J84XIIwc/PH72jEpSe31C4SnT8H2TsIonPru4K8H+zMR\n" ++ "eiFPCyEQtkA6qyI6BJyLm4SGcprSp6XEtHWRqSsjAgMBAAGjQjBAMA4GA1UdDwEB\n" ++ "/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTgqj8ljZ9EXME66C6u\n" ++ "d0yEPmcM9DANBgkqhkiG9w0BAQsFAAOCAgEAuVevuBLaV4OPaAszHQNTVfSVcOQr\n" ++ "PbA56/qJYv331hgELyE03fFo8NWWWt7CgKPBjcZq91l3rhVkz1t5BXdm6ozTaw3d\n" ++ "8VkswTOlMIAVRQdFGjEitpIAq5lNOo93r6kiyi9jyhXWx8bwPWz8HA2YEGGeEaIi\n" ++ "1wrykXprOQ4vMMM2SZ/g6Q8CRFA3lFV96p/2O7qUpUzpvD5RtOjKkjZUbVwlKNrd\n" ++ "rRT90+7iIgXr0PK3aBLXWopBGsaSpVo7Y0VPv+E6dyIvXL9G+VoDhRNCX8reU9di\n" ++ "taY1BMJH/5n9hN9czulegChB8n3nHpDYT3Y+gjwN/KUD+nsa2UUeYNrEjvn8K8l7\n" ++ "lcUq/6qJ34IxD3L/DCfXCh5WAFAeDJDBlrXYFIW7pw0WwfgHJBu6haEaBQmAupVj\n" ++ "yTrsJZ9/nbqkRxWbRHDxakvWOF5D8xh+UG7pWijmZeZ3Gzr9Hb4DJqPb1OG7fpYn\n" ++ "Kx3upPvaJVQTA945xsMfTZDsjxtK0hzthZU4UHlG1sGQUDGpXJpuHfUzVounmdLy\n" ++ "yCwzk5Iwx06MZTMQZBf9JBeW0Y3COmor6xOLRPIh80oat3df1+2IpHLlOR+Vnb5n\n" ++ "wXARPbv0+Em34yaXOp/SX3z7wJl8OSngex2/DaeP0ik0biQVy96QXr8axGbqwua6\n" ++ "OV+KmalBWQewLK8=\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNV\n" ++ "BAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4X\n" ++ "DTA3MDYyOTE1MTMwNVoXDTI3MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQ\n" ++ "BgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwIQ2VydGlnbmEwggEiMA0GCSqGSIb3\n" ++ "DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7qXOEm7RFHYeGifBZ4\n" ++ "QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyHGxny\n" ++ "gQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbw\n" ++ "zBfsV1/pogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q\n" ++ "130yGLMLLGq/jj8UEYkgDncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2\n" ++ "JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKfIrjxwo1p3Po6WAbfAgMBAAGjgbwwgbkw\n" ++ "DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQtCRZvgHyUtVF9lo53BEw\n" ++ "ZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJBgNVBAYT\n" ++ "AkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzj\n" ++ "AQ/JSP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG\n" ++ "9w0BAQUFAAOCAQEAhQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8h\n" ++ "bV6lUmPOEvjvKtpv6zf+EwLHyzs+ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFnc\n" ++ "fca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1kluPBS1xp81HlDQwY9qcEQCYsuu\n" ++ "HWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY1gkIl2PlwS6w\n" ++ "t0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw\n" ++ "WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg==\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIGWzCCBEOgAwIBAgIRAMrpG4nxVQMNo+ZBbcTjpuEwDQYJKoZIhvcNAQELBQAw\n" ++ "WjELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczEcMBoGA1UECwwTMDAw\n" ++ "MiA0ODE0NjMwODEwMDAzNjEZMBcGA1UEAwwQQ2VydGlnbmEgUm9vdCBDQTAeFw0x\n" ++ "MzEwMDEwODMyMjdaFw0zMzEwMDEwODMyMjdaMFoxCzAJBgNVBAYTAkZSMRIwEAYD\n" ++ "VQQKDAlEaGlteW90aXMxHDAaBgNVBAsMEzAwMDIgNDgxNDYzMDgxMDAwMzYxGTAX\n" ++ "BgNVBAMMEENlcnRpZ25hIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw\n" ++ "ggIKAoICAQDNGDllGlmx6mQWDoyUJJV8g9PFOSbcDO8WV43X2KyjQn+Cyu3NW9sO\n" ++ "ty3tRQgXstmzy9YXUnIo245Onoq2C/mehJpNdt4iKVzSs9IGPjA5qXSjklYcoW9M\n" ++ "CiBtnyN6tMbaLOQdLNyzKNAT8kxOAkmhVECe5uUFoC2EyP+YbNDrihqECB63aCPu\n" ++ "I9Vwzm1RaRDuoXrC0SIxwoKF0vJVdlB8JXrJhFwLrN1CTivngqIkicuQstDuI7pm\n" ++ "TLtipPlTWmR7fJj6o0ieD5Wupxj0auwuA0Wv8HT4Ks16XdG+RCYyKfHx9WzMfgIh\n" ++ "C59vpD++nVPiz32pLHxYGpfhPTc3GGYo0kDFUYqMwy3OU4gkWGQwFsWq4NYKpkDf\n" ++ "ePb1BHxpE4S80dGnBs8B92jAqFe7OmGtBIyT46388NtEbVncSVmurJqZNjBBe3Yz\n" ++ "IoejwpKGbvlw7q6Hh5UbxHq9MfPU0uWZ/75I7HX1eBYdpnDBfzwboZL7z8g81sWT\n" ++ "Co/1VTp2lc5ZmIoJlXcymoO6LAQ6l73UL77XbJuiyn1tJslV1c/DeVIICZkHJC1k\n" ++ "JWumIWmbat10TWuXekG9qxf5kBdIjzb5LdXF2+6qhUVB+s06RbFo5jZMm5BX7CO5\n" ++ "hwjCxAnxl4YqKE3idMDaxIzb3+KhF1nOJFl0Mdp//TBt2dzhauH8XwIDAQABo4IB\n" ++ "GjCCARYwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE\n" ++ "FBiHVuBud+4kNTxOc5of1uHieX4rMB8GA1UdIwQYMBaAFBiHVuBud+4kNTxOc5of\n" ++ "1uHieX4rMEQGA1UdIAQ9MDswOQYEVR0gADAxMC8GCCsGAQUFBwIBFiNodHRwczov\n" ++ "L3d3d3cuY2VydGlnbmEuZnIvYXV0b3JpdGVzLzBtBgNVHR8EZjBkMC+gLaArhilo\n" ++ "dHRwOi8vY3JsLmNlcnRpZ25hLmZyL2NlcnRpZ25hcm9vdGNhLmNybDAxoC+gLYYr\n" ++ "aHR0cDovL2NybC5kaGlteW90aXMuY29tL2NlcnRpZ25hcm9vdGNhLmNybDANBgkq\n" ++ "hkiG9w0BAQsFAAOCAgEAlLieT/DjlQgi581oQfccVdV8AOItOoldaDgvUSILSo3L\n" ++ "6btdPrtcPbEo/uRTVRPPoZAbAh1fZkYJMyjhDSSXcNMQH+pkV5a7XdrnxIxPTGRG\n" ++ "HVyH41neQtGbqH6mid2PHMkwgu07nM3A6RngatgCdTer9zQoKJHyBApPNeNgJgH6\n" ++ "0BGM+RFq7q89w1DTj18zeTyGqHNFkIwgtnJzFyO+B2XleJINugHA64wcZr+shncB\n" ++ "lA2c5uk5jR+mUYyZDDl34bSb+hxnV29qao6pK0xXeXpXIs/NX2NGjVxZOob4Mkdi\n" ++ "o2cNGJHc+6Zr9UhhcyNZjgKnvETq9Emd8VRY+WCv2hikLyhF3HqgiIZd8zvn/yk1\n" ++ "gPxkQ5Tm4xxvvq0OKmOZK8l+hfZx6AYDlf7ej0gcWtSS6Cvu5zHbugRqh5jnxV/v\n" ++ "faci9wHYTfmJ0A6aBVmknpjZbyvKcL5kwlWj9Omvw5Ip3IgWJJk8jSaYtlu3zM63\n" ++ "Nwf9JtmYhST/WSMDmu2dnajkXjjO11INb9I/bbEFa0nOipFGc/T2L/Coc3cOZayh\n" ++ "jWZSaX5LaAzHHjcng6WMxwLkFM1JAbBzs/3GkDpv0mztO+7skb6iQ12LAEpmJURw\n" ++ "3kAP+HwV96LOPNdeE4yBFxgX0b3xdxA61GU5wSesVywlVP+i2k+KYTlerj1KjL0=\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIICZTCCAeugAwIBAgIQeI8nXIESUiClBNAt3bpz9DAKBggqhkjOPQQDAzB0MQsw\n" ++ "CQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMScw\n" ++ "JQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxGTAXBgNVBAMT\n" ++ "EENlcnR1bSBFQy0zODQgQ0EwHhcNMTgwMzI2MDcyNDU0WhcNNDMwMzI2MDcyNDU0\n" ++ "WjB0MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBT\n" ++ "LkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxGTAX\n" ++ "BgNVBAMTEENlcnR1bSBFQy0zODQgQ0EwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATE\n" ++ "KI6rGFtqvm5kN2PkzeyrOvfMobgOgknXhimfoZTy42B4mIF4Bk3y7JoOV2CDn7Tm\n" ++ "Fy8as10CW4kjPMIRBSqniBMY81CE1700LCeJVf/OTOffph8oxPBUw7l8t1Ot68Kj\n" ++ "QjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI0GZnQkdjrzife81r1HfS+8\n" ++ "EF9LMA4GA1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNoADBlAjADVS2m5hjEfO/J\n" ++ "UG7BJw+ch69u1RsIGL2SKcHvlJF40jocVYli5RsJHrpka/F2tNQCMQC0QoSZ/6vn\n" ++ "nvuRlydd3LBbMHHOXjgaatkl5+r3YZJW+OraNsKHZZYuciUvf9/DE8k=\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBM\n" ++ "MSIwIAYDVQQKExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5D\n" ++ "ZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBU\n" ++ "cnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIyMTIwNzM3WhcNMjkxMjMxMTIwNzM3\n" ++ "WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMg\n" ++ "Uy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSIw\n" ++ "IAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0B\n" ++ "AQEFAAOCAQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rH\n" ++ "UV+rpDKmYYe2bg+G0jACl/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LM\n" ++ "TXPb865Px1bVWqeWifrzq2jUI4ZZJ88JJ7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVU\n" ++ "BBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4fOQtf/WsX+sWn7Et0brM\n" ++ "kUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0cvW0QM8x\n" ++ "AcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNV\n" ++ "HRMBAf8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNV\n" ++ "HQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15y\n" ++ "sHhE49wcrwn9I0j6vSrEuVUEtRCjjSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfL\n" ++ "I9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1mS1FhIrlQgnXdAIv94nYmem8\n" ++ "J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5ajZt3hrvJBW8qY\n" ++ "VoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI\n" ++ "03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw=\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIF0jCCA7qgAwIBAgIQIdbQSk8lD8kyN/yqXhKN6TANBgkqhkiG9w0BAQ0FADCB\n" ++ "gDELMAkGA1UEBhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMu\n" ++ "QS4xJzAlBgNVBAsTHkNlcnR1bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIG\n" ++ "A1UEAxMbQ2VydHVtIFRydXN0ZWQgTmV0d29yayBDQSAyMCIYDzIwMTExMDA2MDgz\n" ++ "OTU2WhgPMjA0NjEwMDYwODM5NTZaMIGAMQswCQYDVQQGEwJQTDEiMCAGA1UEChMZ\n" ++ "VW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRp\n" ++ "ZmljYXRpb24gQXV0aG9yaXR5MSQwIgYDVQQDExtDZXJ0dW0gVHJ1c3RlZCBOZXR3\n" ++ "b3JrIENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC9+Xj45tWA\n" ++ "DGSdhhuWZGc/IjoedQF97/tcZ4zJzFxrqZHmuULlIEub2pt7uZld2ZuAS9eEQCsn\n" ++ "0+i6MLs+CRqnSZXvK0AkwpfHp+6bJe+oCgCXhVqqndwpyeI1B+twTUrWwbNWuKFB\n" ++ "OJvR+zF/j+Bf4bE/D44WSWDXBo0Y+aomEKsq09DRZ40bRr5HMNUuctHFY9rnY3lE\n" ++ "fktjJImGLjQ/KUxSiyqnwOKRKIm5wFv5HdnnJ63/mgKXwcZQkpsCLL2puTRZCr+E\n" ++ "Sv/f/rOf69me4Jgj7KZrdxYq28ytOxykh9xGc14ZYmhFV+SQgkK7QtbwYeDBoz1m\n" ++ "o130GO6IyY0XRSmZMnUCMe4pJshrAua1YkV/NxVaI2iJ1D7eTiew8EAMvE0Xy02i\n" ++ "sx7QBlrd9pPPV3WZ9fqGGmd4s7+W/jTcvedSVuWz5XV710GRBdxdaeOVDUO5/IOW\n" ++ "OZV7bIBaTxNyxtd9KXpEulKkKtVBRgkg/iKgtlswjbyJDNXXcPiHUv3a76xRLgez\n" ++ "Tv7QCdpw75j6VuZt27VXS9zlLCUVyJ4ueE742pyehizKV/Ma5ciSixqClnrDvFAS\n" ++ "adgOWkaLOusm+iPJtrCBvkIApPjW/jAux9JG9uWOdf3yzLnQh1vMBhBgu4M1t15n\n" ++ "3kfsmUjxpKEV/q2MYo45VU85FrmxY53/twIDAQABo0IwQDAPBgNVHRMBAf8EBTAD\n" ++ "AQH/MB0GA1UdDgQWBBS2oVQ5AsOgP46KvPrU+Bym0ToO/TAOBgNVHQ8BAf8EBAMC\n" ++ "AQYwDQYJKoZIhvcNAQENBQADggIBAHGlDs7k6b8/ONWJWsQCYftMxRQXLYtPU2sQ\n" ++ "F/xlhMcQSZDe28cmk4gmb3DWAl45oPePq5a1pRNcgRRtDoGCERuKTsZPpd1iHkTf\n" ++ "CVn0W3cLN+mLIMb4Ck4uWBzrM9DPhmDJ2vuAL55MYIR4PSFk1vtBHxgP58l1cb29\n" ++ "XN40hz5BsA72udY/CROWFC/emh1auVbONTqwX3BNXuMp8SMoclm2q8KMZiYcdywm\n" ++ "djWLKKdpoPk79SPdhRB0yZADVpHnr7pH1BKXESLjokmUbOe3lEu6LaTaM4tMpkT/\n" ++ "WjzGHWTYtTHkpjx6qFcL2+1hGsvxznN3Y6SHb0xRONbkX8eftoEq5IVIeVheO/jb\n" ++ "AoJnwTnbw3RLPTYe+SmTiGhbqEQZIfCn6IENLOiTNrQ3ssqwGyZ6miUfmpqAnksq\n" ++ "P/ujmv5zMnHCnsZy4YpoJ/HkD7TETKVhk/iXEAcqMCWpuchxuO9ozC1+9eB+D4Ko\n" ++ "b7a6bINDd82Kkhehnlt4Fj1F4jNy3eFmypnTycUm/Q1oBEauttmbjL4ZvrHG8hnj\n" ++ "XALKLNhvSgfZyTXaQHXyxKcZb55CEJh15pWLYLztxRLXis7VmFxWlgPF7ncGNf/P\n" ++ "5O4/E2Hu29othfDNrp2yGAlFw5Khchf8R7agCyzxxN5DaAhqXzvwdmP7zAYspsbi\n" ++ "DrW5viSP\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFwDCCA6igAwIBAgIQHr9ZULjJgDdMBvfrVU+17TANBgkqhkiG9w0BAQ0FADB6\n" ++ "MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEu\n" ++ "MScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxHzAdBgNV\n" ++ "BAMTFkNlcnR1bSBUcnVzdGVkIFJvb3QgQ0EwHhcNMTgwMzE2MTIxMDEzWhcNNDMw\n" ++ "MzE2MTIxMDEzWjB6MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEg\n" ++ "U3lzdGVtcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRo\n" ++ "b3JpdHkxHzAdBgNVBAMTFkNlcnR1bSBUcnVzdGVkIFJvb3QgQ0EwggIiMA0GCSqG\n" ++ "SIb3DQEBAQUAA4ICDwAwggIKAoICAQDRLY67tzbqbTeRn06TpwXkKQMlzhyC93yZ\n" ++ "n0EGze2jusDbCSzBfN8pfktlL5On1AFrAygYo9idBcEq2EXxkd7fO9CAAozPOA/q\n" ++ "p1x4EaTByIVcJdPTsuclzxFUl6s1wB52HO8AU5853BSlLCIls3Jy/I2z5T4IHhQq\n" ++ "NwuIPMqw9MjCoa68wb4pZ1Xi/K1ZXP69VyywkI3C7Te2fJmItdUDmj0VDT06qKhF\n" ++ "8JVOJVkdzZhpu9PMMsmN74H+rX2Ju7pgE8pllWeg8xn2A1bUatMn4qGtg/BKEiJ3\n" ++ "HAVz4hlxQsDsdUaakFjgao4rpUYwBI4Zshfjvqm6f1bxJAPXsiEodg42MEx51UGa\n" ++ "mqi4NboMOvJEGyCI98Ul1z3G4z5D3Yf+xOr1Uz5MZf87Sst4WmsXXw3Hw09Omiqi\n" ++ "7VdNIuJGmj8PkTQkfVXjjJU30xrwCSss0smNtA0Aq2cpKNgB9RkEth2+dv5yXMSF\n" ++ "ytKAQd8FqKPVhJBPC/PgP5sZ0jeJP/J7UhyM9uH3PAeXjA6iWYEMspA90+NZRu0P\n" ++ "qafegGtaqge2Gcu8V/OXIXoMsSt0Puvap2ctTMSYnjYJdmZm/Bo/6khUHL4wvYBQ\n" ++ "v3y1zgD2DGHZ5yQD4OMBgQ692IU0iL2yNqh7XAjlRICMb/gv1SHKHRzQ+8S1h9E6\n" ++ "Tsd2tTVItQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSM+xx1\n" ++ "vALTn04uSNn5YFSqxLNP+jAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQENBQAD\n" ++ "ggIBAEii1QALLtA/vBzVtVRJHlpr9OTy4EA34MwUe7nJ+jW1dReTagVphZzNTxl4\n" ++ "WxmB82M+w85bj/UvXgF2Ez8sALnNllI5SW0ETsXpD4YN4fqzX4IS8TrOZgYkNCvo\n" ++ "zMrnadyHncI013nR03e4qllY/p0m+jiGPp2Kh2RX5Rc64vmNueMzeMGQ2Ljdt4NR\n" ++ "5MTMI9UGfOZR0800McD2RrsLrfw9EAUqO0qRJe6M1ISHgCq8CYyqOhNf6DR5UMEQ\n" ++ "GfnTKB7U0VEwKbOukGfWHwpjscWpxkIxYxeU72nLL/qMFH3EQxiJ2fAyQOaA4kZf\n" ++ "5ePBAFmo+eggvIksDkc0C+pXwlM2/KfUrzHN/gLldfq5Jwn58/U7yn2fqSLLiMmq\n" ++ "0Uc9NneoWWRrJ8/vJ8HjJLWG965+Mk2weWjROeiQWMODvA8s1pfrzgzhIMfatz7D\n" ++ "P78v3DSk+yshzWePS/Tj6tQ/50+6uaWTRRxmHyH6ZF5v4HaUMst19W7l9o/HuKTM\n" ++ "qJZ9ZPskWkoDbGs4xugDQ5r3V7mzKWmTOPQD8rv7gmsHINFSH5pkAnuYZttcTVoP\n" ++ "0ISVoDwUQwbKytu4QTbaakRnh6+v40URFWkIsr4WOZckbxJF0WddCajJFdr60qZf\n" ++ "E2Efv4WstK2tBZQIgx51F9NxO5NQI1mg7TyRVJ12AMXDuDjb\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIICHTCCAaOgAwIBAgIUQ3CCd89NXTTxyq4yLzf39H91oJ4wCgYIKoZIzj0EAwMw\n" ++ "TjELMAkGA1UEBhMCVVMxEjAQBgNVBAoMCUNvbW1TY29wZTErMCkGA1UEAwwiQ29t\n" ++ "bVNjb3BlIFB1YmxpYyBUcnVzdCBFQ0MgUm9vdC0wMTAeFw0yMTA0MjgxNzM1NDNa\n" ++ "Fw00NjA0MjgxNzM1NDJaME4xCzAJBgNVBAYTAlVTMRIwEAYDVQQKDAlDb21tU2Nv\n" ++ "cGUxKzApBgNVBAMMIkNvbW1TY29wZSBQdWJsaWMgVHJ1c3QgRUNDIFJvb3QtMDEw\n" ++ "djAQBgcqhkjOPQIBBgUrgQQAIgNiAARLNumuV16ocNfQj3Rid8NeeqrltqLxeP0C\n" ++ "flfdkXmcbLlSiFS8LwS+uM32ENEp7LXQoMPwiXAZu1FlxUOcw5tjnSCDPgYLpkJE\n" ++ "hRGnSjot6dZoL0hOUysHP029uax3OVejQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYD\n" ++ "VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSOB2LAUN3GGQYARnQE9/OufXVNMDAKBggq\n" ++ "hkjOPQQDAwNoADBlAjEAnDPfQeMjqEI2Jpc1XHvr20v4qotzVRVcrHgpD7oh2MSg\n" ++ "2NED3W3ROT3Ek2DS43KyAjB8xX6I01D1HiXo+k515liWpDVfG2XqYZpwI7UNo5uS\n" ++ "Um9poIyNStDuiw7LR47QjRE=\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIICHDCCAaOgAwIBAgIUKP2ZYEFHpgE6yhR7H+/5aAiDXX0wCgYIKoZIzj0EAwMw\n" ++ "TjELMAkGA1UEBhMCVVMxEjAQBgNVBAoMCUNvbW1TY29wZTErMCkGA1UEAwwiQ29t\n" ++ "bVNjb3BlIFB1YmxpYyBUcnVzdCBFQ0MgUm9vdC0wMjAeFw0yMTA0MjgxNzQ0NTRa\n" ++ "Fw00NjA0MjgxNzQ0NTNaME4xCzAJBgNVBAYTAlVTMRIwEAYDVQQKDAlDb21tU2Nv\n" ++ "cGUxKzApBgNVBAMMIkNvbW1TY29wZSBQdWJsaWMgVHJ1c3QgRUNDIFJvb3QtMDIw\n" ++ "djAQBgcqhkjOPQIBBgUrgQQAIgNiAAR4MIHoYx7l63FRD/cHB8o5mXxO1Q/MMDAL\n" ++ "j2aTPs+9xYa9+bG3tD60B8jzljHz7aRP+KNOjSkVWLjVb3/ubCK1sK9IRQq9qEmU\n" ++ "v4RDsNuESgMjGWdqb8FuvAY5N9GIIvejQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYD\n" ++ "VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTmGHX/72DehKT1RsfeSlXjMjZ59TAKBggq\n" ++ "hkjOPQQDAwNnADBkAjAmc0l6tqvmSfR9Uj/UQQSugEODZXW5hYA4O9Zv5JOGq4/n\n" ++ "ich/m35rChJVYaoR4HkCMHfoMXGsPHED1oQmHhS48zs73u1Z/GtMMH9ZzkXpc2AV\n" ++ "mkzw5l4lIhVtwodZ0LKOag==\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFbDCCA1SgAwIBAgIUPgNJgXUWdDGOTKvVxZAplsU5EN0wDQYJKoZIhvcNAQEL\n" ++ "BQAwTjELMAkGA1UEBhMCVVMxEjAQBgNVBAoMCUNvbW1TY29wZTErMCkGA1UEAwwi\n" ++ "Q29tbVNjb3BlIFB1YmxpYyBUcnVzdCBSU0EgUm9vdC0wMTAeFw0yMTA0MjgxNjQ1\n" ++ "NTRaFw00NjA0MjgxNjQ1NTNaME4xCzAJBgNVBAYTAlVTMRIwEAYDVQQKDAlDb21t\n" ++ "U2NvcGUxKzApBgNVBAMMIkNvbW1TY29wZSBQdWJsaWMgVHJ1c3QgUlNBIFJvb3Qt\n" ++ "MDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCwSGWjDR1C45FtnYSk\n" ++ "YZYSwu3D2iM0GXb26v1VWvZVAVMP8syMl0+5UMuzAURWlv2bKOx7dAvnQmtVzslh\n" ++ "suitQDy6uUEKBU8bJoWPQ7VAtYXR1HHcg0Hz9kXHgKKEUJdGzqAMxGBWBB0HW0al\n" ++ "DrJLpA6lfO741GIDuZNqihS4cPgugkY4Iw50x2tBt9Apo52AsH53k2NC+zSDO3Oj\n" ++ "WiE260f6GBfZumbCk6SP/F2krfxQapWsvCQz0b2If4b19bJzKo98rwjyGpg/qYFl\n" ++ "P8GMicWWMJoKz/TUyDTtnS+8jTiGU+6Xn6myY5QXjQ/cZip8UlF1y5mO6D1cv547\n" ++ "KI2DAg+pn3LiLCuz3GaXAEDQpFSOm117RTYm1nJD68/A6g3czhLmfTifBSeolz7p\n" ++ "UcZsBSjBAg/pGG3svZwG1KdJ9FQFa2ww8esD1eo9anbCyxooSU1/ZOD6K9pzg4H/\n" ++ "kQO9lLvkuI6cMmPNn7togbGEW682v3fuHX/3SZtS7NJ3Wn2RnU3COS3kuoL4b/JO\n" ++ "Hg9O5j9ZpSPcPYeoKFgo0fEbNttPxP/hjFtyjMcmAyejOQoBqsCyMWCDIqFPEgkB\n" ++ "Ea801M/XrmLTBQe0MXXgDW1XT2mH+VepuhX2yFJtocucH+X8eKg1mp9BFM6ltM6U\n" ++ "CBwJrVbl2rZJmkrqYxhTnCwuwwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4G\n" ++ "A1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUN12mmnQywsL5x6YVEFm45P3luG0wDQYJ\n" ++ "KoZIhvcNAQELBQADggIBAK+nz97/4L1CjU3lIpbfaOp9TSp90K09FlxD533Ahuh6\n" ++ "NWPxzIHIxgvoLlI1pKZJkGNRrDSsBTtXAOnTYtPZKdVUvhwQkZyybf5Z/Xn36lbQ\n" ++ "nmhUQo8mUuJM3y+Xpi/SB5io82BdS5pYV4jvguX6r2yBS5KPQJqTRlnLX3gWsWc+\n" ++ "QgvfKNmwrZggvkN80V4aCRckjXtdlemrwWCrWxhkgPut4AZ9HcpZuPN4KWfGVh2v\n" ++ "trV0KnahP/t1MJ+UXjulYPPLXAziDslg+MkfFoom3ecnf+slpoq9uC02EJqxWE2a\n" ++ "aE9gVOX2RhOOiKy8IUISrcZKiX2bwdgt6ZYD9KJ0DLwAHb/WNyVntHKLr4W96ioD\n" ++ "j8z7PEQkguIBpQtZtjSNMgsSDesnwv1B10A8ckYpwIzqug/xBpMu95yo9GA+o/E4\n" ++ "Xo4TwbM6l4c/ksp4qRyv0LAbJh6+cOx69TOY6lz/KwsETkPdY34Op054A5U+1C0w\n" ++ "lREQKC6/oAI+/15Z0wUOlV9TRe9rh9VIzRamloPh37MG88EU26fsHItdkJANclHn\n" ++ "YfkUyq+Dj7+vsQpZXdxc1+SWrVtgHdqul7I52Qb1dgAT+GhMIbA1xNxVssnBQVoc\n" ++ "icCMb3SgazNNtQEo/a2tiRc7ppqEvOuM6sRxJKi6KfkIsidWNTJf6jn7MZrVGczw\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFbDCCA1SgAwIBAgIUVBa/O345lXGN0aoApYYNK496BU4wDQYJKoZIhvcNAQEL\n" ++ "BQAwTjELMAkGA1UEBhMCVVMxEjAQBgNVBAoMCUNvbW1TY29wZTErMCkGA1UEAwwi\n" ++ "Q29tbVNjb3BlIFB1YmxpYyBUcnVzdCBSU0EgUm9vdC0wMjAeFw0yMTA0MjgxNzE2\n" ++ "NDNaFw00NjA0MjgxNzE2NDJaME4xCzAJBgNVBAYTAlVTMRIwEAYDVQQKDAlDb21t\n" ++ "U2NvcGUxKzApBgNVBAMMIkNvbW1TY29wZSBQdWJsaWMgVHJ1c3QgUlNBIFJvb3Qt\n" ++ "MDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDh+g77aAASyE3VrCLE\n" ++ "NQE7xVTlWXZjpX/rwcRqmL0yjReA61260WI9JSMZNRTpf4mnG2I81lDnNJUDMrG0\n" ++ "kyI9p+Kx7eZ7Ti6Hmw0zdQreqjXnfuU2mKKuJZ6VszKWpCtYHu8//mI0SFHRtI1C\n" ++ "rWDaSWqVcN3SAOLMV2MCe5bdSZdbkk6V0/nLKR8YSvgBKtJjCW4k6YnS5cciTNxz\n" ++ "hkcAqg2Ijq6FfUrpuzNPDlJwnZXjfG2WWy09X6GDRl224yW4fKcZgBzqZUPckXk2\n" ++ "LHR88mcGyYnJ27/aaL8j7dxrrSiDeS/sOKUNNwFnJ5rpM9kzXzehxfCrPfp4sOcs\n" ++ "n/Y+n2Dg70jpkEUeBVF4GiwSLFworA2iI540jwXmojPOEXcT1A6kHkIfhs1w/tku\n" ++ "FT0du7jyU1fbzMZ0KZwYszZ1OC4PVKH4kh+Jlk+71O6d6Ts2QrUKOyrUZHk2EOH5\n" ++ "kQMreyBUzQ0ZGshBMjTRsJnhkB4BQDa1t/qp5Xd1pCKBXbCL5CcSD1SIxtuFdOa3\n" ++ "wNemKfrb3vOTlycEVS8KbzfFPROvCgCpLIscgSjX74Yxqa7ybrjKaixUR9gqiC6v\n" ++ "wQcQeKwRoi9C8DfF8rhW3Q5iLc4tVn5V8qdE9isy9COoR+jUKgF4z2rDN6ieZdIs\n" ++ "5fq6M8EGRPbmz6UNp2YINIos8wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4G\n" ++ "A1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUR9DnsSL/nSz12Vdgs7GxcJXvYXowDQYJ\n" ++ "KoZIhvcNAQELBQADggIBAIZpsU0v6Z9PIpNojuQhmaPORVMbc0RTAIFhzTHjCLqB\n" ++ "KCh6krm2qMhDnscTJk3C2OVVnJJdUNjCK9v+5qiXz1I6JMNlZFxHMaNlNRPDk7n3\n" ++ "+VGXu6TwYofF1gbTl4MgqX67tiHCpQ2EAOHyJxCDut0DgdXdaMNmEMjRdrSzbyme\n" ++ "APnCKfWxkxlSaRosTKCL4BWaMS/TiJVZbuXEs1DIFAhKm4sTg7GkcrI7djNB3Nyq\n" ++ "pgdvHSQSn8h2vS/ZjvQs7rfSOBAkNlEv41xdgSGn2rtO/+YHqP65DSdsu3BaVXoT\n" ++ "6fEqSWnHX4dXTEN5bTpl6TBcQe7rd6VzEojov32u5cSoHw2OHG1QAk8mGEPej1WF\n" ++ "sQs3BWDJVTkSBKEqz3EWnzZRSb9wO55nnPt7eck5HHisd5FUmrh1CoFSl+NmYWvt\n" ++ "PjgelmFV4ZFUjO2MJB+ByRCac5krFk5yAD9UG/iNuovnFNa2RU9g7Jauwy8CTl2d\n" ++ "lklyALKrdVwPaFsdZcJfMw8eD/A7hvWwTruc9+olBdytoptLFwG+Qt81IR2tq670\n" ++ "v64fG9PiO/yzcnMcmyiQiRM9HcEARwmWmjgb3bHPDcK0RPOWlc4yOo80nOAXx17O\n" ++ "rg3bhzjlP1v9mxnhMUF6cKojawHhRUzNlM47ni3niAIi9G7oyOzWPPO5std3eqx7\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb\n" ++ "MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow\n" ++ "GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmlj\n" ++ "YXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVowezEL\n" ++ "MAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE\n" ++ "BwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNVBAMM\n" ++ "GEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP\n" ++ "ADCCAQoCggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQua\n" ++ "BtDFcCLNSS1UY8y2bmhGC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe\n" ++ "3M/vg4aijJRPn2jymJBGhCfHdr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4\n" ++ "YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZR\n" ++ "rOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cm\n" ++ "ez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQU\n" ++ "oBEKIz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF\n" ++ "MAMBAf8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20v\n" ++ "QUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29t\n" ++ "b2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG9w0BAQUF\n" ++ "AAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm7l3sAg9g1o1Q\n" ++ "GE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz\n" ++ "Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2\n" ++ "G9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsi\n" ++ "l2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3\n" ++ "smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg==\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIC2zCCAmCgAwIBAgIQfMmPK4TX3+oPyWWa00tNljAKBggqhkjOPQQDAzBIMQsw\n" ++ "CQYDVQQGEwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRS\n" ++ "VVNUIEJSIFJvb3QgQ0EgMSAyMDIwMB4XDTIwMDIxMTA5NDUwMFoXDTM1MDIxMTA5\n" ++ "NDQ1OVowSDELMAkGA1UEBhMCREUxFTATBgNVBAoTDEQtVHJ1c3QgR21iSDEiMCAG\n" ++ "A1UEAxMZRC1UUlVTVCBCUiBSb290IENBIDEgMjAyMDB2MBAGByqGSM49AgEGBSuB\n" ++ "BAAiA2IABMbLxyjR+4T1mu9CFCDhQ2tuda38KwOE1HaTJddZO0Flax7mNCq7dPYS\n" ++ "zuht56vkPE4/RAiLzRZxy7+SmfSk1zxQVFKQhYN4lGdnoxwJGT11NIXe7WB9xwy0\n" ++ "QVK5buXuQqOCAQ0wggEJMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFHOREKv/\n" ++ "VbNafAkl1bK6CKBrqx9tMA4GA1UdDwEB/wQEAwIBBjCBxgYDVR0fBIG+MIG7MD6g\n" ++ "PKA6hjhodHRwOi8vY3JsLmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X2JyX3Jvb3Rf\n" ++ "Y2FfMV8yMDIwLmNybDB5oHegdYZzbGRhcDovL2RpcmVjdG9yeS5kLXRydXN0Lm5l\n" ++ "dC9DTj1ELVRSVVNUJTIwQlIlMjBSb290JTIwQ0ElMjAxJTIwMjAyMCxPPUQtVHJ1\n" ++ "c3QlMjBHbWJILEM9REU/Y2VydGlmaWNhdGVyZXZvY2F0aW9ubGlzdDAKBggqhkjO\n" ++ "PQQDAwNpADBmAjEAlJAtE/rhY/hhY+ithXhUkZy4kzg+GkHaQBZTQgjKL47xPoFW\n" ++ "wKrY7RjEsK70PvomAjEA8yjixtsrmfu3Ubgko6SUeho/5jbiA1czijDLgsfWFBHV\n" ++ "dWNbFJWcHwHP2NVypw87\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIC2zCCAmCgAwIBAgIQXwJB13qHfEwDo6yWjfv/0DAKBggqhkjOPQQDAzBIMQsw\n" ++ "CQYDVQQGEwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRS\n" ++ "VVNUIEVWIFJvb3QgQ0EgMSAyMDIwMB4XDTIwMDIxMTEwMDAwMFoXDTM1MDIxMTA5\n" ++ "NTk1OVowSDELMAkGA1UEBhMCREUxFTATBgNVBAoTDEQtVHJ1c3QgR21iSDEiMCAG\n" ++ "A1UEAxMZRC1UUlVTVCBFViBSb290IENBIDEgMjAyMDB2MBAGByqGSM49AgEGBSuB\n" ++ "BAAiA2IABPEL3YZDIBnfl4XoIkqbz52Yv7QFJsnL46bSj8WeeHsxiamJrSc8ZRCC\n" ++ "/N/DnU7wMyPE0jL1HLDfMxddxfCxivnvubcUyilKwg+pf3VlSSowZ/Rk99Yad9rD\n" ++ "wpdhQntJraOCAQ0wggEJMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFH8QARY3\n" ++ "OqQo5FD4pPfsazK2/umLMA4GA1UdDwEB/wQEAwIBBjCBxgYDVR0fBIG+MIG7MD6g\n" ++ "PKA6hjhodHRwOi8vY3JsLmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X2V2X3Jvb3Rf\n" ++ "Y2FfMV8yMDIwLmNybDB5oHegdYZzbGRhcDovL2RpcmVjdG9yeS5kLXRydXN0Lm5l\n" ++ "dC9DTj1ELVRSVVNUJTIwRVYlMjBSb290JTIwQ0ElMjAxJTIwMjAyMCxPPUQtVHJ1\n" ++ "c3QlMjBHbWJILEM9REU/Y2VydGlmaWNhdGVyZXZvY2F0aW9ubGlzdDAKBggqhkjO\n" ++ "PQQDAwNpADBmAjEAyjzGKnXCXnViOTYAYFqLwZOZzNnbQTs7h5kXO9XMT8oi96CA\n" ++ "y/m0sRtW9XLS/BnRAjEAkfcwkz8QRitxpNA7RJvAKQIFskF3UfN5Wp6OFKBOQtJb\n" ++ "gfM0agPnIjhQW+0ZT0MW\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRF\n" ++ "MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBD\n" ++ "bGFzcyAzIENBIDIgMjAwOTAeFw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NTha\n" ++ "ME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMM\n" ++ "HkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIwDQYJKoZIhvcNAQEB\n" ++ "BQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOADER03\n" ++ "UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42\n" ++ "tSHKXzlABF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9R\n" ++ "ySPocq60vFYJfxLLHLGvKZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsM\n" ++ "lFqVlNpQmvH/pStmMaTJOKDfHR+4CS7zp+hnUquVH+BGPtikw8paxTGA6Eian5Rp\n" ++ "/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUCAwEAAaOCARowggEWMA8G\n" ++ "A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ4PGEMA4G\n" ++ "A1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVj\n" ++ "dG9yeS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUy\n" ++ "MENBJTIwMiUyMDIwMDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRl\n" ++ "cmV2b2NhdGlvbmxpc3QwQ6BBoD+GPWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3Js\n" ++ "L2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAwOS5jcmwwDQYJKoZIhvcNAQEL\n" ++ "BQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm2H6NMLVwMeni\n" ++ "acfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0\n" ++ "o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4K\n" ++ "zCUqNQT4YJEVdT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8\n" ++ "PIWmawomDeCTmGCufsYkl4phX5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3Y\n" ++ "Johw1+qRzT65ysCQblrGXnRl11z+o+I=\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRF\n" ++ "MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBD\n" ++ "bGFzcyAzIENBIDIgRVYgMjAwOTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUw\n" ++ "NDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNV\n" ++ "BAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAwOTCCASIwDQYJKoZI\n" ++ "hvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfSegpn\n" ++ "ljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM0\n" ++ "3TP1YtHhzRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6Z\n" ++ "qQTMFexgaDbtCHu39b+T7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lR\n" ++ "p75mpoo6Kr3HGrHhFPC+Oh25z1uxav60sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8\n" ++ "HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure3511H3a6UCAwEAAaOCASQw\n" ++ "ggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyvcop9Ntea\n" ++ "HNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFw\n" ++ "Oi8vZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xh\n" ++ "c3MlMjAzJTIwQ0ElMjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1E\n" ++ "RT9jZXJ0aWZpY2F0ZXJldm9jYXRpb25saXN0MEagRKBChkBodHRwOi8vd3d3LmQt\n" ++ "dHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xhc3NfM19jYV8yX2V2XzIwMDku\n" ++ "Y3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+PPoeUSbrh/Yp\n" ++ "3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05\n" ++ "nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNF\n" ++ "CSuGdXzfX2lXANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7na\n" ++ "xpeG0ILD5EJt/rDiZE4OJudANCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqX\n" ++ "KVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVvw9y4AyHqnxbxLFS1\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBl\n" ++ "MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n" ++ "d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv\n" ++ "b3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzExMTEwMDAwMDAwWjBlMQswCQYDVQQG\n" ++ "EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl\n" ++ "cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwggEi\n" ++ "MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7c\n" ++ "JpSIqvTO9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYP\n" ++ "mDI2dsze3Tyoou9q+yHyUmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+\n" ++ "wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4\n" ++ "VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpyoeb6pNnVFzF1roV9Iq4/\n" ++ "AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whfGHdPAgMB\n" ++ "AAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW\n" ++ "BBRF66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYun\n" ++ "pyGd823IDzANBgkqhkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRC\n" ++ "dWKuh+vy1dneVrOfzM4UKLkNl2BcEkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTf\n" ++ "fwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38FnSbNd67IJKusm7Xi+fT8r87cm\n" ++ "NW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i8b5QZ7dsvfPx\n" ++ "H2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe\n" ++ "+o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g==\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBl\n" ++ "MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n" ++ "d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv\n" ++ "b3QgRzIwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQG\n" ++ "EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl\n" ++ "cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIwggEi\n" ++ "MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ5ygvUj82ckmIkzTz+GoeMVSA\n" ++ "n61UQbVH35ao1K+ALbkKz3X9iaV9JPrjIgwrvJUXCzO/GU1BBpAAvQxNEP4Htecc\n" ++ "biJVMWWXvdMX0h5i89vqbFCMP4QMls+3ywPgym2hFEwbid3tALBSfK+RbLE4E9Hp\n" ++ "EgjAALAcKxHad3A2m67OeYfcgnDmCXRwVWmvo2ifv922ebPynXApVfSr/5Vh88lA\n" ++ "bx3RvpO704gqu52/clpWcTs/1PPRCv4o76Pu2ZmvA9OPYLfykqGxvYmJHzDNw6Yu\n" ++ "YjOuFgJ3RFrngQo8p0Quebg/BLxcoIfhG69Rjs3sLPr4/m3wOnyqi+RnlTGNAgMB\n" ++ "AAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQW\n" ++ "BBTOw0q5mVXyuNtgv6l+vVa1lzan1jANBgkqhkiG9w0BAQsFAAOCAQEAyqVVjOPI\n" ++ "QW5pJ6d1Ee88hjZv0p3GeDgdaZaikmkuOGybfQTUiaWxMTeKySHMq2zNixya1r9I\n" ++ "0jJmwYrA8y8678Dj1JGG0VDjA9tzd29KOVPt3ibHtX2vK0LRdWLjSisCx1BL4Gni\n" ++ "lmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAWhsI6yLETcDbYz+70CjTVW0z9\n" ++ "B5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0MjomZmWzwPDCv\n" ++ "ON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwo\n" ++ "IhNzbM8m9Yop5w==\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQsw\n" ++ "CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu\n" ++ "ZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3Qg\n" ++ "RzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQGEwJV\n" ++ "UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu\n" ++ "Y29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQBgcq\n" ++ "hkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJf\n" ++ "Zn4f5dwbRXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17Q\n" ++ "RSAPWXYQ1qAk8C3eNvJsKTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/\n" ++ "BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgFUaFNN6KDec6NHSrkhDAKBggqhkjOPQQD\n" ++ "AwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5FyYZ5eEJJZVrmDxxDnOOlY\n" ++ "JjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy1vUhZscv\n" ++ "6pZjamVFkpUBtA==\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh\n" ++ "MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n" ++ "d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD\n" ++ "QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT\n" ++ "MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j\n" ++ "b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG\n" ++ "9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB\n" ++ "CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97\n" ++ "nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt\n" ++ "43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P\n" ++ "T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4\n" ++ "gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO\n" ++ "BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR\n" ++ "TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw\n" ++ "DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr\n" ++ "hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg\n" ++ "06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF\n" ++ "PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls\n" ++ "YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk\n" ++ "CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh\n" ++ "MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n" ++ "d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH\n" ++ "MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT\n" ++ "MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j\n" ++ "b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG\n" ++ "9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI\n" ++ "2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx\n" ++ "1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ\n" ++ "q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz\n" ++ "tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ\n" ++ "vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP\n" ++ "BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV\n" ++ "5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY\n" ++ "1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4\n" ++ "NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG\n" ++ "Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91\n" ++ "8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe\n" ++ "pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl\n" ++ "MrY=\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQsw\n" ++ "CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu\n" ++ "ZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAe\n" ++ "Fw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVTMRUw\n" ++ "EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x\n" ++ "IDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0CAQYF\n" ++ "K4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FG\n" ++ "fp4tn+6OYwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPO\n" ++ "Z9wj/wMco+I+o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAd\n" ++ "BgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNpYim8S8YwCgYIKoZIzj0EAwMDaAAwZQIx\n" ++ "AK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y3maTD/HMsQmP3Wyr+mt/\n" ++ "oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34VOKa5Vt8\n" ++ "sycX\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs\n" ++ "MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n" ++ "d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j\n" ++ "ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL\n" ++ "MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3\n" ++ "LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug\n" ++ "RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm\n" ++ "+9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW\n" ++ "PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM\n" ++ "xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB\n" ++ "Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3\n" ++ "hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg\n" ++ "EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF\n" ++ "MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA\n" ++ "FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec\n" ++ "nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z\n" ++ "eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF\n" ++ "hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2\n" ++ "Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe\n" ++ "vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep\n" ++ "+OkuE6N36B9K\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIICGTCCAZ+gAwIBAgIQCeCTZaz32ci5PhwLBCou8zAKBggqhkjOPQQDAzBOMQsw\n" ++ "CQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJjAkBgNVBAMTHURp\n" ++ "Z2lDZXJ0IFRMUyBFQ0MgUDM4NCBSb290IEc1MB4XDTIxMDExNTAwMDAwMFoXDTQ2\n" ++ "MDExNDIzNTk1OVowTjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJ\n" ++ "bmMuMSYwJAYDVQQDEx1EaWdpQ2VydCBUTFMgRUNDIFAzODQgUm9vdCBHNTB2MBAG\n" ++ "ByqGSM49AgEGBSuBBAAiA2IABMFEoc8Rl1Ca3iOCNQfN0MsYndLxf3c1TzvdlHJS\n" ++ "7cI7+Oz6e2tYIOyZrsn8aLN1udsJ7MgT9U7GCh1mMEy7H0cKPGEQQil8pQgO4CLp\n" ++ "0zVozptjn4S1mU1YoI71VOeVyaNCMEAwHQYDVR0OBBYEFMFRRVBZqz7nLFr6ICIS\n" ++ "B4CIfBFqMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49\n" ++ "BAMDA2gAMGUCMQCJao1H5+z8blUD2WdsJk6Dxv3J+ysTvLd6jLRl0mlpYxNjOyZQ\n" ++ "LgGheQaRnUi/wr4CMEfDFXuxoJGZSZOoPHzoRgaLLPIxAJSdYsiJvRmEFOml+wG4\n" ++ "DXZDjC5Ty3zfDBeWUA==\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFZjCCA06gAwIBAgIQCPm0eKj6ftpqMzeJ3nzPijANBgkqhkiG9w0BAQwFADBN\n" ++ "MQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJTAjBgNVBAMT\n" ++ "HERpZ2lDZXJ0IFRMUyBSU0E0MDk2IFJvb3QgRzUwHhcNMjEwMTE1MDAwMDAwWhcN\n" ++ "NDYwMTE0MjM1OTU5WjBNMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQs\n" ++ "IEluYy4xJTAjBgNVBAMTHERpZ2lDZXJ0IFRMUyBSU0E0MDk2IFJvb3QgRzUwggIi\n" ++ "MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCz0PTJeRGd/fxmgefM1eS87IE+\n" ++ "ajWOLrfn3q/5B03PMJ3qCQuZvWxX2hhKuHisOjmopkisLnLlvevxGs3npAOpPxG0\n" ++ "2C+JFvuUAT27L/gTBaF4HI4o4EXgg/RZG5Wzrn4DReW+wkL+7vI8toUTmDKdFqgp\n" ++ "wgscONyfMXdcvyej/Cestyu9dJsXLfKB2l2w4SMXPohKEiPQ6s+d3gMXsUJKoBZM\n" ++ "pG2T6T867jp8nVid9E6P/DsjyG244gXazOvswzH016cpVIDPRFtMbzCe88zdH5RD\n" ++ "nU1/cHAN1DrRN/BsnZvAFJNY781BOHW8EwOVfH/jXOnVDdXifBBiqmvwPXbzP6Po\n" ++ "sMH976pXTayGpxi0KcEsDr9kvimM2AItzVwv8n/vFfQMFawKsPHTDU9qTXeXAaDx\n" ++ "Zre3zu/O7Oyldcqs4+Fj97ihBMi8ez9dLRYiVu1ISf6nL3kwJZu6ay0/nTvEF+cd\n" ++ "Lvvyz6b84xQslpghjLSR6Rlgg/IwKwZzUNWYOwbpx4oMYIwo+FKbbuH2TbsGJJvX\n" ++ "KyY//SovcfXWJL5/MZ4PbeiPT02jP/816t9JXkGPhvnxd3lLG7SjXi/7RgLQZhNe\n" ++ "XoVPzthwiHvOAbWWl9fNff2C+MIkwcoBOU+NosEUQB+cZtUMCUbW8tDRSHZWOkPL\n" ++ "tgoRObqME2wGtZ7P6wIDAQABo0IwQDAdBgNVHQ4EFgQUUTMc7TZArxfTJc1paPKv\n" ++ "TiM+s0EwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcN\n" ++ "AQEMBQADggIBAGCmr1tfV9qJ20tQqcQjNSH/0GEwhJG3PxDPJY7Jv0Y02cEhJhxw\n" ++ "GXIeo8mH/qlDZJY6yFMECrZBu8RHANmfGBg7sg7zNOok992vIGCukihfNudd5N7H\n" ++ "PNtQOa27PShNlnx2xlv0wdsUpasZYgcYQF+Xkdycx6u1UQ3maVNVzDl92sURVXLF\n" ++ "O4uJ+DQtpBflF+aZfTCIITfNMBc9uPK8qHWgQ9w+iUuQrm0D4ByjoJYJu32jtyoQ\n" ++ "REtGBzRj7TG5BO6jm5qu5jF49OokYTurWGT/u4cnYiWB39yhL/btp/96j1EuMPik\n" ++ "AdKFOV8BmZZvWltwGUb+hmA+rYAQCd05JS9Yf7vSdPD3Rh9GOUrYU9DzLjtxpdRv\n" ++ "/PNn5AeP3SYZ4Y1b+qOTEZvpyDrDVWiakuFSdjjo4bq9+0/V77PnSIMx8IIh47a+\n" ++ "p6tv75/fTM8BuGJqIz3nCU2AG3swpMPdB380vqQmsvZB6Akd4yCYqjdP//fx4ilw\n" ++ "MUc/dNAUFvohigLVigmUdy7yWSiLfFCSCmZ4OIN1xLVaqBHG5cGdZlXPU8Sv13WF\n" ++ "qUITVuwhd4GTWgzqltlJyqEI8pc7bZsEGCREjnwB8twl2F6GmrE52/WRMmrRpnCK\n" ++ "ovfepEWFJqgejF0pW8hL2JpqA15w8oVPbEtoL8pU9ozaMv7Da4M/OMZ+\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBi\n" ++ "MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n" ++ "d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3Qg\n" ++ "RzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBiMQswCQYDVQQGEwJV\n" ++ "UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu\n" ++ "Y29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0GCSqG\n" ++ "SIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3y\n" ++ "ithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1If\n" ++ "xp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDV\n" ++ "ySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfISKhmV1efVFiO\n" ++ "DCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jHtrHEtWoYOAMQ\n" ++ "jdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6MUSaM0C/\n" ++ "CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCi\n" ++ "EhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADM\n" ++ "fRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QY\n" ++ "uKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXK\n" ++ "chYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4KJpn15GkvmB0t\n" ++ "9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB\n" ++ "hjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD\n" ++ "ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2\n" ++ "SV1EY+CtnJYYZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd\n" ++ "+SeuMIW59mdNOj6PWTkiU0TryF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWc\n" ++ "fFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy7zBZLq7gcfJW5GqXb5JQbZaNaHqa\n" ++ "sjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iahixTXTBmyUEFxPT9N\n" ++ "cCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN5r5N\n" ++ "0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie\n" ++ "4u1Ki7wb/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mI\n" ++ "r/OSmbaz5mEP0oUA51Aa5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1\n" ++ "/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tKG48BtieVU+i2iW1bvGjUI+iLUaJW+fCm\n" ++ "gKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP82Z+\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChML\n" ++ "RW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBp\n" ++ "bmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5\n" ++ "IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENlcnRp\n" ++ "ZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQxNzUwNTFaFw0yOTA3\n" ++ "MjQxNDE1MTJaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3d3d3\n" ++ "LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxp\n" ++ "YWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEG\n" ++ "A1UEAxMqRW50cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgp\n" ++ "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArU1LqRKGsuqjIAcVFmQq\n" ++ "K0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOLGp18EzoOH1u3Hs/lJBQe\n" ++ "sYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSrhRSGlVuX\n" ++ "MlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVT\n" ++ "XTzWnLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/\n" ++ "HoZdenoVve8AjhUiVBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH\n" ++ "4QIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV\n" ++ "HQ4EFgQUVeSB0RGAvtiJuQijMfmhJAkWuXAwDQYJKoZIhvcNAQEFBQADggEBADub\n" ++ "j1abMOdTmXx6eadNl9cZlZD7Bh/KM3xGY4+WZiT6QBshJ8rmcnPyT/4xmf3IDExo\n" ++ "U8aAghOY+rat2l098c5u9hURlIIM7j+VrxGrD9cv3h8Dj1csHsm7mhpElesYT6Yf\n" ++ "zX1XEC+bBAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5b\n" ++ "u/8j72gZyxKTJ1wDLW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+\n" ++ "bYQLCIt+jerXmCHG8+c8eS9enNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/Er\n" ++ "fF6adulZkMV8gzURZVE=\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMC\n" ++ "VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0\n" ++ "Lm5ldC9DUFMgaXMgaW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMW\n" ++ "KGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsGA1UEAxMkRW50cnVzdCBSb290IENl\n" ++ "cnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0MloXDTI2MTEyNzIw\n" ++ "NTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMTkw\n" ++ "NwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSBy\n" ++ "ZWZlcmVuY2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNV\n" ++ "BAMTJEVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJ\n" ++ "KoZIhvcNAQEBBQADggEPADCCAQoCggEBALaVtkNC+sZtKm9I35RMOVcF7sN5EUFo\n" ++ "Nu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYszA9u3g3s+IIRe7bJWKKf4\n" ++ "4LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOwwCj0Yzfv9\n" ++ "KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGI\n" ++ "rb68j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi\n" ++ "94DkZfs0Nw4pgHBNrziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOB\n" ++ "sDCBrTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAi\n" ++ "gA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1MzQyWjAfBgNVHSMEGDAWgBRo\n" ++ "kORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DHhmak8fdLQ/uE\n" ++ "vW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA\n" ++ "A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9t\n" ++ "O1KzKtvn1ISMY/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6Zua\n" ++ "AGAT/3B+XxFNSRuzFVJ7yVTav52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP\n" ++ "9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTSW3iDVuycNsMm4hH2Z0kdkquM++v/\n" ++ "eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0tHuu2guQOHXvgR1m\n" ++ "0vdXcDazv/wor3ElhVsT/h5/WrQ8\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIC+TCCAoCgAwIBAgINAKaLeSkAAAAAUNCR+TAKBggqhkjOPQQDAzCBvzELMAkG\n" ++ "A1UEBhMCVVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3\n" ++ "d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVu\n" ++ "dHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEzMDEGA1UEAxMq\n" ++ "RW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRUMxMB4XDTEy\n" ++ "MTIxODE1MjUzNloXDTM3MTIxODE1NTUzNlowgb8xCzAJBgNVBAYTAlVTMRYwFAYD\n" ++ "VQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0\n" ++ "L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJbmMuIC0g\n" ++ "Zm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMzAxBgNVBAMTKkVudHJ1c3QgUm9vdCBD\n" ++ "ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEVDMTB2MBAGByqGSM49AgEGBSuBBAAi\n" ++ "A2IABIQTydC6bUF74mzQ61VfZgIaJPRbiWlH47jCffHyAsWfoPZb1YsGGYZPUxBt\n" ++ "ByQnoaD41UcZYUx9ypMn6nQM72+WCf5j7HBdNq1nd67JnXxVRDqiY1Ef9eNi1KlH\n" ++ "Bz7MIKNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O\n" ++ "BBYEFLdj5xrdjekIplWDpOBqUEFlEUJJMAoGCCqGSM49BAMDA2cAMGQCMGF52OVC\n" ++ "R98crlOZF7ZvHH3hvxGU0QOIdeSNiaSKd0bebWHvAvX7td/M/k7//qnmpwIwW5nX\n" ++ "hTcGtXsI/esni0qU+eH6p44mCOh8kmhtc9hvJqwhAriZtyZBWyVgrtBIGu4G\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMC\n" ++ "VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50\n" ++ "cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3Qs\n" ++ "IEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVz\n" ++ "dCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwHhcNMDkwNzA3MTcy\n" ++ "NTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUVu\n" ++ "dHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwt\n" ++ "dGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0\n" ++ "aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmlj\n" ++ "YXRpb24gQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK\n" ++ "AoIBAQC6hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP/vaCeb9zYQYKpSfYs1/T\n" ++ "RU4cctZOMvJyig/3gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXzHHfV1IWN\n" ++ "cCG0szLni6LVhjkCsbjSR87kyUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hW\n" ++ "wcKUs/Ja5CeanyTXxuzQmyWC48zCxEXFjJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1\n" ++ "U1+cPvQXLOZprE4yTGJ36rfo5bs0vBmLrpxR57d+tVOxMyLlbc9wPBr64ptntoP0\n" ++ "jaWvYkxN4FisZDQSA/i2jZRjJKRxAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAP\n" ++ "BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ60B7vfec7aVHUbI2fkBJmqzAN\n" ++ "BgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5ZiXMRrEPR9RP/\n" ++ "jTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZ\n" ++ "Rkfz6/djwUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v\n" ++ "1fN2D807iDginWyTmsQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4R\n" ++ "nAuknZoh8/CbCzB428Hch0P+vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmH\n" ++ "VHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xOe4pIb4tF9g==\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIGSzCCBDOgAwIBAgIRANm1Q3+vqTkPAAAAAFVlrVgwDQYJKoZIhvcNAQELBQAw\n" ++ "gb4xCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQL\n" ++ "Ex9TZWUgd3d3LmVudHJ1c3QubmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykg\n" ++ "MjAxNSBFbnRydXN0LCBJbmMuIC0gZm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMjAw\n" ++ "BgNVBAMTKUVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEc0\n" ++ "MB4XDTE1MDUyNzExMTExNloXDTM3MTIyNzExNDExNlowgb4xCzAJBgNVBAYTAlVT\n" ++ "MRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1\n" ++ "c3QubmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxNSBFbnRydXN0LCBJ\n" ++ "bmMuIC0gZm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMjAwBgNVBAMTKUVudHJ1c3Qg\n" ++ "Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEc0MIICIjANBgkqhkiG9w0B\n" ++ "AQEFAAOCAg8AMIICCgKCAgEAsewsQu7i0TD/pZJH4i3DumSXbcr3DbVZwbPLqGgZ\n" ++ "2K+EbTBwXX7zLtJTmeH+H17ZSK9dE43b/2MzTdMAArzE+NEGCJR5WIoV3imz/f3E\n" ++ "T+iq4qA7ec2/a0My3dl0ELn39GjUu9CH1apLiipvKgS1sqbHoHrmSKvS0VnM1n4j\n" ++ "5pds8ELl3FFLFUHtSUrJ3hCX1nbB76W1NhSXNdh4IjVS70O92yfbYVaCNNzLiGAM\n" ++ "C1rlLAHGVK/XqsEQe9IFWrhAnoanw5CGAlZSCXqc0ieCU0plUmr1POeo8pyvi73T\n" ++ "DtTUXm6Hnmo9RR3RXRv06QqsYJn7ibT/mCzPfB3pAqoEmh643IhuJbNsZvc8kPNX\n" ++ "wbMv9W3y+8qh+CmdRouzavbmZwe+LGcKKh9asj5XxNMhIWNlUpEbsZmOeX7m640A\n" ++ "2Vqq6nPopIICR5b+W45UYaPrL0swsIsjdXJ8ITzI9vF01Bx7owVV7rtNOzK+mndm\n" ++ "nqxpkCIHH2E6lr7lmk/MBTwoWdPBDFSoWWG9yHJM6Nyfh3+9nEg2XpWjDrk4JFX8\n" ++ "dWbrAuMINClKxuMrLzOg2qOGpRKX/YAr2hRC45K9PvJdXmd0LhyIRyk0X+IyqJwl\n" ++ "N4y6mACXi0mWHv0liqzc2thddG5msP9E36EYxr5ILzeUePiVSj9/E15dWf10hkNj\n" ++ "c0kCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD\n" ++ "VR0OBBYEFJ84xFYjwznooHFs6FRM5Og6sb9nMA0GCSqGSIb3DQEBCwUAA4ICAQAS\n" ++ "5UKme4sPDORGpbZgQIeMJX6tuGguW8ZAdjwD+MlZ9POrYs4QjbRaZIxowLByQzTS\n" ++ "Gwv2LFPSypBLhmb8qoMi9IsabyZIrHZ3CL/FmFz0Jomee8O5ZDIBf9PD3Vht7LGr\n" ++ "hFV0d4QEJ1JrhkzO3bll/9bGXp+aEJlLdWr+aumXIOTkdnrG0CSqkM0gkLpHZPt/\n" ++ "B7NTeLUKYvJzQ85BK4FqLoUWlFPUa19yIqtRLULVAJyZv967lDtX/Zr1hstWO1uI\n" ++ "AeV8KEsD+UmDfLJ/fOPtjqF/YFOOVZ1QNBIPt5d7bIdKROf1beyAN/BYGW5KaHbw\n" ++ "H5Lk6rWS02FREAutp9lfx1/cH6NcjKF+m7ee01ZvZl4HliDtC3T7Zk6LERXpgUl+\n" ++ "b7DUUH8i119lAg2m9IUe2K4GS0qn0jFmwvjO5QimpAKWRGhXxNUzzxkvFMSUHHuk\n" ++ "2fCfDrGA4tGeEWSpiBE6doLlYsKA2KSD7ZPvfC+QsDJMlhVoSFLUmQjAJOgc47Ol\n" ++ "IQ6SwJAfzyBfyjs4x7dtOvPmRLgOMWuIjnDrnBdSqEGULoe256YSxXXfW8AKbnuk\n" ++ "5F6G+TaU33fD6Q3AOfF5u0aOq0NZJ7cguyPpVkAh7DE9ZapD8j3fcEThuk0mEDuY\n" ++ "n/PIjhs4ViFqUZPTkcpG2om3PVODLAgfi49T3f+sHw==\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFiDCCA3CgAwIBAgIIfQmX/vBH6nowDQYJKoZIhvcNAQELBQAwYjELMAkGA1UE\n" ++ "BhMCQ04xMjAwBgNVBAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZ\n" ++ "IENPLixMVEQuMR8wHQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMB4XDTE0\n" ++ "MTEyNjA1MTMxNVoXDTQwMTIzMTE1NTk1OVowYjELMAkGA1UEBhMCQ04xMjAwBgNV\n" ++ "BAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZIENPLixMVEQuMR8w\n" ++ "HQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMIICIjANBgkqhkiG9w0BAQEF\n" ++ "AAOCAg8AMIICCgKCAgEA2aMW8Mh0dHeb7zMNOwZ+Vfy1YI92hhJCfVZmPoiC7XJj\n" ++ "Dp6L3TQsAlFRwxn9WVSEyfFrs0yw6ehGXTjGoqcuEVe6ghWinI9tsJlKCvLriXBj\n" ++ "TnnEt1u9ol2x8kECK62pOqPseQrsXzrj/e+APK00mxqriCZ7VqKChh/rNYmDf1+u\n" ++ "KU49tm7srsHwJ5uu4/Ts765/94Y9cnrrpftZTqfrlYwiOXnhLQiPzLyRuEH3FMEj\n" ++ "qcOtmkVEs7LXLM3GKeJQEK5cy4KOFxg2fZfmiJqwTTQJ9Cy5WmYqsBebnh52nUpm\n" ++ "MUHfP/vFBu8btn4aRjb3ZGM74zkYI+dndRTVdVeSN72+ahsmUPI2JgaQxXABZG12\n" ++ "ZuGR224HwGGALrIuL4xwp9E7PLOR5G62xDtw8mySlwnNR30YwPO7ng/Wi64HtloP\n" ++ "zgsMR6flPri9fcebNaBhlzpBdRfMK5Z3KpIhHtmVdiBnaM8Nvd/WHwlqmuLMc3Gk\n" ++ "L30SgLdTMEZeS1SZD2fJpcjyIMGC7J0R38IC+xo70e0gmu9lZJIQDSri3nDxGGeC\n" ++ "jGHeuLzRL5z7D9Ar7Rt2ueQ5Vfj4oR24qoAATILnsn8JuLwwoC8N9VKejveSswoA\n" ++ "HQBUlwbgsQfZxw9cZX08bVlX5O2ljelAU58VS6Bx9hoh49pwBiFYFIeFd3mqgnkC\n" ++ "AwEAAaNCMEAwHQYDVR0OBBYEFOLJQJ9NzuiaoXzPDj9lxSmIahlRMA8GA1UdEwEB\n" ++ "/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQDRSVfg\n" ++ "p8xoWLoBDysZzY2wYUWsEe1jUGn4H3++Fo/9nesLqjJHdtJnJO29fDMylyrHBYZm\n" ++ "DRd9FBUb1Ov9H5r2XpdptxolpAqzkT9fNqyL7FeoPueBihhXOYV0GkLH6VsTX4/5\n" ++ "COmSdI31R9KrO9b7eGZONn356ZLpBN79SWP8bfsUcZNnL0dKt7n/HipzcEYwv1ry\n" ++ "L3ml4Y0M2fmyYzeMN2WFcGpcWwlyua1jPLHd+PwyvzeG5LuOmCd+uh8W4XAR8gPf\n" ++ "JWIyJyYYMoSf/wA6E7qaTfRPuBRwIrHKK5DOKcFw9C+df/KQHtZa37dG/OaG+svg\n" ++ "IHZ6uqbL9XzeYqWxi+7egmaKTjowHz+Ay60nugxe19CxVsp3cbK1daFQqUBDF8Io\n" ++ "2c9Si1vIY9RCPqAzekYu9wogRlR+ak8x8YF+QnQ4ZXMn7sZ8uI7XpTrXmKGcjBBV\n" ++ "09tL7ECQ8s1uV9JiDnxXk7Gnbc2dg7sq5+W2O3FYrf3RRbxake5TFW/TRQl1brqQ\n" ++ "XR4EzzffHqhmsYzmIGrv/EhOdJhCrylvLmrH+33RZjEizIYAfmaDDEL0vTSSwxrq\n" ++ "T8p+ck0LcIymSLumoRT2+1hEmRSuqguTaaApJUqlyyvdimYHFngVV3Eb7PVHhPOe\n" ++ "MTd61X8kreS8/f3MboPoDKi3QWwH3b08hpcv0g==\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFgjCCA2qgAwIBAgILWku9WvtPilv6ZeUwDQYJKoZIhvcNAQELBQAwTTELMAkG\n" ++ "A1UEBhMCQVQxIzAhBgNVBAoTGmUtY29tbWVyY2UgbW9uaXRvcmluZyBHbWJIMRkw\n" ++ "FwYDVQQDExBHTE9CQUxUUlVTVCAyMDIwMB4XDTIwMDIxMDAwMDAwMFoXDTQwMDYx\n" ++ "MDAwMDAwMFowTTELMAkGA1UEBhMCQVQxIzAhBgNVBAoTGmUtY29tbWVyY2UgbW9u\n" ++ "aXRvcmluZyBHbWJIMRkwFwYDVQQDExBHTE9CQUxUUlVTVCAyMDIwMIICIjANBgkq\n" ++ "hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAri5WrRsc7/aVj6B3GyvTY4+ETUWiD59b\n" ++ "RatZe1E0+eyLinjF3WuvvcTfk0Uev5E4C64OFudBc/jbu9G4UeDLgztzOG53ig9Z\n" ++ "YybNpyrOVPu44sB8R85gfD+yc/LAGbaKkoc1DZAoouQVBGM+uq/ufF7MpotQsjj3\n" ++ "QWPKzv9pj2gOlTblzLmMCcpL3TGQlsjMH/1WljTbjhzqLL6FLmPdqqmV0/0plRPw\n" ++ "yJiT2S0WR5ARg6I6IqIoV6Lr/sCMKKCmfecqQjuCgGOlYx8ZzHyyZqjC0203b+J+\n" ++ "BlHZRYQfEs4kUmSFC0iAToexIiIwquuuvuAC4EDosEKAA1GqtH6qRNdDYfOiaxaJ\n" ++ "SaSjpCuKAsR49GiKweR6NrFvG5Ybd0mN1MkGco/PU+PcF4UgStyYJ9ORJitHHmkH\n" ++ "r96i5OTUawuzXnzUJIBHKWk7buis/UDr2O1xcSvy6Fgd60GXIsUf1DnQJ4+H4xj0\n" ++ "4KlGDfV0OoIu0G4skaMxXDtG6nsEEFZegB31pWXogvziB4xiRfUg3kZwhqG8k9Me\n" ++ "dKZssCz3AwyIDMvUclOGvGBG85hqwvG/Q/lwIHfKN0F5VVJjjVsSn8VoxIidrPIw\n" ++ "q7ejMZdnrY8XD2zHc+0klGvIg5rQmjdJBKuxFshsSUktq6HQjJLyQUp5ISXbY9e2\n" ++ "nKd+Qmn7OmMCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC\n" ++ "AQYwHQYDVR0OBBYEFNwuH9FhN3nkq9XVsxJxaD1qaJwiMB8GA1UdIwQYMBaAFNwu\n" ++ "H9FhN3nkq9XVsxJxaD1qaJwiMA0GCSqGSIb3DQEBCwUAA4ICAQCR8EICaEDuw2jA\n" ++ "VC/f7GLDw56KoDEoqoOOpFaWEhCGVrqXctJUMHytGdUdaG/7FELYjQ7ztdGl4wJC\n" ++ "XtzoRlgHNQIw4Lx0SsFDKv/bGtCwr2zD/cuz9X9tAy5ZVp0tLTWMstZDFyySCstd\n" ++ "6IwPS3BD0IL/qMy/pJTAvoe9iuOTe8aPmxadJ2W8esVCgmxcB9CpwYhgROmYhRZf\n" ++ "+I/KARDOJcP5YBugxZfD0yyIMaK9MOzQ0MAS8cE54+X1+NZK3TTN+2/BT+MAi1bi\n" ++ "kvcoskJ3ciNnxz8RFbLEAwW+uxF7Cr+obuf/WEPPm2eggAe2HcqtbepBEX4tdJP7\n" ++ "wry+UUTF72glJ4DjyKDUEuzZpTcdN3y0kcra1LGWge9oXHYQSa9+pTeAsRxSvTOB\n" ++ "TI/53WXZFM2KJVj04sWDpQmQ1GwUY7VA3+vA/MRYfg0UFodUJ25W5HCEuGwyEn6C\n" ++ "MUO+1918oa2u1qsgEu8KwxCMSZY13At1XrFP1U80DhEgB3VDRemjEdqso5nCtnkn\n" ++ "4rnvyOL2NSl6dPrFf4IFYqYK6miyeUcGbvJXqBUzxvd4Sj1Ce2t+/vdG6tHrju+I\n" ++ "aFvowdlxfv1k7/9nR4hYJS8+hge9+6jlgqispdNpQ80xiEmEU5LAsTkbOYMBMMTy\n" ++ "qfrQA71yN2BWHzZ8vTmR9W0Nv3vXkg==\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFVzCCAz+gAwIBAgINAgPlk28xsBNJiGuiFzANBgkqhkiG9w0BAQwFADBHMQsw\n" ++ "CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU\n" ++ "MBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw\n" ++ "MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp\n" ++ "Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUA\n" ++ "A4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaMf/vo\n" ++ "27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vXmX7w\n" ++ "Cl7raKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7zUjw\n" ++ "TcLCeoiKu7rPWRnWr4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0Pfybl\n" ++ "qAj+lug8aJRT7oM6iCsVlgmy4HqMLnXWnOunVmSPlk9orj2XwoSPwLxAwAtcvfaH\n" ++ "szVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk9+aCEI3oncKKiPo4Zor8\n" ++ "Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zqkUspzBmk\n" ++ "MiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOORc92\n" ++ "wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYWk70p\n" ++ "aDPvOmbsB4om3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+DVrN\n" ++ "VjzRlwW5y0vtOUucxD/SVRNuJLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgFlQID\n" ++ "AQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E\n" ++ "FgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQADggIBAJ+qQibb\n" ++ "C5u+/x6Wki4+omVKapi6Ist9wTrYggoGxval3sBOh2Z5ofmmWJyq+bXmYOfg6LEe\n" ++ "QkEzCzc9zolwFcq1JKjPa7XSQCGYzyI0zzvFIoTgxQ6KfF2I5DUkzps+GlQebtuy\n" ++ "h6f88/qBVRRiClmpIgUxPoLW7ttXNLwzldMXG+gnoot7TiYaelpkttGsN/H9oPM4\n" ++ "7HLwEXWdyzRSjeZ2axfG34arJ45JK3VmgRAhpuo+9K4l/3wV3s6MJT/KYnAK9y8J\n" ++ "ZgfIPxz88NtFMN9iiMG1D53Dn0reWVlHxYciNuaCp+0KueIHoI17eko8cdLiA6Ef\n" ++ "MgfdG+RCzgwARWGAtQsgWSl4vflVy2PFPEz0tv/bal8xa5meLMFrUKTX5hgUvYU/\n" ++ "Z6tGn6D/Qqc6f1zLXbBwHSs09dR2CQzreExZBfMzQsNhFRAbd03OIozUhfJFfbdT\n" ++ "6u9AWpQKXCBfTkBdYiJ23//OYb2MI3jSNwLgjt7RETeJ9r/tSQdirpLsQBqvFAnZ\n" ++ "0E6yove+7u7Y/9waLd64NnHi/Hm3lCXRSHNboTXns5lndcEZOitHTtNCjv0xyBZm\n" ++ "2tIMPNuzjsmhDYAPexZ3FL//2wmUspO8IFgV6dtxQ/PeEMMA3KgqlbbC1j+Qa3bb\n" ++ "bP6MvPJwNQzcmRk13NfIRmPVNnGuV/u3gm3c\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFVzCCAz+gAwIBAgINAgPlrsWNBCUaqxElqjANBgkqhkiG9w0BAQwFADBHMQsw\n" ++ "CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU\n" ++ "MBIGA1UEAxMLR1RTIFJvb3QgUjIwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw\n" ++ "MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp\n" ++ "Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEBAQUA\n" ++ "A4ICDwAwggIKAoICAQDO3v2m++zsFDQ8BwZabFn3GTXd98GdVarTzTukk3LvCvpt\n" ++ "nfbwhYBboUhSnznFt+4orO/LdmgUud+tAWyZH8QiHZ/+cnfgLFuv5AS/T3KgGjSY\n" ++ "6Dlo7JUle3ah5mm5hRm9iYz+re026nO8/4Piy33B0s5Ks40FnotJk9/BW9BuXvAu\n" ++ "MC6C/Pq8tBcKSOWIm8Wba96wyrQD8Nr0kLhlZPdcTK3ofmZemde4wj7I0BOdre7k\n" ++ "RXuJVfeKH2JShBKzwkCX44ofR5GmdFrS+LFjKBC4swm4VndAoiaYecb+3yXuPuWg\n" ++ "f9RhD1FLPD+M2uFwdNjCaKH5wQzpoeJ/u1U8dgbuak7MkogwTZq9TwtImoS1mKPV\n" ++ "+3PBV2HdKFZ1E66HjucMUQkQdYhMvI35ezzUIkgfKtzra7tEscszcTJGr61K8Yzo\n" ++ "dDqs5xoic4DSMPclQsciOzsSrZYuxsN2B6ogtzVJV+mSSeh2FnIxZyuWfoqjx5RW\n" ++ "Ir9qS34BIbIjMt/kmkRtWVtd9QCgHJvGeJeNkP+byKq0rxFROV7Z+2et1VsRnTKa\n" ++ "G73VululycslaVNVJ1zgyjbLiGH7HrfQy+4W+9OmTN6SpdTi3/UGVN4unUu0kzCq\n" ++ "gc7dGtxRcw1PcOnlthYhGXmy5okLdWTK1au8CcEYof/UVKGFPP0UJAOyh9OktwID\n" ++ "AQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E\n" ++ "FgQUu//KjiOfT5nK2+JopqUVJxce2Q4wDQYJKoZIhvcNAQEMBQADggIBAB/Kzt3H\n" ++ "vqGf2SdMC9wXmBFqiN495nFWcrKeGk6c1SuYJF2ba3uwM4IJvd8lRuqYnrYb/oM8\n" ++ "0mJhwQTtzuDFycgTE1XnqGOtjHsB/ncw4c5omwX4Eu55MaBBRTUoCnGkJE+M3DyC\n" ++ "B19m3H0Q/gxhswWV7uGugQ+o+MePTagjAiZrHYNSVc61LwDKgEDg4XSsYPWHgJ2u\n" ++ "NmSRXbBoGOqKYcl3qJfEycel/FVL8/B/uWU9J2jQzGv6U53hkRrJXRqWbTKH7QMg\n" ++ "yALOWr7Z6v2yTcQvG99fevX4i8buMTolUVVnjWQye+mew4K6Ki3pHrTgSAai/Gev\n" ++ "HyICc/sgCq+dVEuhzf9gR7A/Xe8bVr2XIZYtCtFenTgCR2y59PYjJbigapordwj6\n" ++ "xLEokCZYCDzifqrXPW+6MYgKBesntaFJ7qBFVHvmJ2WZICGoo7z7GJa7Um8M7YNR\n" ++ "TOlZ4iBgxcJlkoKM8xAfDoqXvneCbT+PHV28SSe9zE8P4c52hgQjxcCMElv924Sg\n" ++ "JPFI/2R80L5cFtHvma3AH/vLrrw4IgYmZNralw4/KBVEqE8AyvCazM90arQ+POuV\n" ++ "7LXTWtiBmelDGDfrs7vRWGJB82bSj6p4lVQgw1oudCvV0b4YacCs1aTPObpRhANl\n" ++ "6WLAYv7YTVWW4tAR+kg0Eeye7QUd5MjWHYbL\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIICCTCCAY6gAwIBAgINAgPluILrIPglJ209ZjAKBggqhkjOPQQDAzBHMQswCQYD\n" ++ "VQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIG\n" ++ "A1UEAxMLR1RTIFJvb3QgUjMwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAw\n" ++ "WjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2Vz\n" ++ "IExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjMwdjAQBgcqhkjOPQIBBgUrgQQAIgNi\n" ++ "AAQfTzOHMymKoYTey8chWEGJ6ladK0uFxh1MJ7x/JlFyb+Kf1qPKzEUURout736G\n" ++ "jOyxfi//qXGdGIRFBEFVbivqJn+7kAHjSxm65FSWRQmx1WyRRK2EE46ajA2ADDL2\n" ++ "4CejQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW\n" ++ "BBTB8Sa6oC2uhYHP0/EqEr24Cmf9vDAKBggqhkjOPQQDAwNpADBmAjEA9uEglRR7\n" ++ "VKOQFhG/hMjqb2sXnh5GmCCbn9MN2azTL818+FsuVbu/3ZL3pAzcMeGiAjEA/Jdm\n" ++ "ZuVDFhOD3cffL74UOO0BzrEXGhF16b0DjyZ+hOXJYKaV11RZt+cRLInUue4X\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIICCTCCAY6gAwIBAgINAgPlwGjvYxqccpBQUjAKBggqhkjOPQQDAzBHMQswCQYD\n" ++ "VQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIG\n" ++ "A1UEAxMLR1RTIFJvb3QgUjQwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAw\n" ++ "WjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2Vz\n" ++ "IExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQwdjAQBgcqhkjOPQIBBgUrgQQAIgNi\n" ++ "AATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvVYRg1rkDdc/eJkTBa6zzuhXyi\n" ++ "QHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHci8nHc8iMai/lxKvR\n" ++ "HYqjQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW\n" ++ "BBSATNbrdP9JNqPV2Py1PsVq8JQdjDAKBggqhkjOPQQDAwNpADBmAjEA6ED/g94D\n" ++ "9J+uHXqnLrmvT/aDHQ4thQEd0dlq7A/Cr8deVl5c1RxYIigL9zC2L7F8AjEA8GE8\n" ++ "p/SgguMh1YQdc4acLa/KNJvxn7kjNuK8YAOdgLOaVsjh4rsUecrNIdSUtUlD\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIB3DCCAYOgAwIBAgINAgPlfvU/k/2lCSGypjAKBggqhkjOPQQDAjBQMSQwIgYD\n" ++ "VQQLExtHbG9iYWxTaWduIEVDQyBSb290IENBIC0gUjQxEzARBgNVBAoTCkdsb2Jh\n" ++ "bFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMTIxMTEzMDAwMDAwWhcNMzgw\n" ++ "MTE5MDMxNDA3WjBQMSQwIgYDVQQLExtHbG9iYWxTaWduIEVDQyBSb290IENBIC0g\n" ++ "UjQxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wWTAT\n" ++ "BgcqhkjOPQIBBggqhkjOPQMBBwNCAAS4xnnTj2wlDp8uORkcA6SumuU5BwkWymOx\n" ++ "uYb4ilfBV85C+nOh92VC/x7BALJucw7/xyHlGKSq2XE/qNS5zowdo0IwQDAOBgNV\n" ++ "HQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUVLB7rUW44kB/\n" ++ "+wpu+74zyTyjhNUwCgYIKoZIzj0EAwIDRwAwRAIgIk90crlgr/HmnKAWBVBfw147\n" ++ "bmF0774BxL4YSFlhgjICICadVGNA3jdgUM/I2O2dgq43mLyjj0xMqTQrbO/7lZsm\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEk\n" ++ "MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpH\n" ++ "bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX\n" ++ "DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD\n" ++ "QSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu\n" ++ "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6SFkc\n" ++ "8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8ke\n" ++ "hOvRnkmSh5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD\n" ++ "VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYI\n" ++ "KoZIzj0EAwMDaAAwZQIxAOVpEslu28YxuglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg\n" ++ "515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7yFz9SO8NdCKoCOJuxUnO\n" ++ "xwy8p2Fp8fc74SrL+SvzZpA3\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG\n" ++ "A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv\n" ++ "b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw\n" ++ "MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i\n" ++ "YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT\n" ++ "aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ\n" ++ "jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp\n" ++ "xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp\n" ++ "1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG\n" ++ "snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ\n" ++ "U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8\n" ++ "9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E\n" ++ "BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B\n" ++ "AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz\n" ++ "yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE\n" ++ "38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP\n" ++ "AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad\n" ++ "DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME\n" ++ "HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G\n" ++ "A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp\n" ++ "Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4\n" ++ "MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG\n" ++ "A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI\n" ++ "hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8\n" ++ "RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT\n" ++ "gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm\n" ++ "KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd\n" ++ "QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ\n" ++ "XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw\n" ++ "DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o\n" ++ "LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU\n" ++ "RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp\n" ++ "jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK\n" ++ "6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX\n" ++ "mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs\n" ++ "Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH\n" ++ "WD9f\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFgzCCA2ugAwIBAgIORea7A4Mzw4VlSOb/RVEwDQYJKoZIhvcNAQEMBQAwTDEg\n" ++ "MB4GA1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjYxEzARBgNVBAoTCkdsb2Jh\n" ++ "bFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMTQxMjEwMDAwMDAwWhcNMzQx\n" ++ "MjEwMDAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSNjET\n" ++ "MBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCAiIwDQYJ\n" ++ "KoZIhvcNAQEBBQADggIPADCCAgoCggIBAJUH6HPKZvnsFMp7PPcNCPG0RQssgrRI\n" ++ "xutbPK6DuEGSMxSkb3/pKszGsIhrxbaJ0cay/xTOURQh7ErdG1rG1ofuTToVBu1k\n" ++ "ZguSgMpE3nOUTvOniX9PeGMIyBJQbUJmL025eShNUhqKGoC3GYEOfsSKvGRMIRxD\n" ++ "aNc9PIrFsmbVkJq3MQbFvuJtMgamHvm566qjuL++gmNQ0PAYid/kD3n16qIfKtJw\n" ++ "LnvnvJO7bVPiSHyMEAc4/2ayd2F+4OqMPKq0pPbzlUoSB239jLKJz9CgYXfIWHSw\n" ++ "1CM69106yqLbnQneXUQtkPGBzVeS+n68UARjNN9rkxi+azayOeSsJDa38O+2HBNX\n" ++ "k7besvjihbdzorg1qkXy4J02oW9UivFyVm4uiMVRQkQVlO6jxTiWm05OWgtH8wY2\n" ++ "SXcwvHE35absIQh1/OZhFj931dmRl4QKbNQCTXTAFO39OfuD8l4UoQSwC+n+7o/h\n" ++ "bguyCLNhZglqsQY6ZZZZwPA1/cnaKI0aEYdwgQqomnUdnjqGBQCe24DWJfncBZ4n\n" ++ "WUx2OVvq+aWh2IMP0f/fMBH5hc8zSPXKbWQULHpYT9NLCEnFlWQaYw55PfWzjMpY\n" ++ "rZxCRXluDocZXFSxZba/jJvcE+kNb7gu3GduyYsRtYQUigAZcIN5kZeR1Bonvzce\n" ++ "MgfYFGM8KEyvAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTAD\n" ++ "AQH/MB0GA1UdDgQWBBSubAWjkxPioufi1xzWx/B/yGdToDAfBgNVHSMEGDAWgBSu\n" ++ "bAWjkxPioufi1xzWx/B/yGdToDANBgkqhkiG9w0BAQwFAAOCAgEAgyXt6NH9lVLN\n" ++ "nsAEoJFp5lzQhN7craJP6Ed41mWYqVuoPId8AorRbrcWc+ZfwFSY1XS+wc3iEZGt\n" ++ "Ixg93eFyRJa0lV7Ae46ZeBZDE1ZXs6KzO7V33EByrKPrmzU+sQghoefEQzd5Mr61\n" ++ "55wsTLxDKZmOMNOsIeDjHfrYBzN2VAAiKrlNIC5waNrlU/yDXNOd8v9EDERm8tLj\n" ++ "vUYAGm0CuiVdjaExUd1URhxN25mW7xocBFymFe944Hn+Xds+qkxV/ZoVqW/hpvvf\n" ++ "cDDpw+5CRu3CkwWJ+n1jez/QcYF8AOiYrg54NMMl+68KnyBr3TsTjxKM4kEaSHpz\n" ++ "oHdpx7Zcf4LIHv5YGygrqGytXm3ABdJ7t+uA/iU3/gKbaKxCXcPu9czc8FB10jZp\n" ++ "nOZ7BN9uBmm23goJSFmH63sUYHpkqmlD75HHTOwY3WzvUy2MmeFe8nI+z1TIvWfs\n" ++ "pA9MRf/TuTAjB0yPEL+GltmZWrSZVxykzLsViVO6LAUP5MSeGbEYNNVMnbrt9x+v\n" ++ "JJUEeKgDu+6B5dpffItKoZB0JaezPkvILFa9x8jvOOJckvB595yEunQtYQEgfn7R\n" ++ "8k8HWV+LLUNS60YMlOH1Zkd5d9VUWx+tJDfLRVpOoERIyNiwmcUVhAn21klJwGW4\n" ++ "5hpxbqCo8YLoRT5s1gLXCmeDBVrJpBA=\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIICCzCCAZGgAwIBAgISEdK7ujNu1LzmJGjFDYQdmOhDMAoGCCqGSM49BAMDMEYx\n" ++ "CzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYDVQQD\n" ++ "ExNHbG9iYWxTaWduIFJvb3QgRTQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMyMDAw\n" ++ "MDAwMFowRjELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2Ex\n" ++ "HDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBFNDYwdjAQBgcqhkjOPQIBBgUrgQQA\n" ++ "IgNiAAScDrHPt+ieUnd1NPqlRqetMhkytAepJ8qUuwzSChDH2omwlwxwEwkBjtjq\n" ++ "R+q+soArzfwoDdusvKSGN+1wCAB16pMLey5SnCNoIwZD7JIvU4Tb+0cUB+hflGdd\n" ++ "yXqBPCCjQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud\n" ++ "DgQWBBQxCpCPtsad0kRLgLWi5h+xEk8blTAKBggqhkjOPQQDAwNoADBlAjEA31SQ\n" ++ "7Zvvi5QCkxeCmb6zniz2C5GMn0oUsfZkvLtoURMMA/cVi4RguYv/Uo7njLwcAjA8\n" ++ "+RHUjE7AwWHCFUyqqx0LMV87HOIAl0Qx5v5zli/altP+CAezNIm8BZ/3Hobui3A=\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFWjCCA0KgAwIBAgISEdK7udcjGJ5AXwqdLdDfJWfRMA0GCSqGSIb3DQEBDAUA\n" ++ "MEYxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYD\n" ++ "VQQDExNHbG9iYWxTaWduIFJvb3QgUjQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMy\n" ++ "MDAwMDAwMFowRjELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYt\n" ++ "c2ExHDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBSNDYwggIiMA0GCSqGSIb3DQEB\n" ++ "AQUAA4ICDwAwggIKAoICAQCsrHQy6LNl5brtQyYdpokNRbopiLKkHWPd08EsCVeJ\n" ++ "OaFV6Wc0dwxu5FUdUiXSE2te4R2pt32JMl8Nnp8semNgQB+msLZ4j5lUlghYruQG\n" ++ "vGIFAha/r6gjA7aUD7xubMLL1aa7DOn2wQL7Id5m3RerdELv8HQvJfTqa1VbkNud\n" ++ "316HCkD7rRlr+/fKYIje2sGP1q7Vf9Q8g+7XFkyDRTNrJ9CG0Bwta/OrffGFqfUo\n" ++ "0q3v84RLHIf8E6M6cqJaESvWJ3En7YEtbWaBkoe0G1h6zD8K+kZPTXhc+CtI4wSE\n" ++ "y132tGqzZfxCnlEmIyDLPRT5ge1lFgBPGmSXZgjPjHvjK8Cd+RTyG/FWaha/LIWF\n" ++ "zXg4mutCagI0GIMXTpRW+LaCtfOW3T3zvn8gdz57GSNrLNRyc0NXfeD412lPFzYE\n" ++ "+cCQYDdF3uYM2HSNrpyibXRdQr4G9dlkbgIQrImwTDsHTUB+JMWKmIJ5jqSngiCN\n" ++ "I/onccnfxkF0oE32kRbcRoxfKWMxWXEM2G/CtjJ9++ZdU6Z+Ffy7dXxd7Pj2Fxzs\n" ++ "x2sZy/N78CsHpdlseVR2bJ0cpm4O6XkMqCNqo98bMDGfsVR7/mrLZqrcZdCinkqa\n" ++ "ByFrgY/bxFn63iLABJzjqls2k+g9vXqhnQt2sQvHnf3PmKgGwvgqo6GDoLclcqUC\n" ++ "4wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV\n" ++ "HQ4EFgQUA1yrc4GHqMywptWU4jaWSf8FmSwwDQYJKoZIhvcNAQEMBQADggIBAHx4\n" ++ "7PYCLLtbfpIrXTncvtgdokIzTfnvpCo7RGkerNlFo048p9gkUbJUHJNOxO97k4Vg\n" ++ "JuoJSOD1u8fpaNK7ajFxzHmuEajwmf3lH7wvqMxX63bEIaZHU1VNaL8FpO7XJqti\n" ++ "2kM3S+LGteWygxk6x9PbTZ4IevPuzz5i+6zoYMzRx6Fcg0XERczzF2sUyQQCPtIk\n" ++ "pnnpHs6i58FZFZ8d4kuaPp92CC1r2LpXFNqD6v6MVenQTqnMdzGxRBF6XLE+0xRF\n" ++ "FRhiJBPSy03OXIPBNvIQtQ6IbbjhVp+J3pZmOUdkLG5NrmJ7v2B0GbhWrJKsFjLt\n" ++ "rWhV/pi60zTe9Mlhww6G9kuEYO4Ne7UyWHmRVSyBQ7N0H3qqJZ4d16GLuc1CLgSk\n" ++ "ZoNNiTW2bKg2SnkheCLQQrzRQDGQob4Ez8pn7fXwgNNgyYMqIgXQBztSvwyeqiv5\n" ++ "u+YfjyW6hY0XHgL+XVAEV8/+LbzvXMAaq7afJMbfc2hIkCwU9D9SGuTSyxTDYWnP\n" ++ "4vkYxboznxSjBF25cfe1lNj2M8FawTSLfJvdkzrnE6JwYZ+vj+vYxXX4M2bUdGc6\n" ++ "N3ec592kD3ZDZopD8p/7DEJ4Y9HiD2971KE9dJeFt0g5QdYg/NA6s/rob8SKunE3\n" ++ "vouXsXgxT7PntgMTzlSdriVZzH81Xwj3QEUxeCp6\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEh\n" ++ "MB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBE\n" ++ "YWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3\n" ++ "MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRo\n" ++ "ZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3Mg\n" ++ "MiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggEN\n" ++ "ADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCA\n" ++ "PVYYYwhv2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6w\n" ++ "wdhFJ2+qN1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXi\n" ++ "EqITLdiOr18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMY\n" ++ "avx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+\n" ++ "YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0OBBYEFNLE\n" ++ "sNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h\n" ++ "/t2oatTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5\n" ++ "IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmlj\n" ++ "YXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD\n" ++ "ggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wimPQoZ+YeAEW5p5JYXMP80kWNy\n" ++ "OO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKtI3lpjbi2Tc7P\n" ++ "TMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ\n" ++ "HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mER\n" ++ "dEr/VxqHD3VILs9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5Cuf\n" ++ "ReYNnyicsbkqWletNw+vHX/bvZ8=\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx\n" ++ "EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoT\n" ++ "EUdvRGFkZHkuY29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRp\n" ++ "ZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIz\n" ++ "NTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQH\n" ++ "EwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8GA1UE\n" ++ "AxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIw\n" ++ "DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKD\n" ++ "E6bFIEMBO4Tx5oVJnyfq9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH\n" ++ "/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD+qK+ihVqf94Lw7YZFAXK6sOoBJQ7Rnwy\n" ++ "DfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutdfMh8+7ArU6SSYmlRJQVh\n" ++ "GkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMlNAJWJwGR\n" ++ "tDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEA\n" ++ "AaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE\n" ++ "FDqahQcQZyi27/a9BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmX\n" ++ "WWcDYfF+OwYxdS2hII5PZYe096acvNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu\n" ++ "9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r5N9ss4UXnT3ZJE95kTXWXwTr\n" ++ "gIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYVN8Gb5DKj7Tjo\n" ++ "2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO\n" ++ "LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI\n" ++ "4uJEvlz36hz1\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIICVDCCAdugAwIBAgIQZ3SdjXfYO2rbIvT/WeK/zjAKBggqhkjOPQQDAzBsMQsw\n" ++ "CQYDVQQGEwJHUjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2Vh\n" ++ "cmNoIEluc3RpdHV0aW9ucyBDQTEkMCIGA1UEAwwbSEFSSUNBIFRMUyBFQ0MgUm9v\n" ++ "dCBDQSAyMDIxMB4XDTIxMDIxOTExMDExMFoXDTQ1MDIxMzExMDEwOVowbDELMAkG\n" ++ "A1UEBhMCR1IxNzA1BgNVBAoMLkhlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJj\n" ++ "aCBJbnN0aXR1dGlvbnMgQ0ExJDAiBgNVBAMMG0hBUklDQSBUTFMgRUNDIFJvb3Qg\n" ++ "Q0EgMjAyMTB2MBAGByqGSM49AgEGBSuBBAAiA2IABDgI/rGgltJ6rK9JOtDA4MM7\n" ++ "KKrxcm1lAEeIhPyaJmuqS7psBAqIXhfyVYf8MLA04jRYVxqEU+kw2anylnTDUR9Y\n" ++ "STHMmE5gEYd103KUkE+bECUqqHgtvpBBWJAVcqeht6NCMEAwDwYDVR0TAQH/BAUw\n" ++ "AwEB/zAdBgNVHQ4EFgQUyRtTgRL+BNUW0aq8mm+3oJUZbsowDgYDVR0PAQH/BAQD\n" ++ "AgGGMAoGCCqGSM49BAMDA2cAMGQCMBHervjcToiwqfAircJRQO9gcS3ujwLEXQNw\n" ++ "SaSS6sUUiHCm0w2wqsosQJz76YJumgIwK0eaB8bRwoF8yguWGEEbo/QwCZ61IygN\n" ++ "nxS2PFOiTAZpffpskcYqSUXm7LcT4Tps\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFpDCCA4ygAwIBAgIQOcqTHO9D88aOk8f0ZIk4fjANBgkqhkiG9w0BAQsFADBs\n" ++ "MQswCQYDVQQGEwJHUjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl\n" ++ "c2VhcmNoIEluc3RpdHV0aW9ucyBDQTEkMCIGA1UEAwwbSEFSSUNBIFRMUyBSU0Eg\n" ++ "Um9vdCBDQSAyMDIxMB4XDTIxMDIxOTEwNTUzOFoXDTQ1MDIxMzEwNTUzN1owbDEL\n" ++ "MAkGA1UEBhMCR1IxNzA1BgNVBAoMLkhlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNl\n" ++ "YXJjaCBJbnN0aXR1dGlvbnMgQ0ExJDAiBgNVBAMMG0hBUklDQSBUTFMgUlNBIFJv\n" ++ "b3QgQ0EgMjAyMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAIvC569l\n" ++ "mwVnlskNJLnQDmT8zuIkGCyEf3dRywQRNrhe7Wlxp57kJQmXZ8FHws+RFjZiPTgE\n" ++ "4VGC/6zStGndLuwRo0Xua2s7TL+MjaQenRG56Tj5eg4MmOIjHdFOY9TnuEFE+2uv\n" ++ "a9of08WRiFukiZLRgeaMOVig1mlDqa2YUlhu2wr7a89o+uOkXjpFc5gH6l8Cct4M\n" ++ "pbOfrqkdtx2z/IpZ525yZa31MJQjB/OCFks1mJxTuy/K5FrZx40d/JiZ+yykgmvw\n" ++ "Kh+OC19xXFyuQnspiYHLA6OZyoieC0AJQTPb5lh6/a6ZcMBaD9YThnEvdmn8kN3b\n" ++ "LW7R8pv1GmuebxWMevBLKKAiOIAkbDakO/IwkfN4E8/BPzWr8R0RI7VDIp4BkrcY\n" ++ "AuUR0YLbFQDMYTfBKnya4dC6s1BG7oKsnTH4+yPiAwBIcKMJJnkVU2DzOFytOOqB\n" ++ "AGMUuTNe3QvboEUHGjMJ+E20pwKmafTCWQWIZYVWrkvL4N48fS0ayOn7H6NhStYq\n" ++ "E613TBoYm5EPWNgGVMWX+Ko/IIqmhaZ39qb8HOLubpQzKoNQhArlT4b4UEV4AIHr\n" ++ "W2jjJo3Me1xR9BQsQL4aYB16cmEdH2MtiKrOokWQCPxrvrNQKlr9qEgYRtaQQJKQ\n" ++ "CoReaDH46+0N0x3GfZkYVVYnZS6NRcUk7M7jAgMBAAGjQjBAMA8GA1UdEwEB/wQF\n" ++ "MAMBAf8wHQYDVR0OBBYEFApII6ZgpJIKM+qTW8VX6iVNvRLuMA4GA1UdDwEB/wQE\n" ++ "AwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAPpBIqm5iFSVmewzVjIuJndftTgfvnNAU\n" ++ "X15QvWiWkKQUEapobQk1OUAJ2vQJLDSle1mESSmXdMgHHkdt8s4cUCbjnj1AUz/3\n" ++ "f5Z2EMVGpdAgS1D0NTsY9FVqQRtHBmg8uwkIYtlfVUKqrFOFrJVWNlar5AWMxaja\n" ++ "H6NpvVMPxP/cyuN+8kyIhkdGGvMA9YCRotxDQpSbIPDRzbLrLFPCU3hKTwSUQZqP\n" ++ "JzLB5UkZv/HywouoCjkxKLR9YjYsTewfM7Z+d21+UPCfDtcRj88YxeMn/ibvBZ3P\n" ++ "zzfF0HvaO7AWhAw6k9a+F9sPPg4ZeAnHqQJyIkv3N3a6dcSFA1pj1bF1BcK5vZSt\n" ++ "jBWZp5N99sXzqnTPBIWUmAD04vnKJGW/4GKvyMX6ssmeVkjaef2WdhW+o45WxLM0\n" ++ "/L5H9MG0qPzVMIho7suuyWPEdr6sOBjhXlzPrjoiUevRi7PzKzMHVIf6tLITe7pT\n" ++ "BGIBnfHAT+7hOtSLIBD6Alfm78ELt5BGnBkpjNxvoEppaZS3JGWg/6w/zgH7IS79\n" ++ "aPib8qXPMThcFarmlwDB31qlpzmq6YR/PFGoOtmUW4y/Twhx5duoXNTSpv4Ao8YW\n" ++ "xw/ogM4cKGR0GQjTQuPOAF1/sdwTsOEFy9EgqoZ0njnnkf3/W9b3raYvAwtt41dU\n" ++ "63ZTGI0RmLo=\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIICwzCCAkqgAwIBAgIBADAKBggqhkjOPQQDAjCBqjELMAkGA1UEBhMCR1IxDzAN\n" ++ "BgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl\n" ++ "c2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxRDBCBgNVBAMTO0hl\n" ++ "bGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgRUNDIFJv\n" ++ "b3RDQSAyMDE1MB4XDTE1MDcwNzEwMzcxMloXDTQwMDYzMDEwMzcxMlowgaoxCzAJ\n" ++ "BgNVBAYTAkdSMQ8wDQYDVQQHEwZBdGhlbnMxRDBCBgNVBAoTO0hlbGxlbmljIEFj\n" ++ "YWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9yaXR5\n" ++ "MUQwQgYDVQQDEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0\n" ++ "dXRpb25zIEVDQyBSb290Q0EgMjAxNTB2MBAGByqGSM49AgEGBSuBBAAiA2IABJKg\n" ++ "QehLgoRc4vgxEZmGZE4JJS+dQS8KrjVPdJWyUWRrjWvmP3CV8AVER6ZyOFB2lQJa\n" ++ "jq4onvktTpnvLEhvTCUp6NFxW98dwXU3tNf6e3pCnGoKVlp8aQuqgAkkbH7BRqNC\n" ++ "MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFLQi\n" ++ "C4KZJAEOnLvkDv2/+5cgk5kqMAoGCCqGSM49BAMCA2cAMGQCMGfOFmI4oqxiRaep\n" ++ "lSTAGiecMjvAwNW6qef4BENThe5SId6d9SWDPp5YSy/XZxMOIQIwBeF1Ad5o7Sof\n" ++ "TUwJCA3sS61kFyjndc5FZXIhF8siQQ6ME5g4mlRtm8rifOoCWCKR\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIGCzCCA/OgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBpjELMAkGA1UEBhMCR1Ix\n" ++ "DzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5k\n" ++ "IFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMT\n" ++ "N0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9v\n" ++ "dENBIDIwMTUwHhcNMTUwNzA3MTAxMTIxWhcNNDAwNjMwMTAxMTIxWjCBpjELMAkG\n" ++ "A1UEBhMCR1IxDzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNh\n" ++ "ZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkx\n" ++ "QDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1\n" ++ "dGlvbnMgUm9vdENBIDIwMTUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC\n" ++ "AQDC+Kk/G4n8PDwEXT2QNrCROnk8ZlrvbTkBSRq0t89/TSNTt5AA4xMqKKYx8ZEA\n" ++ "4yjsriFBzh/a/X0SWwGDD7mwX5nh8hKDgE0GPt+sr+ehiGsxr/CL0BgzuNtFajT0\n" ++ "AoAkKAoCFZVedioNmToUW/bLy1O8E00BiDeUJRtCvCLYjqOWXjrZMts+6PAQZe10\n" ++ "4S+nfK8nNLspfZu2zwnI5dMK/IhlZXQK3HMcXM1AsRzUtoSMTFDPaI6oWa7CJ06C\n" ++ "ojXdFPQf/7J31Ycvqm59JCfnxssm5uX+Zwdj2EUN3TpZZTlYepKZcj2chF6IIbjV\n" ++ "9Cz82XBST3i4vTwri5WY9bPRaM8gFH5MXF/ni+X1NYEZN9cRCLdmvtNKzoNXADrD\n" ++ "gfgXy5I2XdGj2HUb4Ysn6npIQf1FGQatJ5lOwXBH3bWfgVMS5bGMSF0xQxfjjMZ6\n" ++ "Y5ZLKTBOhE5iGV48zpeQpX8B653g+IuJ3SWYPZK2fu/Z8VFRfS0myGlZYeCsargq\n" ++ "NhEEelC9MoS+L9xy1dcdFkfkR2YgP/SWxa+OAXqlD3pk9Q0Yh9muiNX6hME6wGko\n" ++ "LfINaFGq46V3xqSQDqE3izEjR8EJCOtu93ib14L8hCCZSRm2Ekax+0VVFqmjZayc\n" ++ "Bw/qa9wfLgZy7IaIEuQt218FL+TwA9MmM+eAws1CoRc0CwIDAQABo0IwQDAPBgNV\n" ++ "HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUcRVnyMjJvXVd\n" ++ "ctA4GGqd83EkVAswDQYJKoZIhvcNAQELBQADggIBAHW7bVRLqhBYRjTyYtcWNl0I\n" ++ "XtVsyIe9tC5G8jH4fOpCtZMWVdyhDBKg2mF+D1hYc2Ryx+hFjtyp8iY/xnmMsVMI\n" ++ "M4GwVhO+5lFc2JsKT0ucVlMC6U/2DWDqTUJV6HwbISHTGzrMd/K4kPFox/la/vot\n" ++ "9L/J9UUbzjgQKjeKeaO04wlshYaT/4mWJ3iBj2fjRnRUjtkNaeJK9E10A/+yd+2V\n" ++ "Z5fkscWrv2oj6NSU4kQoYsRL4vDY4ilrGnB+JGGTe08DMiUNRSQrlrRGar9KC/ea\n" ++ "j8GsGsVn82800vpzY4zvFrCopEYq+OsS7HK07/grfoxSwIuEVPkvPuNVqNxmsdnh\n" ++ "X9izjFk0WaSrT2y7HxjbdavYy5LNlDhhDgcGH0tGEPEVvo2FXDtKK4F5D7Rpn0lQ\n" ++ "l033DlZdwJVqwjbDG2jJ9SrcR5q+ss7FJej6A7na+RZukYT1HCjI/CbM1xyQVqdf\n" ++ "bzoEvM14iQuODy+jqk+iGxI9FghAD/FGTNeqewjBCvVtJ94Cj8rDtSvK6evIIVM4\n" ++ "pcw72Hc3MKJP2W/R8kCtQXoXxdZKNYm3QdV8hn9VTYNKpXMgwDqvkPGaJI7ZjnHK\n" ++ "e7iG2rKPmT4dEw0SEe7Uq/DpFXYC5ODfqiAeW2GFZECpkJcNrVPSWh2HagCXZWK0\n" ++ "vm9qp/UsQu0yrbYhnr68\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFajCCA1KgAwIBAgIQLd2szmKXlKFD6LDNdmpeYDANBgkqhkiG9w0BAQsFADBP\n" ++ "MQswCQYDVQQGEwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0\n" ++ "ZC4xGzAZBgNVBAMMEkhpUEtJIFJvb3QgQ0EgLSBHMTAeFw0xOTAyMjIwOTQ2MDRa\n" ++ "Fw0zNzEyMzExNTU5NTlaME8xCzAJBgNVBAYTAlRXMSMwIQYDVQQKDBpDaHVuZ2h3\n" ++ "YSBUZWxlY29tIENvLiwgTHRkLjEbMBkGA1UEAwwSSGlQS0kgUm9vdCBDQSAtIEcx\n" ++ "MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA9B5/UnMyDHPkvRN0o9Qw\n" ++ "qNCuS9i233VHZvR85zkEHmpwINJaR3JnVfSl6J3VHiGh8Ge6zCFovkRTv4354twv\n" ++ "Vcg3Px+kwJyz5HdcoEb+d/oaoDjq7Zpy3iu9lFc6uux55199QmQ5eiY29yTw1S+6\n" ++ "lZgRZq2XNdZ1AYDgr/SEYYwNHl98h5ZeQa/rh+r4XfEuiAU+TCK72h8q3VJGZDnz\n" ++ "Qs7ZngyzsHeXZJzA9KMuH5UHsBffMNsAGJZMoYFL3QRtU6M9/Aes1MU3guvklQgZ\n" ++ "KILSQjqj2FPseYlgSGDIcpJQ3AOPgz+yQlda22rpEZfdhSi8MEyr48KxRURHH+CK\n" ++ "FgeW0iEPU8DtqX7UTuybCeyvQqww1r/REEXgphaypcXTT3OUM3ECoWqj1jOXTyFj\n" ++ "HluP2cFeRXF3D4FdXyGarYPM+l7WjSNfGz1BryB1ZlpK9p/7qxj3ccC2HTHsOyDr\n" ++ "y+K49a6SsvfhhEvyovKTmiKe0xRvNlS9H15ZFblzqMF8b3ti6RZsR1pl8w4Rm0bZ\n" ++ "/W3c1pzAtH2lsN0/Vm+h+fbkEkj9Bn8SV7apI09bA8PgcSojt/ewsTu8mL3WmKgM\n" ++ "a/aOEmem8rJY5AIJEzypuxC00jBF8ez3ABHfZfjcK0NVvxaXxA/VLGGEqnKG/uY6\n" ++ "fsI/fe78LxQ+5oXdUG+3Se0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNV\n" ++ "HQ4EFgQU8ncX+l6o/vY9cdVouslGDDjYr7AwDgYDVR0PAQH/BAQDAgGGMA0GCSqG\n" ++ "SIb3DQEBCwUAA4ICAQBQUfB13HAE4/+qddRxosuej6ip0691x1TPOhwEmSKsxBHi\n" ++ "7zNKpiMdDg1H2DfHb680f0+BazVP6XKlMeJ45/dOlBhbQH3PayFUhuaVevvGyuqc\n" ++ "SE5XCV0vrPSltJczWNWseanMX/mF+lLFjfiRFOs6DRfQUsJ748JzjkZ4Bjgs6Fza\n" ++ "ZsT0pPBWGTMpWmWSBUdGSquEwx4noR8RkpkndZMPvDY7l1ePJlsMu5wP1G4wB9Tc\n" ++ "XzZoZjmDlicmisjEOf6aIW/Vcobpf2Lll07QJNBAsNB1CI69aO4I1258EHBGG3zg\n" ++ "iLKecoaZAeO/n0kZtCW+VmWuF2PlHt/o/0elv+EmBYTksMCv5wiZqAxeJoBF1Pho\n" ++ "L5aPruJKHJwWDBNvOIf2u8g0X5IDUXlwpt/L9ZlNec1OvFefQ05rLisY+GpzjLrF\n" ++ "Ne85akEez3GoorKGB1s6yeHvP2UEgEcyRHCVTjFnanRbEEV16rCf0OY1/k6fi8wr\n" ++ "kkVbbiVghUbN0aqwdmaTd5a+g744tiROJgvM7XpWGuDpWsZkrUx6AEhEL7lAuxM+\n" ++ "vhV4nYWBSipX3tUZQ9rbyltHhoMLP7YNdnhzeSJesYAfz77RP1YQmCuVh6EfnWQU\n" ++ "YDksswBVLuT1sw5XxJFBAJw/6KXf6vb/yPCtbVKoF6ubYfwSUTXkJf2vqmqGOQ==\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFzzCCA7egAwIBAgIUCBZfikyl7ADJk0DfxMauI7gcWqQwDQYJKoZIhvcNAQEL\n" ++ "BQAwbzELMAkGA1UEBhMCSEsxEjAQBgNVBAgTCUhvbmcgS29uZzESMBAGA1UEBxMJ\n" ++ "SG9uZyBLb25nMRYwFAYDVQQKEw1Ib25na29uZyBQb3N0MSAwHgYDVQQDExdIb25n\n" ++ "a29uZyBQb3N0IFJvb3QgQ0EgMzAeFw0xNzA2MDMwMjI5NDZaFw00MjA2MDMwMjI5\n" ++ "NDZaMG8xCzAJBgNVBAYTAkhLMRIwEAYDVQQIEwlIb25nIEtvbmcxEjAQBgNVBAcT\n" ++ "CUhvbmcgS29uZzEWMBQGA1UEChMNSG9uZ2tvbmcgUG9zdDEgMB4GA1UEAxMXSG9u\n" ++ "Z2tvbmcgUG9zdCBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK\n" ++ "AoICAQCziNfqzg8gTr7m1gNt7ln8wlffKWihgw4+aMdoWJwcYEuJQwy51BWy7sFO\n" ++ "dem1p+/l6TWZ5Mwc50tfjTMwIDNT2aa71T4Tjukfh0mtUC1Qyhi+AViiE3CWu4mI\n" ++ "VoBc+L0sPOFMV4i707mV78vH9toxdCim5lSJ9UExyuUmGs2C4HDaOym71QP1mbpV\n" ++ "9WTRYA6ziUm4ii8F0oRFKHyPaFASePwLtVPLwpgchKOesL4jpNrcyCse2m5FHomY\n" ++ "2vkALgbpDDtw1VAliJnLzXNg99X/NWfFobxeq81KuEXryGgeDQ0URhLj0mRiikKY\n" ++ "vLTGCAj4/ahMZJx2Ab0vqWwzD9g/KLg8aQFChn5pwckGyuV6RmXpwtZQQS4/t+Tt\n" ++ "bNe/JgERohYpSms0BpDsE9K2+2p20jzt8NYt3eEV7KObLyzJPivkaTv/ciWxNoZb\n" ++ "x39ri1UbSsUgYT2uy1DhCDq+sI9jQVMwCFk8mB13umOResoQUGC/8Ne8lYePl8X+\n" ++ "l2oBlKN8W4UdKjk60FSh0Tlxnf0h+bV78OLgAo9uliQlLKAeLKjEiafv7ZkGL7YK\n" ++ "TE/bosw3Gq9HhS2KX8Q0NEwA/RiTZxPRN+ZItIsGxVd7GYYKecsAyVKvQv83j+Gj\n" ++ "Hno9UKtjBucVtT+2RTeUN7F+8kjDf8V1/peNRY8apxpyKBpADwIDAQABo2MwYTAP\n" ++ "BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBQXnc0e\n" ++ "i9Y5K3DTXNSguB+wAPzFYTAdBgNVHQ4EFgQUF53NHovWOStw01zUoLgfsAD8xWEw\n" ++ "DQYJKoZIhvcNAQELBQADggIBAFbVe27mIgHSQpsY1Q7XZiNc4/6gx5LS6ZStS6LG\n" ++ "7BJ8dNVI0lkUmcDrudHr9EgwW62nV3OZqdPlt9EuWSRY3GguLmLYauRwCy0gUCCk\n" ++ "MpXRAJi70/33MvJJrsZ64Ee+bs7Lo3I6LWldy8joRTnU+kLBEUx3XZL7av9YROXr\n" ++ "gZ6voJmtvqkBZss4HTzfQx/0TW60uhdG/H39h4F5ag0zD/ov+BS5gLNdTaqX4fnk\n" ++ "GMX41TiMJjz98iji7lpJiCzfeT2OnpA8vUFKOt1b9pq0zj8lMH8yfaIDlNDceqFS\n" ++ "3m6TjRgm/VWsvY+b0s+v54Ysyx8Jb6NvqYTUc79NoXQbTiNg8swOqn+knEwlqLJm\n" ++ "Ozj/2ZQw9nKEvmhVEA/GcywWaZMH/rFF7buiVWqw2rVKAiUnhde3t4ZEFolsgCs+\n" ++ "l6mc1X5VTMbeRRAc6uk7nwNT7u56AQIWeNTowr5GdogTPyK7SBIdUgC0An4hGh6c\n" ++ "JfTzPV4e0hz5sy229zdcxsshTrD3mUcYhcErulWuBurQB7Lcq9CClnXO0lD+mefP\n" ++ "L5/ndtFhKvshuzHQqp9HpLIiyhY6UFfEW0NnxWViA0kB60PZ2Pierc+xYw5F9KBa\n" ++ "LJstxabArahH9CdMOA0uG0k7UvToiIMrVCjU8jVStDKDYmlkDJGcn5fqdBb9HxEG\n" ++ "mpv0\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw\n" ++ "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\n" ++ "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4\n" ++ "WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu\n" ++ "ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY\n" ++ "MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc\n" ++ "h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+\n" ++ "0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U\n" ++ "A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW\n" ++ "T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH\n" ++ "B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC\n" ++ "B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv\n" ++ "KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn\n" ++ "OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn\n" ++ "jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw\n" ++ "qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI\n" ++ "rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV\n" ++ "HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq\n" ++ "hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL\n" ++ "ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ\n" ++ "3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK\n" ++ "NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5\n" ++ "ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur\n" ++ "TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC\n" ++ "jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc\n" ++ "oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq\n" ++ "4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA\n" ++ "mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d\n" ++ "emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw\n" ++ "CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg\n" ++ "R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00\n" ++ "MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBT\n" ++ "ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYw\n" ++ "EAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW\n" ++ "+1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9\n" ++ "ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T\n" ++ "AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI\n" ++ "zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW\n" ++ "tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1\n" ++ "/q4AaOeMSQ+2b1tbFfLn\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFYDCCA0igAwIBAgIQCgFCgAAAAUUjyES1AAAAAjANBgkqhkiG9w0BAQsFADBK\n" ++ "MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVu\n" ++ "VHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwHhcNMTQwMTE2MTgxMjIzWhcNMzQw\n" ++ "MTE2MTgxMjIzWjBKMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScw\n" ++ "JQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwggIiMA0GCSqG\n" ++ "SIb3DQEBAQUAA4ICDwAwggIKAoICAQCnUBneP5k91DNG8W9RYYKyqU+PZ4ldhNlT\n" ++ "3Qwo2dfw/66VQ3KZ+bVdfIrBQuExUHTRgQ18zZshq0PirK1ehm7zCYofWjK9ouuU\n" ++ "+ehcCuz/mNKvcbO0U59Oh++SvL3sTzIwiEsXXlfEU8L2ApeN2WIrvyQfYo3fw7gp\n" ++ "S0l4PJNgiCL8mdo2yMKi1CxUAGc1bnO/AljwpN3lsKImesrgNqUZFvX9t++uP0D1\n" ++ "bVoE/c40yiTcdCMbXTMTEl3EASX2MN0CXZ/g1Ue9tOsbobtJSdifWwLziuQkkORi\n" ++ "T0/Br4sOdBeo0XKIanoBScy0RnnGF7HamB4HWfp1IYVl3ZBWzvurpWCdxJ35UrCL\n" ++ "vYf5jysjCiN2O/cz4ckA82n5S6LgTrx+kzmEB/dEcH7+B1rlsazRGMzyNeVJSQjK\n" ++ "Vsk9+w8YfYs7wRPCTY/JTw436R+hDmrfYi7LNQZReSzIJTj0+kuniVyc0uMNOYZK\n" ++ "dHzVWYfCP04MXFL0PfdSgvHqo6z9STQaKPNBiDoT7uje/5kdX7rL6B7yuVBgwDHT\n" ++ "c+XvvqDtMwt0viAgxGds8AgDelWAf0ZOlqf0Hj7h9tgJ4TNkK2PXMl6f+cB7D3hv\n" ++ "l7yTmvmcEpB4eoCHFddydJxVdHixuuFucAS6T6C6aMN7/zHwcz09lCqxC0EOoP5N\n" ++ "iGVreTO01wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB\n" ++ "/zAdBgNVHQ4EFgQU7UQZwNPwBovupHu+QucmVMiONnYwDQYJKoZIhvcNAQELBQAD\n" ++ "ggIBAA2ukDL2pkt8RHYZYR4nKM1eVO8lvOMIkPkp165oCOGUAFjvLi5+U1KMtlwH\n" ++ "6oi6mYtQlNeCgN9hCQCTrQ0U5s7B8jeUeLBfnLOic7iPBZM4zY0+sLj7wM+x8uwt\n" ++ "LRvM7Kqas6pgghstO8OEPVeKlh6cdbjTMM1gCIOQ045U8U1mwF10A0Cj7oV+wh93\n" ++ "nAbowacYXVKV7cndJZ5t+qntozo00Fl72u1Q8zW/7esUTTHHYPTa8Yec4kjixsU3\n" ++ "+wYQ+nVZZjFHKdp2mhzpgq7vmrlR94gjmmmVYjzlVYA211QC//G5Xc7UI2/YRYRK\n" ++ "W2XviQzdFKcgyxilJbQN+QHwotL0AMh0jqEqSI5l2xPE4iUXfeu+h1sXIFRRk0pT\n" ++ "AwvsXcoz7WL9RccvW9xYoIA55vrX/hMUpu09lEpCdNTDd1lzzY9GvlU47/rokTLq\n" ++ "l1gEIt44w8y8bckzOmoKaT+gyOpyj4xjhiO9bTyWnpXgSUyqorkqG5w2gXjtw+hG\n" ++ "4iZZRHUe2XWJUc0QhJ1hYMtd+ZciTY6Y5uN/9lu7rs3KSoFrXgvzUeF0K+l+J6fZ\n" ++ "mUlO+KWA2yUPHGNiiskzZ2s8EIPGrd6ozRaOjfAHN3Gf8qv8QfXBi+wAN10J5U6A\n" ++ "7/qxXDgGpRtK4dw4LTzcqx+QGtVKnO7RcGzM7vRX+Bi6hG6H\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFZjCCA06gAwIBAgIQCgFCgAAAAUUjz0Z8AAAAAjANBgkqhkiG9w0BAQsFADBN\n" ++ "MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVu\n" ++ "VHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwHhcNMTQwMTE2MTc1MzMyWhcN\n" ++ "MzQwMTE2MTc1MzMyWjBNMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0\n" ++ "MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwggIi\n" ++ "MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2IpT8pEiv6EdrCvsnduTyP4o7\n" ++ "ekosMSqMjbCpwzFrqHd2hCa2rIFCDQjrVVi7evi8ZX3yoG2LqEfpYnYeEe4IFNGy\n" ++ "RBb06tD6Hi9e28tzQa68ALBKK0CyrOE7S8ItneShm+waOh7wCLPQ5CQ1B5+ctMlS\n" ++ "bdsHyo+1W/CD80/HLaXIrcuVIKQxKFdYWuSNG5qrng0M8gozOSI5Cpcu81N3uURF\n" ++ "/YTLNiCBWS2ab21ISGHKTN9T0a9SvESfqy9rg3LvdYDaBjMbXcjaY8ZNzaxmMc3R\n" ++ "3j6HEDbhuaR672BQssvKplbgN6+rNBM5Jeg5ZuSYeqoSmJxZZoY+rfGwyj4GD3vw\n" ++ "EUs3oERte8uojHH01bWRNszwFcYr3lEXsZdMUD2xlVl8BX0tIdUAvwFnol57plzy\n" ++ "9yLxkA2T26pEUWbMfXYD62qoKjgZl3YNa4ph+bz27nb9cCvdKTz4Ch5bQhyLVi9V\n" ++ "GxyhLrXHFub4qjySjmm2AcG1hp2JDws4lFTo6tyePSW8Uybt1as5qsVATFSrsrTZ\n" ++ "2fjXctscvG29ZV/viDUqZi/u9rNl8DONfJhBaUYPQxxp+pu10GFqzcpL2UyQRqsV\n" ++ "WaFHVCkugyhfHMKiq3IXAAaOReyL4jM9f9oZRORicsPfIsbyVtTdX5Vy7W1f90gD\n" ++ "W/3FKqD2cyOEEBsB5wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/\n" ++ "BAUwAwEB/zAdBgNVHQ4EFgQU43HgntinQtnbcZFrlJPrw6PRFKMwDQYJKoZIhvcN\n" ++ "AQELBQADggIBAEf63QqwEZE4rU1d9+UOl1QZgkiHVIyqZJnYWv6IAcVYpZmxI1Qj\n" ++ "t2odIFflAWJBF9MJ23XLblSQdf4an4EKwt3X9wnQW3IV5B4Jaj0z8yGa5hV+rVHV\n" ++ "DRDtfULAj+7AmgjVQdZcDiFpboBhDhXAuM/FSRJSzL46zNQuOAXeNf0fb7iAaJg9\n" ++ "TaDKQGXSc3z1i9kKlT/YPyNtGtEqJBnZhbMX73huqVjRI9PHE+1yJX9dsXNw0H8G\n" ++ "lwmEKYBhHfpe/3OsoOOJuBxxFcbeMX8S3OFtm6/n6J91eEyrRjuazr8FGF1NFTwW\n" ++ "mhlQBJqymm9li1JfPFgEKCXAZmExfrngdbkaqIHWchezxQMxNRF4eKLg6TCMf4Df\n" ++ "WN88uieW4oA0beOY02QnrEh+KHdcxiVhJfiFDGX6xDIvpZgF5PgLZxYWxoK4Mhn5\n" ++ "+bl53B/N66+rDt0b20XkeucC4pVd/GnwU2lhlXV5C15V5jgclKlZM57IcXR5f1GJ\n" ++ "tshquDDIajjDbp7hNxbqBWJMWxJH7ae0s1hWx0nzfxJoCTFx8G34Tkf71oXuxVhA\n" ++ "GaQdp/lLQzfcaFpPz+vCZHTetBXZ9FRUGi8c15dxVJCO2SCdUyt/q4/i6jC8UDfv\n" ++ "8Ue1fXwsBOxonbRJRBD0ckscZOf85muQ3Wl9af0AVqW3rLatt8o+Ae+c\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4\n" ++ "MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6\n" ++ "ZW5wZS5jb20wHhcNMDcxMjEzMTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYD\n" ++ "VQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5j\n" ++ "b20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ03rKDx6sp4boFmVq\n" ++ "scIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAKClaO\n" ++ "xdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6H\n" ++ "LmYRY2xU+zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFX\n" ++ "uaOKmMPsOzTFlUFpfnXCPCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQD\n" ++ "yCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxTOTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+\n" ++ "JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbKF7jJeodWLBoBHmy+E60Q\n" ++ "rLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK0GqfvEyN\n" ++ "BjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8L\n" ++ "hij+0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIB\n" ++ "QFqNeb+Lz0vPqhbBleStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+\n" ++ "HMh3/1uaD7euBUbl8agW7EekFwIDAQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2lu\n" ++ "Zm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+SVpFTlBFIFMuQS4gLSBDSUYg\n" ++ "QTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBGNjIgUzgxQzBB\n" ++ "BgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx\n" ++ "MCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC\n" ++ "AQYwHQYDVR0OBBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUA\n" ++ "A4ICAQB4pgwWSp9MiDrAyw6lFn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWb\n" ++ "laQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbgakEyrkgPH7UIBzg/YsfqikuFgba56\n" ++ "awmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8qhT/AQKM6WfxZSzwo\n" ++ "JNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Csg1lw\n" ++ "LDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCT\n" ++ "VyvehQP5aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGk\n" ++ "LhObNA5me0mrZJfQRsN5nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJb\n" ++ "UjWumDqtujWTI6cfSN01RpiyEGjkpTHCClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/\n" ++ "QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZoQ0iy2+tzJOeRf1SktoA+\n" ++ "naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1ZWrOZyGls\n" ++ "QyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw==\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYD\n" ++ "VQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0\n" ++ "ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0G\n" ++ "CSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTAeFw0wOTA2MTYxMTMwMThaFw0y\n" ++ "OTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3Qx\n" ++ "FjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3pp\n" ++ "Z25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o\n" ++ "dTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvP\n" ++ "kd6mJviZpWNwrZuuyjNAfW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tc\n" ++ "cbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG0IMZfcChEhyVbUr02MelTTMuhTlAdX4U\n" ++ "fIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKApxn1ntxVUwOXewdI/5n7\n" ++ "N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm1HxdrtbC\n" ++ "xkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1\n" ++ "+rUCAwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G\n" ++ "A1UdDgQWBBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPM\n" ++ "Pcu1SCOhGnqmKrs0aDAbBgNVHREEFDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqG\n" ++ "SIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0olZMEyL/azXm4Q5DwpL7v8u8h\n" ++ "mLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfXI/OMn74dseGk\n" ++ "ddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775\n" ++ "tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c\n" ++ "2Pm2G2JwCz02yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5t\n" ++ "HMN1Rq41Bab2XD0h7lbwyYIiLXpUq3DDfSJlgnCW\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIICWTCCAd+gAwIBAgIQZvI9r4fei7FK6gxXMQHC7DAKBggqhkjOPQQDAzBlMQsw\n" ++ "CQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYD\n" ++ "VQQDEy1NaWNyb3NvZnQgRUNDIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIw\n" ++ "MTcwHhcNMTkxMjE4MjMwNjQ1WhcNNDIwNzE4MjMxNjA0WjBlMQswCQYDVQQGEwJV\n" ++ "UzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1NaWNy\n" ++ "b3NvZnQgRUNDIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwdjAQBgcq\n" ++ "hkjOPQIBBgUrgQQAIgNiAATUvD0CQnVBEyPNgASGAlEvaqiBYgtlzPbKnR5vSmZR\n" ++ "ogPZnZH6thaxjG7efM3beaYvzrvOcS/lpaso7GMEZpn4+vKTEAXhgShC48Zo9OYb\n" ++ "hGBKia/teQ87zvH2RPUBeMCjVDBSMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8E\n" ++ "BTADAQH/MB0GA1UdDgQWBBTIy5lycFIM+Oa+sgRXKSrPQhDtNTAQBgkrBgEEAYI3\n" ++ "FQEEAwIBADAKBggqhkjOPQQDAwNoADBlAjBY8k3qDPlfXu5gKcs68tvWMoQZP3zV\n" ++ "L8KxzJOuULsJMsbG7X7JNpQS5GiFBqIb0C8CMQCZ6Ra0DvpWSNSkMBaReNtUjGUB\n" ++ "iudQZsIxtzm6uBoiB078a1QWIP8rtedMDE2mT3M=\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFqDCCA5CgAwIBAgIQHtOXCV/YtLNHcB6qvn9FszANBgkqhkiG9w0BAQwFADBl\n" ++ "MQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYw\n" ++ "NAYDVQQDEy1NaWNyb3NvZnQgUlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5\n" ++ "IDIwMTcwHhcNMTkxMjE4MjI1MTIyWhcNNDIwNzE4MjMwMDIzWjBlMQswCQYDVQQG\n" ++ "EwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1N\n" ++ "aWNyb3NvZnQgUlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwggIi\n" ++ "MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKW76UM4wplZEWCpW9R2LBifOZ\n" ++ "Nt9GkMml7Xhqb0eRaPgnZ1AzHaGm++DlQ6OEAlcBXZxIQIJTELy/xztokLaCLeX0\n" ++ "ZdDMbRnMlfl7rEqUrQ7eS0MdhweSE5CAg2Q1OQT85elss7YfUJQ4ZVBcF0a5toW1\n" ++ "HLUX6NZFndiyJrDKxHBKrmCk3bPZ7Pw71VdyvD/IybLeS2v4I2wDwAW9lcfNcztm\n" ++ "gGTjGqwu+UcF8ga2m3P1eDNbx6H7JyqhtJqRjJHTOoI+dkC0zVJhUXAoP8XFWvLJ\n" ++ "jEm7FFtNyP9nTUwSlq31/niol4fX/V4ggNyhSyL71Imtus5Hl0dVe49FyGcohJUc\n" ++ "aDDv70ngNXtk55iwlNpNhTs+VcQor1fznhPbRiefHqJeRIOkpcrVE7NLP8TjwuaG\n" ++ "YaRSMLl6IE9vDzhTyzMMEyuP1pq9KsgtsRx9S1HKR9FIJ3Jdh+vVReZIZZ2vUpC6\n" ++ "W6IYZVcSn2i51BVrlMRpIpj0M+Dt+VGOQVDJNE92kKz8OMHY4Xu54+OU4UZpyw4K\n" ++ "UGsTuqwPN1q3ErWQgR5WrlcihtnJ0tHXUeOrO8ZV/R4O03QK0dqq6mm4lyiPSMQH\n" ++ "+FJDOvTKVTUssKZqwJz58oHhEmrARdlns87/I6KJClTUFLkqqNfs+avNJVgyeY+Q\n" ++ "W5g5xAgGwax/Dj0ApQIDAQABo1QwUjAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/\n" ++ "BAUwAwEB/zAdBgNVHQ4EFgQUCctZf4aycI8awznjwNnpv7tNsiMwEAYJKwYBBAGC\n" ++ "NxUBBAMCAQAwDQYJKoZIhvcNAQEMBQADggIBAKyvPl3CEZaJjqPnktaXFbgToqZC\n" ++ "LgLNFgVZJ8og6Lq46BrsTaiXVq5lQ7GPAJtSzVXNUzltYkyLDVt8LkS/gxCP81OC\n" ++ "gMNPOsduET/m4xaRhPtthH80dK2Jp86519efhGSSvpWhrQlTM93uCupKUY5vVau6\n" ++ "tZRGrox/2KJQJWVggEbbMwSubLWYdFQl3JPk+ONVFT24bcMKpBLBaYVu32TxU5nh\n" ++ "SnUgnZUP5NbcA/FZGOhHibJXWpS2qdgXKxdJ5XbLwVaZOjex/2kskZGT4d9Mozd2\n" ++ "TaGf+G0eHdP67Pv0RR0Tbc/3WeUiJ3IrhvNXuzDtJE3cfVa7o7P4NHmJweDyAmH3\n" ++ "pvwPuxwXC65B2Xy9J6P9LjrRk5Sxcx0ki69bIImtt2dmefU6xqaWM/5TkshGsRGR\n" ++ "xpl/j8nWZjEgQRCHLQzWwa80mMpkg/sTV9HB8Dx6jKXB/ZUhoHHBk2dxEuqPiApp\n" ++ "GWSZI1b7rCoucL5mxAyE7+WL85MB+GqQk2dLsmijtWKP6T+MejteD+eMuMZ87zf9\n" ++ "dOLITzNy4ZQ5bb0Sr74MTnB8G2+NszKTc0QWbej09+CVgI+WXTik9KveCjCHk9hN\n" ++ "AHFiRSdLOkKEW39lt2c0Ui2cFmuqqNh7o0JMcccMyj6D5KbvtwEwXlGjefVwaaZB\n" ++ "RA+GsCyRxj3qrg+E\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFojCCA4qgAwIBAgIUAZQwHqIL3fXFMyqxQ0Rx+NZQTQ0wDQYJKoZIhvcNAQEM\n" ++ "BQAwaTELMAkGA1UEBhMCS1IxJjAkBgNVBAoMHU5BVkVSIEJVU0lORVNTIFBMQVRG\n" ++ "T1JNIENvcnAuMTIwMAYDVQQDDClOQVZFUiBHbG9iYWwgUm9vdCBDZXJ0aWZpY2F0\n" ++ "aW9uIEF1dGhvcml0eTAeFw0xNzA4MTgwODU4NDJaFw0zNzA4MTgyMzU5NTlaMGkx\n" ++ "CzAJBgNVBAYTAktSMSYwJAYDVQQKDB1OQVZFUiBCVVNJTkVTUyBQTEFURk9STSBD\n" ++ "b3JwLjEyMDAGA1UEAwwpTkFWRVIgR2xvYmFsIFJvb3QgQ2VydGlmaWNhdGlvbiBB\n" ++ "dXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC21PGTXLVA\n" ++ "iQqrDZBbUGOukJR0F0Vy1ntlWilLp1agS7gvQnXp2XskWjFlqxcX0TM62RHcQDaH\n" ++ "38dq6SZeWYp34+hInDEW+j6RscrJo+KfziFTowI2MMtSAuXaMl3Dxeb57hHHi8lE\n" ++ "HoSTGEq0n+USZGnQJoViAbbJAh2+g1G7XNr4rRVqmfeSVPc0W+m/6imBEtRTkZaz\n" ++ "kVrd/pBzKPswRrXKCAfHcXLJZtM0l/aM9BhK4dA9WkW2aacp+yPOiNgSnABIqKYP\n" ++ "szuSjXEOdMWLyEz59JuOuDxp7W87UC9Y7cSw0BwbagzivESq2M0UXZR4Yb8Obtoq\n" ++ "vC8MC3GmsxY/nOb5zJ9TNeIDoKAYv7vxvvTWjIcNQvcGufFt7QSUqP620wbGQGHf\n" ++ "nZ3zVHbOUzoBppJB7ASjjw2i1QnK1sua8e9DXcCrpUHPXFNwcMmIpi3Ua2FzUCaG\n" ++ "YQ5fG8Ir4ozVu53BA0K6lNpfqbDKzE0K70dpAy8i+/Eozr9dUGWokG2zdLAIx6yo\n" ++ "0es+nPxdGoMuK8u180SdOqcXYZaicdNwlhVNt0xz7hlcxVs+Qf6sdWA7G2POAN3a\n" ++ "CJBitOUt7kinaxeZVL6HSuOpXgRM6xBtVNbv8ejyYhbLgGvtPe31HzClrkvJE+2K\n" ++ "AQHJuFFYwGY6sWZLxNUxAmLpdIQM201GLQIDAQABo0IwQDAdBgNVHQ4EFgQU0p+I\n" ++ "36HNLL3s9TsBAZMzJ7LrYEswDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMB\n" ++ "Af8wDQYJKoZIhvcNAQEMBQADggIBADLKgLOdPVQG3dLSLvCkASELZ0jKbY7gyKoN\n" ++ "qo0hV4/GPnrK21HUUrPUloSlWGB/5QuOH/XcChWB5Tu2tyIvCZwTFrFsDDUIbatj\n" ++ "cu3cvuzHV+YwIHHW1xDBE1UBjCpD5EHxzzp6U5LOogMFDTjfArsQLtk70pt6wKGm\n" ++ "+LUx5vR1yblTmXVHIloUFcd4G7ad6Qz4G3bxhYTeodoS76TiEJd6eN4MUZeoIUCL\n" ++ "hr0N8F5OSza7OyAfikJW4Qsav3vQIkMsRIz75Sq0bBwcupTgE34h5prCy8VCZLQe\n" ++ "lHsIJchxzIdFV4XTnyliIoNRlwAYl3dqmJLJfGBs32x9SuRwTMKeuB330DTHD8z7\n" ++ "p/8Dvq1wkNoL3chtl1+afwkyQf3NosxabUzyqkn+Zvjp2DXrDige7kgvOtB5CTh8\n" ++ "piKCk5XQA76+AqAF3SAi428diDRgxuYKuQl1C/AH6GmWNcf7I4GOODm4RStDeKLR\n" ++ "LBT/DShycpWbXgnbiUSYqqFJu3FS8r/2/yehNq+4tneI3TqkbZs0kNwUXTC/t+sX\n" ++ "5Ie3cdCh13cV1ELX8vMxmV2b3RZtP+oGI/hGoiLtk/bdmuYqh7GYVPEi92tF4+KO\n" ++ "dh2ajcQGjTa3FPOdVGm3jjzVpG2Tgbet9r1ke8LJaDmgkpzNNIaRkPpkUZ3+/uul\n" ++ "9XXeifdy\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQG\n" ++ "EwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3\n" ++ "MDUGA1UECwwuVGFuw7pzw610dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNl\n" ++ "cnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBBcmFueSAoQ2xhc3MgR29sZCkgRsWR\n" ++ "dGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgxMjA2MTUwODIxWjCB\n" ++ "pzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxOZXRM\n" ++ "b2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlm\n" ++ "aWNhdGlvbiBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNz\n" ++ "IEdvbGQpIEbFkXRhbsO6c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A\n" ++ "MIIBCgKCAQEAxCRec75LbRTDofTjl5Bu0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrT\n" ++ "lF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw/HpYzY6b7cNGbIRwXdrz\n" ++ "AZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAkH3B5r9s5\n" ++ "VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRG\n" ++ "ILdwfzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2\n" ++ "BJtr+UBdADTHLpl1neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAG\n" ++ "AQH/AgEEMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2M\n" ++ "U9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwWqZw8UQCgwBEIBaeZ5m8BiFRh\n" ++ "bvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTtaYtOUZcTh5m2C\n" ++ "+C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC\n" ++ "bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2F\n" ++ "uLjbvrW5KfnaNwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2\n" ++ "XjG4Kvte9nHfRCaexOYNkbQudZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E=\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIDtTCCAp2gAwIBAgIQdrEgUnTwhYdGs/gjGvbCwDANBgkqhkiG9w0BAQsFADBt\n" ++ "MQswCQYDVQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUg\n" ++ "Rm91bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9i\n" ++ "YWwgUm9vdCBHQiBDQTAeFw0xNDEyMDExNTAwMzJaFw0zOTEyMDExNTEwMzFaMG0x\n" ++ "CzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQLExlPSVNURSBG\n" ++ "b3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2Jh\n" ++ "bCBSb290IEdCIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2Be3\n" ++ "HEokKtaXscriHvt9OO+Y9bI5mE4nuBFde9IllIiCFSZqGzG7qFshISvYD06fWvGx\n" ++ "WuR51jIjK+FTzJlFXHtPrby/h0oLS5daqPZI7H17Dc0hBt+eFf1Biki3IPShehtX\n" ++ "1F1Q/7pn2COZH8g/497/b1t3sWtuuMlk9+HKQUYOKXHQuSP8yYFfTvdv37+ErXNk\n" ++ "u7dCjmn21HYdfp2nuFeKUWdy19SouJVUQHMD9ur06/4oQnc/nSMbsrY9gBQHTC5P\n" ++ "99UKFg29ZkM3fiNDecNAhvVMKdqOmq0NpQSHiB6F4+lT1ZvIiwNjeOvgGUpuuy9r\n" ++ "M2RYk61pv48b74JIxwIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUw\n" ++ "AwEB/zAdBgNVHQ4EFgQUNQ/INmNe4qPs+TtmFc5RUuORmj0wEAYJKwYBBAGCNxUB\n" ++ "BAMCAQAwDQYJKoZIhvcNAQELBQADggEBAEBM+4eymYGQfp3FsLAmzYh7KzKNbrgh\n" ++ "cViXfa43FK8+5/ea4n32cZiZBKpDdHij40lhPnOMTZTg+XHEthYOU3gf1qKHLwI5\n" ++ "gSk8rxWYITD+KJAAjNHhy/peyP34EEY7onhCkRd0VQreUGdNZtGn//3ZwLWoo4rO\n" ++ "ZvUPQ82nK1d7Y0Zqqi5S2PTt4W2tKZB4SLrhI6qjiey1q5bAtEuiHZeeevJuQHHf\n" ++ "aPFlTc58Bd9TZaml8LGXBHAVRgOY1NK/VLSgWH1Sb9pWJmLU2NuJMW8c8CLC02Ic\n" ++ "Nc1MaRVUGpCY3useX8p3x8uOPUNpnJpY0CQ73xtAln41rYHHTnG6iBM=\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIICaTCCAe+gAwIBAgIQISpWDK7aDKtARb8roi066jAKBggqhkjOPQQDAzBtMQsw\n" ++ "CQYDVQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91\n" ++ "bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwg\n" ++ "Um9vdCBHQyBDQTAeFw0xNzA1MDkwOTQ4MzRaFw00MjA1MDkwOTU4MzNaMG0xCzAJ\n" ++ "BgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQLExlPSVNURSBGb3Vu\n" ++ "ZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2JhbCBS\n" ++ "b290IEdDIENBMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAETOlQwMYPchi82PG6s4ni\n" ++ "eUqjFqdrVCTbUf/q9Akkwwsin8tqJ4KBDdLArzHkdIJuyiXZjHWd8dvQmqJLIX4W\n" ++ "p2OQ0jnUsYd4XxiWD1AbNTcPasbc2RNNpI6QN+a9WzGRo1QwUjAOBgNVHQ8BAf8E\n" ++ "BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUSIcUrOPDnpBgOtfKie7T\n" ++ "rYy0UGYwEAYJKwYBBAGCNxUBBAMCAQAwCgYIKoZIzj0EAwMDaAAwZQIwJsdpW9zV\n" ++ "57LnyAyMjMPdeYwbY9XJUpROTYJKcx6ygISpJcBMWm1JKWB4E+J+SOtkAjEA2zQg\n" ++ "Mgj/mkkCtojeFK9dbJlxjRo/i9fgojaGHAeCOnZT/cKi7e97sIBPWA9LUzm9\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFYDCCA0igAwIBAgIUeFhfLq0sGUvjNwc1NBMotZbUZZMwDQYJKoZIhvcNAQEL\n" ++ "BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc\n" ++ "BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMSBHMzAeFw0xMjAxMTIxNzI3NDRaFw00\n" ++ "MjAxMTIxNzI3NDRaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM\n" ++ "aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDEgRzMwggIiMA0GCSqG\n" ++ "SIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvlAQjunybEC0BJyFuTHK3C3kEakEPBtV\n" ++ "wedYMB0ktMPvhd6MLOHBPd+C5k+tR4ds7FtJwUrVu4/sh6x/gpqG7D0DmVIB0jWe\n" ++ "rNrwU8lmPNSsAgHaJNM7qAJGr6Qc4/hzWHa39g6QDbXwz8z6+cZM5cOGMAqNF341\n" ++ "68Xfuw6cwI2H44g4hWf6Pser4BOcBRiYz5P1sZK0/CPTz9XEJ0ngnjybCKOLXSoh\n" ++ "4Pw5qlPafX7PGglTvF0FBM+hSo+LdoINofjSxxR3W5A2B4GbPgb6Ul5jxaYA/qXp\n" ++ "UhtStZI5cgMJYr2wYBZupt0lwgNm3fME0UDiTouG9G/lg6AnhF4EwfWQvTA9xO+o\n" ++ "abw4m6SkltFi2mnAAZauy8RRNOoMqv8hjlmPSlzkYZqn0ukqeI1RPToV7qJZjqlc\n" ++ "3sX5kCLliEVx3ZGZbHqfPT2YfF72vhZooF6uCyP8Wg+qInYtyaEQHeTTRCOQiJ/G\n" ++ "KubX9ZqzWB4vMIkIG1SitZgj7Ah3HJVdYdHLiZxfokqRmu8hqkkWCKi9YSgxyXSt\n" ++ "hfbZxbGL0eUQMk1fiyA6PEkfM4VZDdvLCXVDaXP7a3F98N/ETH3Goy7IlXnLc6KO\n" ++ "Tk0k+17kBL5yG6YnLUlamXrXXAkgt3+UuU/xDRxeiEIbEbfnkduebPRq34wGmAOt\n" ++ "zCjvpUfzUwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB\n" ++ "BjAdBgNVHQ4EFgQUo5fW816iEOGrRZ88F2Q87gFwnMwwDQYJKoZIhvcNAQELBQAD\n" ++ "ggIBABj6W3X8PnrHX3fHyt/PX8MSxEBd1DKquGrX1RUVRpgjpeaQWxiZTOOtQqOC\n" ++ "MTaIzen7xASWSIsBx40Bz1szBpZGZnQdT+3Btrm0DWHMY37XLneMlhwqI2hrhVd2\n" ++ "cDMT/uFPpiN3GPoajOi9ZcnPP/TJF9zrx7zABC4tRi9pZsMbj/7sPtPKlL92CiUN\n" ++ "qXsCHKnQO18LwIE6PWThv6ctTr1NxNgpxiIY0MWscgKCP6o6ojoilzHdCGPDdRS5\n" ++ "YCgtW2jgFqlmgiNR9etT2DGbe+m3nUvriBbP+V04ikkwj+3x6xn0dxoxGE1nVGwv\n" ++ "b2X52z3sIexe9PSLymBlVNFxZPT5pqOBMzYzcfCkeF9OrYMh3jRJjehZrJ3ydlo2\n" ++ "8hP0r+AJx2EqbPfgna67hkooby7utHnNkDPDs3b69fBsnQGQ+p6Q9pxyz0fawx/k\n" ++ "NSBT8lTR32GDpgLiJTjehTItXnOQUl1CxM49S+H5GYQd1aJQzEH7QRTDvdbJWqNj\n" ++ "ZgKAvQU6O0ec7AAmTPWIUb+oI38YB7AL7YsmoWTTYUrrXJ/es69nA7Mf3W1daWhp\n" ++ "q1467HxpvMc7hU6eFbm0FU/DlXpY18ls6Wy58yljXrQs8C097Vpl4KlbQMJImYFt\n" ++ "nh8GKjwStIsPm6Ik8KaN1nrgS7ZklmOVhMJKzRwuJIczYOXD\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x\n" ++ "GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv\n" ++ "b3QgQ0EgMjAeFw0wNjExMjQxODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNV\n" ++ "BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W\n" ++ "YWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCa\n" ++ "GMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6XJxg\n" ++ "Fyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55J\n" ++ "WpzmM+Yklvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bB\n" ++ "rrcCaoF6qUWD4gXmuVbBlDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp\n" ++ "+ARz8un+XJiM9XOva7R+zdRcAitMOeGylZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1\n" ++ "ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt66/3FsvbzSUr5R/7mp/i\n" ++ "Ucw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1JdxnwQ5hYIiz\n" ++ "PtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og\n" ++ "/zOhD7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UH\n" ++ "oycR7hYQe7xFSkyyBNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuI\n" ++ "yV77zGHcizN300QyNQliBJIWENieJ0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1Ud\n" ++ "EwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBQahGK8SEwzJQTU7tD2\n" ++ "A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGUa6FJpEcwRTEL\n" ++ "MAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT\n" ++ "ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2f\n" ++ "BluornFdLwUvZ+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzn\n" ++ "g/iN/Ae42l9NLmeyhP3ZRPx3UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2Bl\n" ++ "fF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodmVjB3pjd4M1IQWK4/YY7yarHvGH5K\n" ++ "WWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK+JDSV6IZUaUtl0Ha\n" ++ "B0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrWIozc\n" ++ "hLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPR\n" ++ "TUIZ3Ph1WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWD\n" ++ "mbA4CD/pXvk1B+TJYm5Xf6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0Z\n" ++ "ohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y\n" ++ "4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8VCLAAVBpQ570su9t+Oza\n" ++ "8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQEL\n" ++ "BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc\n" ++ "BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00\n" ++ "MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM\n" ++ "aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIgRzMwggIiMA0GCSqG\n" ++ "SIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFhZiFf\n" ++ "qq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMW\n" ++ "n4rjyduYNM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ym\n" ++ "c5GQYaYDFCDy54ejiK2toIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+\n" ++ "O7q414AB+6XrW7PFXmAqMaCvN+ggOp+oMiwMzAkd056OXbxMmO7FGmh77FOm6RQ1\n" ++ "o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+lV0POKa2Mq1W/xPtbAd0j\n" ++ "IaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZoL1NesNKq\n" ++ "IcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz\n" ++ "8eQQsSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43eh\n" ++ "vNURG3YBZwjgQQvD6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l\n" ++ "7ZizlWNof/k19N+IxWA1ksB8aRxhlRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALG\n" ++ "cC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB\n" ++ "BjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZIhvcNAQELBQAD\n" ++ "ggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66\n" ++ "AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RC\n" ++ "roijQ1h5fq7KpVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0Ga\n" ++ "W/ZZGYjeVYg3UQt4XAoeo0L9x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4n\n" ++ "lv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgzdWqTHBLmYF5vHX/JHyPLhGGfHoJE\n" ++ "+V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6XU/IyAgkwo1jwDQHV\n" ++ "csaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+NwmNtd\n" ++ "dbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNg\n" ++ "KCLjsZWDzYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeM\n" ++ "HVOyToV7BjjHLPj4sHKNJeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4\n" ++ "WSr2Rz0ZiC3oheGe7IUIarFsNMkd7EgrO3jtZsSOeWmD3n+M\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x\n" ++ "GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv\n" ++ "b3QgQ0EgMzAeFw0wNjExMjQxOTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNV\n" ++ "BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W\n" ++ "YWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDM\n" ++ "V0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNggDhoB\n" ++ "4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUr\n" ++ "H556VOijKTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd\n" ++ "8lyyBTNvijbO0BNO/79KDDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9Cabwv\n" ++ "vWhDFlaJKjdhkf2mrk7AyxRllDdLkgbvBNDInIjbC3uBr7E9KsRlOni27tyAsdLT\n" ++ "mZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwpp5ijJUMv7/FfJuGITfhe\n" ++ "btfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8nT8KKdjc\n" ++ "T5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDt\n" ++ "WAEXMJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZ\n" ++ "c6tsgLjoC2SToJyMGf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A\n" ++ "4iLItLRkT9a6fUg+qGkM17uGcclzuD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYD\n" ++ "VR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHTBgkrBgEEAb5YAAMwgcUwgZMG\n" ++ "CCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmljYXRlIGNvbnN0\n" ++ "aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0\n" ++ "aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVu\n" ++ "dC4wLQYIKwYBBQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2Nw\n" ++ "czALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4G\n" ++ "A1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4ywLQoUmkRzBFMQswCQYDVQQGEwJC\n" ++ "TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UEAxMSUXVvVmFkaXMg\n" ++ "Um9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZVqyM0\n" ++ "7ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSem\n" ++ "d1o417+shvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd\n" ++ "+LJ2w/w4E6oM3kJpK27zPOuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B\n" ++ "4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadN\n" ++ "t54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp8kokUvd0/bpO5qgdAm6x\n" ++ "DYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBCbjPsMZ57\n" ++ "k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6s\n" ++ "zHXug/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0j\n" ++ "Wy10QJLZYxkNc91pvGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeT\n" ++ "mJlglFwjz1onl14LBQaTNx47aTbrqZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK\n" ++ "4SVhM7JZG+Ju1zdXtg2pEto=\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFYDCCA0igAwIBAgIULvWbAiin23r/1aOp7r0DoM8Sah0wDQYJKoZIhvcNAQEL\n" ++ "BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc\n" ++ "BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMyBHMzAeFw0xMjAxMTIyMDI2MzJaFw00\n" ++ "MjAxMTIyMDI2MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM\n" ++ "aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDMgRzMwggIiMA0GCSqG\n" ++ "SIb3DQEBAQUAA4ICDwAwggIKAoICAQCzyw4QZ47qFJenMioKVjZ/aEzHs286IxSR\n" ++ "/xl/pcqs7rN2nXrpixurazHb+gtTTK/FpRp5PIpM/6zfJd5O2YIyC0TeytuMrKNu\n" ++ "FoM7pmRLMon7FhY4futD4tN0SsJiCnMK3UmzV9KwCoWdcTzeo8vAMvMBOSBDGzXR\n" ++ "U7Ox7sWTaYI+FrUoRqHe6okJ7UO4BUaKhvVZR74bbwEhELn9qdIoyhA5CcoTNs+c\n" ++ "ra1AdHkrAj80//ogaX3T7mH1urPnMNA3I4ZyYUUpSFlob3emLoG+B01vr87ERROR\n" ++ "FHAGjx+f+IdpsQ7vw4kZ6+ocYfx6bIrc1gMLnia6Et3UVDmrJqMz6nWB2i3ND0/k\n" ++ "A9HvFZcba5DFApCTZgIhsUfei5pKgLlVj7WiL8DWM2fafsSntARE60f75li59wzw\n" ++ "eyuxwHApw0BiLTtIadwjPEjrewl5qW3aqDCYz4ByA4imW0aucnl8CAMhZa634Ryl\n" ++ "sSqiMd5mBPfAdOhx3v89WcyWJhKLhZVXGqtrdQtEPREoPHtht+KPZ0/l7DxMYIBp\n" ++ "VzgeAVuNVejH38DMdyM0SXV89pgR6y3e7UEuFAUCf+D+IOs15xGsIs5XPd7JMG0Q\n" ++ "A4XN8f+MFrXBsj6IbGB/kE+V9/YtrQE5BwT6dYB9v0lQ7e/JxHwc64B+27bQ3RP+\n" ++ "ydOc17KXqQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB\n" ++ "BjAdBgNVHQ4EFgQUxhfQvKjqAkPyGwaZXSuQILnXnOQwDQYJKoZIhvcNAQELBQAD\n" ++ "ggIBADRh2Va1EodVTd2jNTFGu6QHcrxfYWLopfsLN7E8trP6KZ1/AvWkyaiTt3px\n" ++ "KGmPc+FSkNrVvjrlt3ZqVoAh313m6Tqe5T72omnHKgqwGEfcIHB9UqM+WXzBusnI\n" ++ "FUBhynLWcKzSt/Ac5IYp8M7vaGPQtSCKFWGafoaYtMnCdvvMujAWzKNhxnQT5Wvv\n" ++ "oxXqA/4Ti2Tk08HS6IT7SdEQTXlm66r99I0xHnAUrdzeZxNMgRVhvLfZkXdxGYFg\n" ++ "u/BYpbWcC/ePIlUnwEsBbTuZDdQdm2NnL9DuDcpmvJRPpq3t/O5jrFc/ZSXPsoaP\n" ++ "0Aj/uHYUbt7lJ+yreLVTubY/6CD50qi+YUbKh4yE8/nxoGibIh6BJpsQBJFxwAYf\n" ++ "3KDTuVan45gtf4Od34wrnDKOMpTwATwiKp9Dwi7DmDkHOHv8XgBCH/MyJnmDhPbl\n" ++ "8MFREsALHgQjDFSlTC9JxUrRtm5gDWv8a4uFJGS3iQ6rJUdbPM9+Sb3H6QrG2vd+\n" ++ "DhcI00iX0HGS8A85PjRqHH3Y8iKuu2n0M7SmSFXRDw4m6Oy2Cy2nhTXN/VnIn9HN\n" ++ "PlopNLk9hM6xZdRZkZFWdSHBd575euFgndOtBBj0fOtek49TSiIp+EgrPk2GrFt/\n" ++ "ywaZWWDYWGWVjUTR939+J399roD1B0y2PpxxVJkES/1Y+Zj0\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIClDCCAhqgAwIBAgIILCmcWxbtBZUwCgYIKoZIzj0EAwIwfzELMAkGA1UEBhMC\n" ++ "VVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9T\n" ++ "U0wgQ29ycG9yYXRpb24xNDAyBgNVBAMMK1NTTC5jb20gRVYgUm9vdCBDZXJ0aWZp\n" ++ "Y2F0aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEyMTgxNTIzWhcNNDEwMjEyMTgx\n" ++ "NTIzWjB/MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hv\n" ++ "dXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjE0MDIGA1UEAwwrU1NMLmNv\n" ++ "bSBFViBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49\n" ++ "AgEGBSuBBAAiA2IABKoSR5CYG/vvw0AHgyBO8TCCogbR8pKGYfL2IWjKAMTH6kMA\n" ++ "VIbc/R/fALhBYlzccBYy3h+Z1MzFB8gIH2EWB1E9fVwHU+M1OIzfzZ/ZLg1Kthku\n" ++ "WnBaBu2+8KGwytAJKaNjMGEwHQYDVR0OBBYEFFvKXuXe0oGqzagtZFG22XKbl+ZP\n" ++ "MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUW8pe5d7SgarNqC1kUbbZcpuX\n" ++ "5k8wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2gAMGUCMQCK5kCJN+vp1RPZ\n" ++ "ytRrJPOwPYdGWBrssd9v+1a6cGvHOMzosYxPD/fxZ3YOg9AeUY8CMD32IygmTMZg\n" ++ "h5Mmm7I1HrrW9zzRHM76JTymGoEVW/MSD2zuZYrJh6j5B+BimoxcSg==\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIF6zCCA9OgAwIBAgIIVrYpzTS8ePYwDQYJKoZIhvcNAQELBQAwgYIxCzAJBgNV\n" ++ "BAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjEYMBYGA1UE\n" ++ "CgwPU1NMIENvcnBvcmF0aW9uMTcwNQYDVQQDDC5TU0wuY29tIEVWIFJvb3QgQ2Vy\n" ++ "dGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIyMB4XDTE3MDUzMTE4MTQzN1oXDTQy\n" ++ "MDUzMDE4MTQzN1owgYIxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4G\n" ++ "A1UEBwwHSG91c3RvbjEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMTcwNQYDVQQD\n" ++ "DC5TU0wuY29tIEVWIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIy\n" ++ "MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAjzZlQOHWTcDXtOlG2mvq\n" ++ "M0fNTPl9fb69LT3w23jhhqXZuglXaO1XPqDQCEGD5yhBJB/jchXQARr7XnAjssuf\n" ++ "OePPxU7Gkm0mxnu7s9onnQqG6YE3Bf7wcXHswxzpY6IXFJ3vG2fThVUCAtZJycxa\n" ++ "4bH3bzKfydQ7iEGonL3Lq9ttewkfokxykNorCPzPPFTOZw+oz12WGQvE43LrrdF9\n" ++ "HSfvkusQv1vrO6/PgN3B0pYEW3p+pKk8OHakYo6gOV7qd89dAFmPZiw+B6KjBSYR\n" ++ "aZfqhbcPlgtLyEDhULouisv3D5oi53+aNxPN8k0TayHRwMwi8qFG9kRpnMphNQcA\n" ++ "b9ZhCBHqurj26bNg5U257J8UZslXWNvNh2n4ioYSA0e/ZhN2rHd9NCSFg83XqpyQ\n" ++ "Gp8hLH94t2S42Oim9HizVcuE0jLEeK6jj2HdzghTreyI/BXkmg3mnxp3zkyPuBQV\n" ++ "PWKchjgGAGYS5Fl2WlPAApiiECtoRHuOec4zSnaqW4EWG7WK2NAAe15itAnWhmMO\n" ++ "pgWVSbooi4iTsjQc2KRVbrcc0N6ZVTsj9CLg+SlmJuwgUHfbSguPvuUCYHBBXtSu\n" ++ "UDkiFCbLsjtzdFVHB3mBOagwE0TlBIqulhMlQg+5U8Sb/M3kHN48+qvWBkofZ6aY\n" ++ "MBzdLNvcGJVXZsb/XItW9XcCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNV\n" ++ "HSMEGDAWgBT5YLvU49U09rj1BoAlp3PbRmmonjAdBgNVHQ4EFgQU+WC71OPVNPa4\n" ++ "9QaAJadz20ZpqJ4wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQBW\n" ++ "s47LCp1Jjr+kxJG7ZhcFUZh1++VQLHqe8RT6q9OKPv+RKY9ji9i0qVQBDb6Thi/5\n" ++ "Sm3HXvVX+cpVHBK+Rw82xd9qt9t1wkclf7nxY/hoLVUE0fKNsKTPvDxeH3jnpaAg\n" ++ "cLAExbf3cqfeIg29MyVGjGSSJuM+LmOW2puMPfgYCdcDzH2GguDKBAdRUNf/ktUM\n" ++ "79qGn5nX67evaOI5JpS6aLe/g9Pqemc9YmeuJeVy6OLk7K4S9ksrPJ/psEDzOFSz\n" ++ "/bdoyNrGj1E8svuR3Bznm53htw1yj+KkxKl4+esUrMZDBcJlOSgYAsOCsp0FvmXt\n" ++ "ll9ldDz7CTUue5wT/RsPXcdtgTpWD8w74a8CLyKsRspGPKAcTNZEtF4uXBVmCeEm\n" ++ "Kf7GUmG6sXP/wwyc5WxqlD8UykAWlYTzWamsX0xhk23RO8yilQwipmdnRC652dKK\n" ++ "QbNmC1r7fSOl8hqw/96bg5Qu0T/fkreRrwU7ZcegbLHNYhLDkBvjJc40vG93drEQ\n" ++ "w/cFGsDWr3RiSBd3kmmQYRzelYB0VI8YHMPzA9C/pEN1hlMYegouCRw2n5H9gooi\n" ++ "S9EOUCXdywMMF8mDAAhONU2Ki+3wApRmLER/y5UnlhetCTCstnEXbosX9hwJ1C07\n" ++ "mKVx01QT2WDz9UtmT/rx7iASjbSsV7FFY6GsdqnC+w==\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIICjTCCAhSgAwIBAgIIdebfy8FoW6gwCgYIKoZIzj0EAwIwfDELMAkGA1UEBhMC\n" ++ "VVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9T\n" ++ "U0wgQ29ycG9yYXRpb24xMTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZpY2F0\n" ++ "aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEyMTgxNDAzWhcNNDEwMjEyMTgxNDAz\n" ++ "WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hvdXN0\n" ++ "b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNvbSBS\n" ++ "b290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49AgEGBSuB\n" ++ "BAAiA2IABEVuqVDEpiM2nl8ojRfLliJkP9x6jh3MCLOicSS6jkm5BBtHllirLZXI\n" ++ "7Z4INcgn64mMU1jrYor+8FsPazFSY0E7ic3s7LaNGdM0B9y7xgZ/wkWV7Mt/qCPg\n" ++ "CemB+vNH06NjMGEwHQYDVR0OBBYEFILRhXMw5zUE044CkvvlpNHEIejNMA8GA1Ud\n" ++ "EwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUgtGFczDnNQTTjgKS++Wk0cQh6M0wDgYD\n" ++ "VR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2cAMGQCMG/n61kRpGDPYbCWe+0F+S8T\n" ++ "kdzt5fxQaxFGRrMcIQBiu77D5+jNB5n5DQtdcj7EqgIwH7y6C+IwJPt8bYBVCpk+\n" ++ "gA0z5Wajs6O7pdWLjwkspl1+4vAHCGht0nxpbl/f5Wpl\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIF3TCCA8WgAwIBAgIIeyyb0xaAMpkwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UE\n" ++ "BhMCVVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQK\n" ++ "DA9TU0wgQ29ycG9yYXRpb24xMTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZp\n" ++ "Y2F0aW9uIEF1dGhvcml0eSBSU0EwHhcNMTYwMjEyMTczOTM5WhcNNDEwMjEyMTcz\n" ++ "OTM5WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hv\n" ++ "dXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNv\n" ++ "bSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFJTQTCCAiIwDQYJKoZIhvcN\n" ++ "AQEBBQADggIPADCCAgoCggIBAPkP3aMrfcvQKv7sZ4Wm5y4bunfh4/WvpOz6Sl2R\n" ++ "xFdHaxh3a3by/ZPkPQ/CFp4LZsNWlJ4Xg4XOVu/yFv0AYvUiCVToZRdOQbngT0aX\n" ++ "qhvIuG5iXmmxX9sqAn78bMrzQdjt0Oj8P2FI7bADFB0QDksZ4LtO7IZl/zbzXmcC\n" ++ "C52GVWH9ejjt/uIZALdvoVBidXQ8oPrIJZK0bnoix/geoeOy3ZExqysdBP+lSgQ3\n" ++ "6YWkMyv94tZVNHwZpEpox7Ko07fKoZOI68GXvIz5HdkihCR0xwQ9aqkpk8zruFvh\n" ++ "/l8lqjRYyMEjVJ0bmBHDOJx+PYZspQ9AhnwC9FwCTyjLrnGfDzrIM/4RJTXq/LrF\n" ++ "YD3ZfBjVsqnTdXgDciLKOsMf7yzlLqn6niy2UUb9rwPW6mBo6oUWNmuF6R7As93E\n" ++ "JNyAKoFBbZQ+yODJgUEAnl6/f8UImKIYLEJAs/lvOCdLToD0PYFH4Ih86hzOtXVc\n" ++ "US4cK38acijnALXRdMbX5J+tB5O2UzU1/Dfkw/ZdFr4hc96SCvigY2q8lpJqPvi8\n" ++ "ZVWb3vUNiSYE/CUapiVpy8JtynziWV+XrOvvLsi81xtZPCvM8hnIk2snYxnP/Okm\n" ++ "+Mpxm3+T/jRnhE6Z6/yzeAkzcLpmpnbtG3PrGqUNxCITIJRWCk4sbE6x/c+cCbqi\n" ++ "M+2HAgMBAAGjYzBhMB0GA1UdDgQWBBTdBAkHovV6fVJTEpKV7jiAJQ2mWTAPBgNV\n" ++ "HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFN0ECQei9Xp9UlMSkpXuOIAlDaZZMA4G\n" ++ "A1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAIBgRlCn7Jp0cHh5wYfGV\n" ++ "cpNxJK1ok1iOMq8bs3AD/CUrdIWQPXhq9LmLpZc7tRiRux6n+UBbkflVma8eEdBc\n" ++ "Hadm47GUBwwyOabqG7B52B2ccETjit3E+ZUfijhDPwGFpUenPUayvOUiaPd7nNgs\n" ++ "PgohyC0zrL/FgZkxdMF1ccW+sfAjRfSda/wZY52jvATGGAslu1OJD7OAUN5F7kR/\n" ++ "q5R4ZJjT9ijdh9hwZXT7DrkT66cPYakylszeu+1jTBi7qUD3oFRuIIhxdRjqerQ0\n" ++ "cuAjJ3dctpDqhiVAq+8zD8ufgr6iIPv2tS0a5sKFsXQP+8hlAqRSAUfdSSLBv9jr\n" ++ "a6x+3uxjMxW3IwiPxg+NQVrdjsW5j+VFP3jbutIbQLH+cU0/4IGiul607BXgk90I\n" ++ "H37hVZkLId6Tngr75qNJvTYw/ud3sqB1l7UtgYgXZSD32pAAn8lSzDLKNXz1PQ/Y\n" ++ "K9f1JmzJBjSWFupwWRoyeXkLtoh/D1JIPb9s2KJELtFOt3JY04kTlf5Eq/jXixtu\n" ++ "nLwsoFvVagCvXzfh1foQC5ichucmj87w7G6KVwuA406ywKBjYZC6VWg3dGq2ktuf\n" ++ "oYYitmUnDuy2n0Jg5GfCtdpBC8TTi2EbvPofkSvXRAdeuims2cXp71NIWuuA8ShY\n" ++ "Ic2wBlX7Jz9TkHCpBB5XJ7k=\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIICOjCCAcCgAwIBAgIQFAP1q/s3ixdAW+JDsqXRxDAKBggqhkjOPQQDAzBOMQsw\n" ++ "CQYDVQQGEwJVUzEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMSUwIwYDVQQDDBxT\n" ++ "U0wuY29tIFRMUyBFQ0MgUm9vdCBDQSAyMDIyMB4XDTIyMDgyNTE2MzM0OFoXDTQ2\n" ++ "MDgxOTE2MzM0N1owTjELMAkGA1UEBhMCVVMxGDAWBgNVBAoMD1NTTCBDb3Jwb3Jh\n" ++ "dGlvbjElMCMGA1UEAwwcU1NMLmNvbSBUTFMgRUNDIFJvb3QgQ0EgMjAyMjB2MBAG\n" ++ "ByqGSM49AgEGBSuBBAAiA2IABEUpNXP6wrgjzhR9qLFNoFs27iosU8NgCTWyJGYm\n" ++ "acCzldZdkkAZDsalE3D07xJRKF3nzL35PIXBz5SQySvOkkJYWWf9lCcQZIxPBLFN\n" ++ "SeR7T5v15wj4A4j3p8OSSxlUgaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSME\n" ++ "GDAWgBSJjy+j6CugFFR781a4Jl9nOAuc0DAdBgNVHQ4EFgQUiY8vo+groBRUe/NW\n" ++ "uCZfZzgLnNAwDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMDA2gAMGUCMFXjIlbp\n" ++ "15IkWE8elDIPDAI2wv2sdDJO4fscgIijzPvX6yv/N33w7deedWo1dlJF4AIxAMeN\n" ++ "b0Igj762TVntd00pxCAgRWSGOlDGxK0tk/UYfXLtqc/ErFc2KAhl3zx5Zn6g6g==\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFiTCCA3GgAwIBAgIQb77arXO9CEDii02+1PdbkTANBgkqhkiG9w0BAQsFADBO\n" ++ "MQswCQYDVQQGEwJVUzEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMSUwIwYDVQQD\n" ++ "DBxTU0wuY29tIFRMUyBSU0EgUm9vdCBDQSAyMDIyMB4XDTIyMDgyNTE2MzQyMloX\n" ++ "DTQ2MDgxOTE2MzQyMVowTjELMAkGA1UEBhMCVVMxGDAWBgNVBAoMD1NTTCBDb3Jw\n" ++ "b3JhdGlvbjElMCMGA1UEAwwcU1NMLmNvbSBUTFMgUlNBIFJvb3QgQ0EgMjAyMjCC\n" ++ "AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANCkCXJPQIgSYT41I57u9nTP\n" ++ "L3tYPc48DRAokC+X94xI2KDYJbFMsBFMF3NQ0CJKY7uB0ylu1bUJPiYYf7ISf5OY\n" ++ "t6/wNr/y7hienDtSxUcZXXTzZGbVXcdotL8bHAajvI9AI7YexoS9UcQbOcGV0ins\n" ++ "S657Lb85/bRi3pZ7QcacoOAGcvvwB5cJOYF0r/c0WRFXCsJbwST0MXMwgsadugL3\n" ++ "PnxEX4MN8/HdIGkWCVDi1FW24IBydm5MR7d1VVm0U3TZlMZBrViKMWYPHqIbKUBO\n" ++ "L9975hYsLfy/7PO0+r4Y9ptJ1O4Fbtk085zx7AGL0SDGD6C1vBdOSHtRwvzpXGk3\n" ++ "R2azaPgVKPC506QVzFpPulJwoxJF3ca6TvvC0PeoUidtbnm1jPx7jMEWTO6Af77w\n" ++ "dr5BUxIzrlo4QqvXDz5BjXYHMtWrifZOZ9mxQnUjbvPNQrL8VfVThxc7wDNY8VLS\n" ++ "+YCk8OjwO4s4zKTGkH8PnP2L0aPP2oOnaclQNtVcBdIKQXTbYxE3waWglksejBYS\n" ++ "d66UNHsef8JmAOSqg+qKkK3ONkRN0VHpvB/zagX9wHQfJRlAUW7qglFA35u5CCoG\n" ++ "AtUjHBPW6dvbxrB6y3snm/vg1UYk7RBLY0ulBY+6uB0rpvqR4pJSvezrZ5dtmi2f\n" ++ "gTIFZzL7SAg/2SW4BCUvAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0j\n" ++ "BBgwFoAU+y437uOEeicuzRk1sTN8/9REQrkwHQYDVR0OBBYEFPsuN+7jhHonLs0Z\n" ++ "NbEzfP/UREK5MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAjYlt\n" ++ "hEUY8U+zoO9opMAdrDC8Z2awms22qyIZZtM7QbUQnRC6cm4pJCAcAZli05bg4vsM\n" ++ "QtfhWsSWTVTNj8pDU/0quOr4ZcoBwq1gaAafORpR2eCNJvkLTqVTJXojpBzOCBvf\n" ++ "R4iyrT7gJ4eLSYwfqUdYe5byiB0YrrPRpgqU+tvT5TgKa3kSM/tKWTcWQA673vWJ\n" ++ "DPFs0/dRa1419dvAJuoSc06pkZCmF8NsLzjUo3KUQyxi4U5cMj29TH0ZR6LDSeeW\n" ++ "P4+a0zvkEdiLA9z2tmBVGKaBUfPhqBVq6+AL8BQx1rmMRTqoENjwuSfr98t67wVy\n" ++ "lrXEj5ZzxOhWc5y8aVFjvO9nHEMaX3cZHxj4HCUp+UmZKbaSPaKDN7EgkaibMOlq\n" ++ "bLQjk2UEqxHzDh1TJElTHaE/nUiSEeJ9DU/1172iWD54nR4fK/4huxoTtrEoZP2w\n" ++ "AgDHbICivRZQIA9ygV/MlP+7mea6kMvq+cYMwq7FGc4zoWtcu358NFcXrfA/rs3q\n" ++ "r5nsLFR+jM4uElZI7xc7P0peYNLcdDa8pUNjyw9bowJWCZ4kLOGGgYz+qxcs+sji\n" ++ "Mho6/4UIyYOf8kpIEFR3N+2ivEC+5BB09+Rbu7nzifmPQdjH5FCQNYA+HLhNkNPU\n" ++ "98OwoX6EyneSMSy4kLGCenROmxMmtNVQZlR4rmA=\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIDcjCCAlqgAwIBAgIUPopdB+xV0jLVt+O2XwHrLdzk1uQwDQYJKoZIhvcNAQEL\n" ++ "BQAwUTELMAkGA1UEBhMCUEwxKDAmBgNVBAoMH0tyYWpvd2EgSXpiYSBSb3psaWN6\n" ++ "ZW5pb3dhIFMuQS4xGDAWBgNVBAMMD1NaQUZJUiBST09UIENBMjAeFw0xNTEwMTkw\n" ++ "NzQzMzBaFw0zNTEwMTkwNzQzMzBaMFExCzAJBgNVBAYTAlBMMSgwJgYDVQQKDB9L\n" ++ "cmFqb3dhIEl6YmEgUm96bGljemVuaW93YSBTLkEuMRgwFgYDVQQDDA9TWkFGSVIg\n" ++ "Uk9PVCBDQTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC3vD5QqEvN\n" ++ "QLXOYeeWyrSh2gwisPq1e3YAd4wLz32ohswmUeQgPYUM1ljj5/QqGJ3a0a4m7utT\n" ++ "3PSQ1hNKDJA8w/Ta0o4NkjrcsbH/ON7Dui1fgLkCvUqdGw+0w8LBZwPd3BucPbOw\n" ++ "3gAeqDRHu5rr/gsUvTaE2g0gv/pby6kWIK05YO4vdbbnl5z5Pv1+TW9NL++IDWr6\n" ++ "3fE9biCloBK0TXC5ztdyO4mTp4CEHCdJckm1/zuVnsHMyAHs6A6KCpbns6aH5db5\n" ++ "BSsNl0BwPLqsdVqc1U2dAgrSS5tmS0YHF2Wtn2yIANwiieDhZNRnvDF5YTy7ykHN\n" ++ "XGoAyDw4jlivAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD\n" ++ "AgEGMB0GA1UdDgQWBBQuFqlKGLXLzPVvUPMjX/hd56zwyDANBgkqhkiG9w0BAQsF\n" ++ "AAOCAQEAtXP4A9xZWx126aMqe5Aosk3AM0+qmrHUuOQn/6mWmc5G4G18TKI4pAZw\n" ++ "8PRBEew/R40/cof5O/2kbytTAOD/OblqBw7rHRz2onKQy4I9EYKL0rufKq8h5mOG\n" ++ "nXkZ7/e7DDWQw4rtTw/1zBLZpD67oPwglV9PJi8RI4NOdQcPv5vRtB3pEAT+ymCP\n" ++ "oky4rc/hkA/NrgrHXXu3UNLUYfrVFdvXn4dRVOul4+vJhaAlIDf7js4MNIThPIGy\n" ++ "d05DpYhfhmehPea0XGG2Ptv+tyjFogeutcrKjSoS75ftwjCkySp6+/NNIxuZMzSg\n" ++ "LvWpCz/UXeHPhJ/iGcJfitYgHuNztw==\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIICOjCCAcGgAwIBAgIQQvLM2htpN0RfFf51KBC49DAKBggqhkjOPQQDAzBfMQsw\n" ++ "CQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1T\n" ++ "ZWN0aWdvIFB1YmxpYyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBFNDYwHhcN\n" ++ "MjEwMzIyMDAwMDAwWhcNNDYwMzIxMjM1OTU5WjBfMQswCQYDVQQGEwJHQjEYMBYG\n" ++ "A1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1TZWN0aWdvIFB1YmxpYyBT\n" ++ "ZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBFNDYwdjAQBgcqhkjOPQIBBgUrgQQA\n" ++ "IgNiAAR2+pmpbiDt+dd34wc7qNs9Xzjoq1WmVk/WSOrsfy2qw7LFeeyZYX8QeccC\n" ++ "WvkEN/U0NSt3zn8gj1KjAIns1aeibVvjS5KToID1AZTc8GgHHs3u/iVStSBDHBv+\n" ++ "6xnOQ6OjQjBAMB0GA1UdDgQWBBTRItpMWfFLXyY4qp3W7usNw/upYTAOBgNVHQ8B\n" ++ "Af8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNnADBkAjAn7qRa\n" ++ "qCG76UeXlImldCBteU/IvZNeWBj7LRoAasm4PdCkT0RHlAFWovgzJQxC36oCMB3q\n" ++ "4S6ILuH5px0CMk7yn2xVdOOurvulGu7t0vzCAxHrRVxgED1cf5kDW21USAGKcw==\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFijCCA3KgAwIBAgIQdY39i658BwD6qSWn4cetFDANBgkqhkiG9w0BAQwFADBf\n" ++ "MQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQD\n" ++ "Ey1TZWN0aWdvIFB1YmxpYyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBSNDYw\n" ++ "HhcNMjEwMzIyMDAwMDAwWhcNNDYwMzIxMjM1OTU5WjBfMQswCQYDVQQGEwJHQjEY\n" ++ "MBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1TZWN0aWdvIFB1Ymxp\n" ++ "YyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBSNDYwggIiMA0GCSqGSIb3DQEB\n" ++ "AQUAA4ICDwAwggIKAoICAQCTvtU2UnXYASOgHEdCSe5jtrch/cSV1UgrJnwUUxDa\n" ++ "ef0rty2k1Cz66jLdScK5vQ9IPXtamFSvnl0xdE8H/FAh3aTPaE8bEmNtJZlMKpnz\n" ++ "SDBh+oF8HqcIStw+KxwfGExxqjWMrfhu6DtK2eWUAtaJhBOqbchPM8xQljeSM9xf\n" ++ "iOefVNlI8JhD1mb9nxc4Q8UBUQvX4yMPFF1bFOdLvt30yNoDN9HWOaEhUTCDsG3X\n" ++ "ME6WW5HwcCSrv0WBZEMNvSE6Lzzpng3LILVCJ8zab5vuZDCQOc2TZYEhMbUjUDM3\n" ++ "IuM47fgxMMxF/mL50V0yeUKH32rMVhlATc6qu/m1dkmU8Sf4kaWD5QazYw6A3OAS\n" ++ "VYCmO2a0OYctyPDQ0RTp5A1NDvZdV3LFOxxHVp3i1fuBYYzMTYCQNFu31xR13NgE\n" ++ "SJ/AwSiItOkcyqex8Va3e0lMWeUgFaiEAin6OJRpmkkGj80feRQXEgyDet4fsZfu\n" ++ "+Zd4KKTIRJLpfSYFplhym3kT2BFfrsU4YjRosoYwjviQYZ4ybPUHNs2iTG7sijbt\n" ++ "8uaZFURww3y8nDnAtOFr94MlI1fZEoDlSfB1D++N6xybVCi0ITz8fAr/73trdf+L\n" ++ "HaAZBav6+CuBQug4urv7qv094PPK306Xlynt8xhW6aWWrL3DkJiy4Pmi1KZHQ3xt\n" ++ "zwIDAQABo0IwQDAdBgNVHQ4EFgQUVnNYZJX5khqwEioEYnmhQBWIIUkwDgYDVR0P\n" ++ "AQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAC9c\n" ++ "mTz8Bl6MlC5w6tIyMY208FHVvArzZJ8HXtXBc2hkeqK5Duj5XYUtqDdFqij0lgVQ\n" ++ "YKlJfp/imTYpE0RHap1VIDzYm/EDMrraQKFz6oOht0SmDpkBm+S8f74TlH7Kph52\n" ++ "gDY9hAaLMyZlbcp+nv4fjFg4exqDsQ+8FxG75gbMY/qB8oFM2gsQa6H61SilzwZA\n" ++ "Fv97fRheORKkU55+MkIQpiGRqRxOF3yEvJ+M0ejf5lG5Nkc/kLnHvALcWxxPDkjB\n" ++ "JYOcCj+esQMzEhonrPcibCTRAUH4WAP+JWgiH5paPHxsnnVI84HxZmduTILA7rpX\n" ++ "DhjvLpr3Etiga+kFpaHpaPi8TD8SHkXoUsCjvxInebnMMTzD9joiFgOgyY9mpFui\n" ++ "TdaBJQbpdqQACj7LzTWb4OE4y2BThihCQRxEV+ioratF4yUQvNs+ZUH7G6aXD+u5\n" ++ "dHn5HrwdVw1Hr8Mvn4dGp+smWg9WY7ViYG4A++MnESLn/pmPNPW56MORcr3Ywx65\n" ++ "LvKRRFHQV80MNNVIIb/bE/FmJUNS0nAiNs2fxBx1IK1jcmMGDw4nztJqDby1ORrp\n" ++ "0XZ60Vzk50lJLVU3aPAaOpg+VBeHVOmmJ1CJeyAvP/+/oYtKR5j/K3tJPsMpRmAY\n" ++ "QqszKbrAKbkTidOIijlBO8n9pu0f9GBj39ItVQGL\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIDbTCCAlWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJKUDEr\n" ++ "MCkGA1UEChMiSmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEcMBoG\n" ++ "A1UEAxMTU2VjdXJlU2lnbiBSb290Q0ExMTAeFw0wOTA0MDgwNDU2NDdaFw0yOTA0\n" ++ "MDgwNDU2NDdaMFgxCzAJBgNVBAYTAkpQMSswKQYDVQQKEyJKYXBhbiBDZXJ0aWZp\n" ++ "Y2F0aW9uIFNlcnZpY2VzLCBJbmMuMRwwGgYDVQQDExNTZWN1cmVTaWduIFJvb3RD\n" ++ "QTExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/XeqpRyQBTvLTJsz\n" ++ "i1oURaTnkBbR31fSIRCkF/3frNYfp+TbfPfs37gD2pRY/V1yfIw/XwFndBWW4wI8\n" ++ "h9uuywGOwvNmxoVF9ALGOrVisq/6nL+k5tSAMJjzDbaTj6nU2DbysPyKyiyhFTOV\n" ++ "MdrAG/LuYpmGYz+/3ZMqg6h2uRMft85OQoWPIucuGvKVCbIFtUROd6EgvanyTgp9\n" ++ "UK31BQ1FT0Zx/Sg+U/sE2C3XZR1KG/rPO7AxmjVuyIsG0wCR8pQIZUyxNAYAeoni\n" ++ "8McDWc/V1uinMrPmmECGxc0nEovMe863ETxiYAcjPitAbpSACW22s293bzUIUPsC\n" ++ "h8U+iQIDAQABo0IwQDAdBgNVHQ4EFgQUW/hNT7KlhtQ60vFjmqC+CfZXt94wDgYD\n" ++ "VR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEB\n" ++ "AKChOBZmLqdWHyGcBvod7bkixTgm2E5P7KN/ed5GIaGHd48HCJqypMWvDzKYC3xm\n" ++ "KbabfSVSSUOrTC4rbnpwrxYO4wJs+0LmGJ1F2FXI6Dvd5+H0LgscNFxsWEr7jIhQ\n" ++ "X5Ucv+2rIrVls4W6ng+4reV6G4pQOh29Dbx7VFALuUKvVaAYga1lme++5Jy/xIWr\n" ++ "QbJUb9wlze144o4MjQlJ3WN7WmmWAiGovVJZ6X01y8hSyn+B/tlr0/cR7SXf+Of5\n" ++ "pPpyl4RTDaXQMhhRdlkUbA/r7F+AjHVDg8OFmP9Mni0N5HeDk061lgeLKBObjBmN\n" ++ "QSdJQO7e5iNEOdyhIta6A/I=\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBI\n" ++ "MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x\n" ++ "FzAVBgNVBAMTDlNlY3VyZVRydXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIz\n" ++ "MTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAeBgNVBAoTF1NlY3VyZVRydXN0IENv\n" ++ "cnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCCASIwDQYJKoZIhvcN\n" ++ "AQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQXOZEz\n" ++ "Zum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO\n" ++ "0gMdA+9tDWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIao\n" ++ "wW8xQmxSPmjL8xk037uHGFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj\n" ++ "7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b01k/unK8RCSc43Oz969XL0Imnal0ugBS\n" ++ "8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmHursCAwEAAaOBnTCBmjAT\n" ++ "BgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB\n" ++ "/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCeg\n" ++ "JYYjaHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGC\n" ++ "NxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt3\n" ++ "6Z3q059c4EVlew3KW+JwULKUBRSuSceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/\n" ++ "3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHfmbx8IVQr5Fiiu1cprp6poxkm\n" ++ "D5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZnMUFdAvnZyPS\n" ++ "CPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR\n" ++ "3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE=\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBK\n" ++ "MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x\n" ++ "GTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkx\n" ++ "MjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3Qg\n" ++ "Q29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwggEiMA0GCSqG\n" ++ "SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jxYDiJ\n" ++ "iQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa\n" ++ "/FHtaMbQbqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJ\n" ++ "jnIFHovdRIWCQtBJwB1g8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnI\n" ++ "HmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYVHDGA76oYa8J719rO+TMg1fW9ajMtgQT7\n" ++ "sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi0XPnj3pDAgMBAAGjgZ0w\n" ++ "gZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQF\n" ++ "MAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCsw\n" ++ "KaAnoCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsG\n" ++ "AQQBgjcVAQQDAgEAMA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0L\n" ++ "URYD7xh8yOOvaliTFGCRsoTciE6+OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXO\n" ++ "H0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cnCDpOGR86p1hcF895P4vkp9Mm\n" ++ "I50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/53CYNv6ZHdAbY\n" ++ "iNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc\n" ++ "f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIICODCCAb6gAwIBAgIJANZdm7N4gS7rMAoGCCqGSM49BAMDMGExCzAJBgNVBAYT\n" ++ "AkpQMSUwIwYDVQQKExxTRUNPTSBUcnVzdCBTeXN0ZW1zIENPLixMVEQuMSswKQYD\n" ++ "VQQDEyJTZWN1cml0eSBDb21tdW5pY2F0aW9uIEVDQyBSb290Q0ExMB4XDTE2MDYx\n" ++ "NjA1MTUyOFoXDTM4MDExODA1MTUyOFowYTELMAkGA1UEBhMCSlAxJTAjBgNVBAoT\n" ++ "HFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xKzApBgNVBAMTIlNlY3VyaXR5\n" ++ "IENvbW11bmljYXRpb24gRUNDIFJvb3RDQTEwdjAQBgcqhkjOPQIBBgUrgQQAIgNi\n" ++ "AASkpW9gAwPDvTH00xecK4R1rOX9PVdu12O/5gSJko6BnOPpR27KkBLIE+Cnnfdl\n" ++ "dB9sELLo5OnvbYUymUSxXv3MdhDYW72ixvnWQuRXdtyQwjWpS4g8EkdtXP9JTxpK\n" ++ "ULGjQjBAMB0GA1UdDgQWBBSGHOf+LaVKiwj+KBH6vqNm+GBZLzAOBgNVHQ8BAf8E\n" ++ "BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjAVXUI9/Lbu\n" ++ "9zuxNuie9sRGKEkz0FhDKmMpzE2xtHqiuQ04pV1IKv3LsnNdo4gIxwwCMQDAqy0O\n" ++ "be0YottT6SXbVQjgUMzfRGEWgqtJsLKB7HOHeLRMsmIbEvoWTSVLY70eN9k=\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDEl\n" ++ "MCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMe\n" ++ "U2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoX\n" ++ "DTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRy\n" ++ "dXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3VyaXR5IENvbW11bmlj\n" ++ "YXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANAV\n" ++ "OVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGr\n" ++ "zbl+dp+++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVM\n" ++ "VAX3NuRFg3sUZdbcDE3R3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQ\n" ++ "hNBqyjoGADdH5H5XTz+L62e4iKrFvlNVspHEfbmwhRkGeC7bYRr6hfVKkaHnFtWO\n" ++ "ojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1KEOtOghY6rCcMU/Gt1SSw\n" ++ "awNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8QIH4D5cs\n" ++ "OPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3\n" ++ "DQEBCwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpF\n" ++ "coJxDjrSzG+ntKEju/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXc\n" ++ "okgfGT+Ok+vx+hfuzU7jBBJV1uXk3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8\n" ++ "t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6qtnRGEmyR7jTV7JqR50S+kDFy\n" ++ "1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29mvVXIwAHIRc/\n" ++ "SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFfzCCA2egAwIBAgIJAOF8N0D9G/5nMA0GCSqGSIb3DQEBDAUAMF0xCzAJBgNV\n" ++ "BAYTAkpQMSUwIwYDVQQKExxTRUNPTSBUcnVzdCBTeXN0ZW1zIENPLixMVEQuMScw\n" ++ "JQYDVQQDEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTMwHhcNMTYwNjE2\n" ++ "MDYxNzE2WhcNMzgwMTE4MDYxNzE2WjBdMQswCQYDVQQGEwJKUDElMCMGA1UEChMc\n" ++ "U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UEAxMeU2VjdXJpdHkg\n" ++ "Q29tbXVuaWNhdGlvbiBSb290Q0EzMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC\n" ++ "CgKCAgEA48lySfcw3gl8qUCBWNO0Ot26YQ+TUG5pPDXC7ltzkBtnTCHsXzW7OT4r\n" ++ "CmDvu20rhvtxosis5FaU+cmvsXLUIKx00rgVrVH+hXShuRD+BYD5UpOzQD11EKzA\n" ++ "lrenfna84xtSGc4RHwsENPXY9Wk8d/Nk9A2qhd7gCVAEF5aEt8iKvE1y/By7z/MG\n" ++ "TfmfZPd+pmaGNXHIEYBMwXFAWB6+oHP2/D5Q4eAvJj1+XCO1eXDe+uDRpdYMQXF7\n" ++ "9+qMHIjH7Iv10S9VlkZ8WjtYO/u62C21Jdp6Ts9EriGmnpjKIG58u4iFW/vAEGK7\n" ++ "8vknR+/RiTlDxN/e4UG/VHMgly1s2vPUB6PmudhvrvyMGS7TZ2crldtYXLVqAvO4\n" ++ "g160a75BflcJdURQVc1aEWEhCmHCqYj9E7wtiS/NYeCVvsq1e+F7NGcLH7YMx3we\n" ++ "GVPKp7FKFSBWFHA9K4IsD50VHUeAR/94mQ4xr28+j+2GaR57GIgUssL8gjMunEst\n" ++ "+3A7caoreyYn8xrC3PsXuKHqy6C0rtOUfnrQq8PsOC0RLoi/1D+tEjtCrI8Cbn3M\n" ++ "0V9hvqG8OmpI6iZVIhZdXw3/JzOfGAN0iltSIEdrRU0id4xVJ/CvHozJgyJUt5rQ\n" ++ "T9nO/NkuHJYosQLTA70lUhw0Zk8jq/R3gpYd0VcwCBEF/VfR2ccCAwEAAaNCMEAw\n" ++ "HQYDVR0OBBYEFGQUfPxYchamCik0FW8qy7z8r6irMA4GA1UdDwEB/wQEAwIBBjAP\n" ++ "BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBDAUAA4ICAQDcAiMI4u8hOscNtybS\n" ++ "YpOnpSNyByCCYN8Y11StaSWSntkUz5m5UoHPrmyKO1o5yGwBQ8IibQLwYs1OY0PA\n" ++ "FNr0Y/Dq9HHuTofjcan0yVflLl8cebsjqodEV+m9NU1Bu0soo5iyG9kLFwfl9+qd\n" ++ "9XbXv8S2gVj/yP9kaWJ5rW4OH3/uHWnlt3Jxs/6lATWUVCvAUm2PVcTJ0rjLyjQI\n" ++ "UYWg9by0F1jqClx6vWPGOi//lkkZhOpn2ASxYfQAW0q3nHE3GYV5v4GwxxMOdnE+\n" ++ "OoAGrgYWp421wsTL/0ClXI2lyTrtcoHKXJg80jQDdwj98ClZXSEIx2C/pHF7uNke\n" ++ "gr4Jr2VvKKu/S7XuPghHJ6APbw+LP6yVGPO5DtxnVW5inkYO0QR4ynKudtml+LLf\n" ++ "iAlhi+8kTtFZP1rUPcmTPCtk9YENFpb3ksP+MW/oKjJ0DvRMmEoYDjBU1cXrvMUV\n" ++ "nuiZIesnKwkK2/HmcBhWuwzkvvnoEKQTkrgc4NtnHVMDpCKn3F2SEDzq//wbEBrD\n" ++ "2NCcnWXL0CsnMQMeNuE9dnUM/0Umud1RvCPHX9jYhxBAEg09ODfnRDwYwFMJZI//\n" ++ "1ZqmfHAuc1Uh6N//g7kdPjIe1qZ9LPFm6Vwdp6POXiUyK+OVrCoHzrQoeIY8Laad\n" ++ "TdJ0MN1kURXbg4NR16/9M51NZg==\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl\n" ++ "MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp\n" ++ "U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw\n" ++ "NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE\n" ++ "ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp\n" ++ "ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3\n" ++ "DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf\n" ++ "8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN\n" ++ "+lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0\n" ++ "X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa\n" ++ "K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA\n" ++ "1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G\n" ++ "A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR\n" ++ "zt0fhvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0\n" ++ "YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD\n" ++ "bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w\n" ++ "DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3\n" ++ "L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D\n" ++ "eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl\n" ++ "xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp\n" ++ "VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY\n" ++ "WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q=\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx\n" ++ "EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT\n" ++ "HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs\n" ++ "ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw\n" ++ "MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6\n" ++ "b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj\n" ++ "aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp\n" ++ "Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\n" ++ "ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg\n" ++ "nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1\n" ++ "HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N\n" ++ "Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN\n" ++ "dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0\n" ++ "HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO\n" ++ "BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G\n" ++ "CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU\n" ++ "sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3\n" ++ "4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg\n" ++ "8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K\n" ++ "pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1\n" ++ "mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMx\n" ++ "EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT\n" ++ "HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVs\n" ++ "ZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5\n" ++ "MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNVBAYTAlVTMRAwDgYD\n" ++ "VQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFy\n" ++ "ZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2Vy\n" ++ "dmljZXMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI\n" ++ "hvcNAQEBBQADggEPADCCAQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20p\n" ++ "OsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm2\n" ++ "8xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4PahHQUw2eeBGg6345AWh1K\n" ++ "Ts9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLPLJGmpufe\n" ++ "hRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk\n" ++ "6mFBrMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAw\n" ++ "DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+q\n" ++ "AdcwKziIorhtSpzyEZGDMA0GCSqGSIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMI\n" ++ "bw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPPE95Dz+I0swSdHynVv/heyNXB\n" ++ "ve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTyxQGjhdByPq1z\n" ++ "qwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd\n" ++ "iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn\n" ++ "0q23KXB56jzaYyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCN\n" ++ "sSi6\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFujCCA6KgAwIBAgIJALtAHEP1Xk+wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV\n" ++ "BAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2ln\n" ++ "biBHb2xkIENBIC0gRzIwHhcNMDYxMDI1MDgzMDM1WhcNMzYxMDI1MDgzMDM1WjBF\n" ++ "MQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dpc3NTaWduIEFHMR8wHQYDVQQDExZT\n" ++ "d2lzc1NpZ24gR29sZCBDQSAtIEcyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC\n" ++ "CgKCAgEAr+TufoskDhJuqVAtFkQ7kpJcyrhdhJJCEyq8ZVeCQD5XJM1QiyUqt2/8\n" ++ "76LQwB8CJEoTlo8jE+YoWACjR8cGp4QjK7u9lit/VcyLwVcfDmJlD909Vopz2q5+\n" ++ "bbqBHH5CjCA12UNNhPqE21Is8w4ndwtrvxEvcnifLtg+5hg3Wipy+dpikJKVyh+c\n" ++ "6bM8K8vzARO/Ws/BtQpgvd21mWRTuKCWs2/iJneRjOBiEAKfNA+k1ZIzUd6+jbqE\n" ++ "emA8atufK+ze3gE/bk3lUIbLtK/tREDFylqM2tIrfKjuvqblCqoOpd8FUrdVxyJd\n" ++ "MmqXl2MT28nbeTZ7hTpKxVKJ+STnnXepgv9VHKVxaSvRAiTysybUa9oEVeXBCsdt\n" ++ "MDeQKuSeFDNeFhdVxVu1yzSJkvGdJo+hB9TGsnhQ2wwMC3wLjEHXuendjIj3o02y\n" ++ "MszYF9rNt85mndT9Xv+9lz4pded+p2JYryU0pUHHPbwNUMoDAw8IWh+Vc3hiv69y\n" ++ "FGkOpeUDDniOJihC8AcLYiAQZzlG+qkDzAQ4embvIIO1jEpWjpEA/I5cgt6IoMPi\n" ++ "aG59je883WX0XaxR7ySArqpWl2/5rX3aYT+YdzylkbYcjCbaZaIJbcHiVOO5ykxM\n" ++ "gI93e2CaHt+28kgeDrpOVG2Y4OGiGqJ3UM/EY5LsRxmd6+ZrzsECAwEAAaOBrDCB\n" ++ "qTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUWyV7\n" ++ "lqRlUX64OfPAeGZe6Drn8O4wHwYDVR0jBBgwFoAUWyV7lqRlUX64OfPAeGZe6Drn\n" ++ "8O4wRgYDVR0gBD8wPTA7BglghXQBWQECAQEwLjAsBggrBgEFBQcCARYgaHR0cDov\n" ++ "L3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBACe6\n" ++ "45R88a7A3hfm5djV9VSwg/S7zV4Fe0+fdWavPOhWfvxyeDgD2StiGwC5+OlgzczO\n" ++ "UYrHUDFu4Up+GC9pWbY9ZIEr44OE5iKHjn3g7gKZYbge9LgriBIWhMIxkziWMaa5\n" ++ "O1M/wySTVltpkuzFwbs4AOPsF6m43Md8AYOfMke6UiI0HTJ6CVanfCU2qT1L2sCC\n" ++ "bwq7EsiHSycR+R4tx5M/nttfJmtS2S6K8RTGRI0Vqbe/vd6mGu6uLftIdxf+u+yv\n" ++ "GPUqUfA5hJeVbG4bwyvEdGB5JbAKJ9/fXtI5z0V9QkvfsywexcZdylU6oJxpmo/a\n" ++ "77KwPJ+HbBIrZXAVUjEaJM9vMSNQH4xPjyPDdEFjHFWoFN0+4FFQz/EbMFYOkrCC\n" ++ "hdiDyyJkvC24JdVUorgG6q2SpCSgwYa1ShNqR88uC1aVVMvOmttqtKay20EIhid3\n" ++ "92qgQmwLOM7XdVAyksLfKzAiSNDVQTglXaTpXZ/GlHXQRf0wl0OPkKsKx4ZzYEpp\n" ++ "Ld6leNcG2mqeSz53OiATIgHQv2ieY2BrNU0LbbqhPcCT4H8js1WtciVORvnSFu+w\n" ++ "ZMEBnunKoGqYDs/YYPIvSbjkQuE4NRb0yG5P94FW6LqjviOvrv1vA+ACOzB2+htt\n" ++ "Qc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFvTCCA6WgAwIBAgIITxvUL1S7L0swDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UE\n" ++ "BhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWdu\n" ++ "IFNpbHZlciBDQSAtIEcyMB4XDTA2MTAyNTA4MzI0NloXDTM2MTAyNTA4MzI0Nlow\n" ++ "RzELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMY\n" ++ "U3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A\n" ++ "MIICCgKCAgEAxPGHf9N4Mfc4yfjDmUO8x/e8N+dOcbpLj6VzHVxumK4DV644N0Mv\n" ++ "Fz0fyM5oEMF4rhkDKxD6LHmD9ui5aLlV8gREpzn5/ASLHvGiTSf5YXu6t+WiE7br\n" ++ "YT7QbNHm+/pe7R20nqA1W6GSy/BJkv6FCgU+5tkL4k+73JU3/JHpMjUi0R86TieF\n" ++ "nbAVlDLaYQ1HTWBCrpJH6INaUFjpiou5XaHc3ZlKHzZnu0jkg7Y360g6rw9njxcH\n" ++ "6ATK72oxh9TAtvmUcXtnZLi2kUpCe2UuMGoM9ZDulebyzYLs2aFK7PayS+VFheZt\n" ++ "eJMELpyCbTapxDFkH4aDCyr0NQp4yVXPQbBH6TCfmb5hqAaEuSh6XzjZG6k4sIN/\n" ++ "c8HDO0gqgg8hm7jMqDXDhBuDsz6+pJVpATqJAHgE2cn0mRmrVn5bi4Y5FZGkECwJ\n" ++ "MoBgs5PAKrYYC51+jUnyEEp/+dVGLxmSo5mnJqy7jDzmDrxHB9xzUfFwZC8I+bRH\n" ++ "HTBsROopN4WSaGa8gzj+ezku01DwH/teYLappvonQfGbGHLy9YR0SslnxFSuSGTf\n" ++ "jNFusB3hB48IHpmccelM2KX3RxIfdNFRnobzwqIjQAtz20um53MGjMGg6cFZrEb6\n" ++ "5i/4z3GcRm25xBWNOHkDRUjvxF3XCO6HOSKGsg0PWEP3calILv3q1h8CAwEAAaOB\n" ++ "rDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU\n" ++ "F6DNweRBtjpbO8tFnb0cwpj6hlgwHwYDVR0jBBgwFoAUF6DNweRBtjpbO8tFnb0c\n" ++ "wpj6hlgwRgYDVR0gBD8wPTA7BglghXQBWQEDAQEwLjAsBggrBgEFBQcCARYgaHR0\n" ++ "cDovL3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIB\n" ++ "AHPGgeAn0i0P4JUw4ppBf1AsX19iYamGamkYDHRJ1l2E6kFSGG9YrVBWIGrGvShp\n" ++ "WJHckRE1qTodvBqlYJ7YH39FkWnZfrt4csEGDyrOj4VwYaygzQu4OSlWhDJOhrs9\n" ++ "xCrZ1x9y7v5RoSJBsXECYxqCsGKrXlcSH9/L3XWgwF15kIwb4FDm3jH+mHtwX6WQ\n" ++ "2K34ArZv02DdQEsixT2tOnqfGhpHkXkzuoLcMmkDlm4fS/Bx/uNncqCxv1yL5PqZ\n" ++ "IseEuRuNI5c/7SXgz2W79WEE790eslpBIlqhn10s6FvJbakMDHiqYMZWjwFaDGi8\n" ++ "aRl5xB9+lwW/xekkUV7U1UtT7dkjWjYDZaPBA61BMPNGG4WQr2W11bHkFlt4dR2X\n" ++ "em1ZqSqPe97Dh4kQmUlzeMg9vVE1dCrV8X5pGyq7O70luJpaPXJhkGaH7gzWTdQR\n" ++ "dAtq/gsD/KNVV4n+SsuuWxcFyPKNIzFTONItaj+CuY0IavdeQXRuwxF+B6wpYJE/\n" ++ "OMpXEA29MC/HpeZBoNquBYeaoKRlbEwJDIm6uNO5wJOKMPqN5ZprFQFOZ6raYlY+\n" ++ "hAhm0sQ2fac+EPyI4NSA5QC9qvNOBqN6avlicuMJT+ubDgEj8Z+7fNzcbBGXJbLy\n" ++ "tGMU0gYqZ4yD9c7qB9iaah7s5Aq7KkzrCWA5zspi2C5u\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx\n" ++ "KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd\n" ++ "BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl\n" ++ "YyBHbG9iYWxSb290IENsYXNzIDIwHhcNMDgxMDAxMTA0MDE0WhcNMzMxMDAxMjM1\n" ++ "OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy\n" ++ "aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50\n" ++ "ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwggEiMA0G\n" ++ "CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl82hVYAUd\n" ++ "AqSzm1nzHoqvNK38DcLZSBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiC\n" ++ "FoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/FvudocP05l03Sx5iRUKrERLMjfTlH6VJi\n" ++ "1hKTXrcxlkIF+3anHqP1wvzpesVsqXFP6st4vGCvx9702cu+fjOlbpSD8DT6Iavq\n" ++ "jnKgP6TeMFvvhk1qlVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGVWOHAD3bZ\n" ++ "wI18gfNycJ5v/hqO2V81xrJvNHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGj\n" ++ "QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/\n" ++ "WSA2AHmgoCJrjNXyYdK4LMuCSjANBgkqhkiG9w0BAQsFAAOCAQEAMQOiYQsfdOhy\n" ++ "NsZt+U2e+iKo4YFWz827n+qrkRk4r6p8FU3ztqONpfSO9kSpp+ghla0+AGIWiPAC\n" ++ "uvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNfvNoBYimipidx5joifsFvHZVw\n" ++ "IEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR3p1m0IvVVGb6\n" ++ "g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN\n" ++ "9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlP\n" ++ "BSeOE6Fuwg==\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx\n" ++ "KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd\n" ++ "BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl\n" ++ "YyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgxMDAxMTAyOTU2WhcNMzMxMDAxMjM1\n" ++ "OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy\n" ++ "aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50\n" ++ "ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0G\n" ++ "CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN\n" ++ "8ELg63iIVl6bmlQdTQyK9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/\n" ++ "RLyTPWGrTs0NvvAgJ1gORH8EGoel15YUNpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4\n" ++ "hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZFiP0Zf3WHHx+xGwpzJFu5\n" ++ "ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W0eDrXltM\n" ++ "EnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGj\n" ++ "QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1\n" ++ "A/d2O2GCahKqGFPrAyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOy\n" ++ "WL6ukK2YJ5f+AbGwUgC4TeQbIXQbfsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ\n" ++ "1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzTucpH9sry9uetuUg/vBa3wW30\n" ++ "6gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7hP0HHRwA11fXT\n" ++ "91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml\n" ++ "e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4p\n" ++ "TpPDpFQUWw==\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIEYzCCA0ugAwIBAgIBATANBgkqhkiG9w0BAQsFADCB0jELMAkGA1UEBhMCVFIx\n" ++ "GDAWBgNVBAcTD0dlYnplIC0gS29jYWVsaTFCMEAGA1UEChM5VHVya2l5ZSBCaWxp\n" ++ "bXNlbCB2ZSBUZWtub2xvamlrIEFyYXN0aXJtYSBLdXJ1bXUgLSBUVUJJVEFLMS0w\n" ++ "KwYDVQQLEyRLYW11IFNlcnRpZmlrYXN5b24gTWVya2V6aSAtIEthbXUgU00xNjA0\n" ++ "BgNVBAMTLVRVQklUQUsgS2FtdSBTTSBTU0wgS29rIFNlcnRpZmlrYXNpIC0gU3Vy\n" ++ "dW0gMTAeFw0xMzExMjUwODI1NTVaFw00MzEwMjUwODI1NTVaMIHSMQswCQYDVQQG\n" ++ "EwJUUjEYMBYGA1UEBxMPR2ViemUgLSBLb2NhZWxpMUIwQAYDVQQKEzlUdXJraXll\n" ++ "IEJpbGltc2VsIHZlIFRla25vbG9qaWsgQXJhc3Rpcm1hIEt1cnVtdSAtIFRVQklU\n" ++ "QUsxLTArBgNVBAsTJEthbXUgU2VydGlmaWthc3lvbiBNZXJrZXppIC0gS2FtdSBT\n" ++ "TTE2MDQGA1UEAxMtVFVCSVRBSyBLYW11IFNNIFNTTCBLb2sgU2VydGlmaWthc2kg\n" ++ "LSBTdXJ1bSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr3UwM6q7\n" ++ "a9OZLBI3hNmNe5eA027n/5tQlT6QlVZC1xl8JoSNkvoBHToP4mQ4t4y86Ij5iySr\n" ++ "LqP1N+RAjhgleYN1Hzv/bKjFxlb4tO2KRKOrbEz8HdDc72i9z+SqzvBV96I01INr\n" ++ "N3wcwv61A+xXzry0tcXtAA9TNypN9E8Mg/uGz8v+jE69h/mniyFXnHrfA2eJLJ2X\n" ++ "YacQuFWQfw4tJzh03+f92k4S400VIgLI4OD8D62K18lUUMw7D8oWgITQUVbDjlZ/\n" ++ "iSIzL+aFCr2lqBs23tPcLG07xxO9WSMs5uWk99gL7eqQQESolbuT1dCANLZGeA4f\n" ++ "AJNG4e7p+exPFwIDAQABo0IwQDAdBgNVHQ4EFgQUZT/HiobGPN08VFw1+DrtUgxH\n" ++ "V8gwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL\n" ++ "BQADggEBACo/4fEyjq7hmFxLXs9rHmoJ0iKpEsdeV31zVmSAhHqT5Am5EM2fKifh\n" ++ "AHe+SMg1qIGf5LgsyX8OsNJLN13qudULXjS99HMpw+0mFZx+CFOKWI3QSyjfwbPf\n" ++ "IPP54+M638yclNhOT8NrF7f3cuitZjO1JVOr4PhMqZ398g26rrnZqsZr+ZO7rqu4\n" ++ "lzwDGrpDxpa5RXI4s6ehlj2Re37AIVNMh+3yC1SVUZPVIqUNivGTDj5UDrDYyU7c\n" ++ "8jEyVupk+eq1nRZmQnLzf9OxMUP8pI4X8W0jq5Rm+K37DwhuJi1/FwcJsoz7UMCf\n" ++ "lo3Ptv0AnVoUmr8CRPXBwp8iXqIPoeM=\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcx\n" ++ "EjAQBgNVBAoTCVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMT\n" ++ "VFdDQSBHbG9iYWwgUm9vdCBDQTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5\n" ++ "NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQKEwlUQUlXQU4tQ0ExEDAOBgNVBAsT\n" ++ "B1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3QgQ0EwggIiMA0GCSqG\n" ++ "SIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2CnJfF\n" ++ "10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz\n" ++ "0ALfUPZVr2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfCh\n" ++ "MBwqoJimFb3u/Rk28OKRQ4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbH\n" ++ "zIh1HrtsBv+baz4X7GGqcXzGHaL3SekVtTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc\n" ++ "46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1WKKD+u4ZqyPpcC1jcxkt2\n" ++ "yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99sy2sbZCi\n" ++ "laLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYP\n" ++ "oA/pyJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQA\n" ++ "BDzfuBSO6N+pjWxnkjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcE\n" ++ "qYSjMq+u7msXi7Kx/mzhkIyIqJdIzshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm\n" ++ "4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB\n" ++ "/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6gcFGn90xHNcgL\n" ++ "1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn\n" ++ "LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WF\n" ++ "H6vPNOw/KP4M8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNo\n" ++ "RI2T9GRwoD2dKAXDOXC4Ynsg/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+\n" ++ "nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlglPx4mI88k1HtQJAH32RjJMtOcQWh\n" ++ "15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryPA9gK8kxkRr05YuWW\n" ++ "6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3mi4TW\n" ++ "nsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5j\n" ++ "wa19hAM8EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWz\n" ++ "aGHQRiapIVJpLesux+t3zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmy\n" ++ "KwbQBM0=\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzES\n" ++ "MBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFU\n" ++ "V0NBIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMz\n" ++ "WhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJVEFJV0FO\n" ++ "LUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlm\n" ++ "aWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\n" ++ "AQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFE\n" ++ "AcK0HMMxQhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HH\n" ++ "K3XLfJ+utdGdIzdjp9xCoi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeX\n" ++ "RfwZVzsrb+RH9JlF/h3x+JejiB03HFyP4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/z\n" ++ "rX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1ry+UPizgN7gr8/g+YnzAx\n" ++ "3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV\n" ++ "HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkq\n" ++ "hkiG9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeC\n" ++ "MErJk/9q56YAf4lCmtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdls\n" ++ "XebQ79NqZp4VKIV66IIArB6nCWlWQtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62D\n" ++ "lhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVYT0bf+215WfKEIlKuD8z7fDvn\n" ++ "aspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocnyYh0igzyXxfkZ\n" ++ "YiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw==\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIICQjCCAcmgAwIBAgIQNjqWjMlcsljN0AFdxeVXADAKBggqhkjOPQQDAzBjMQsw\n" ++ "CQYDVQQGEwJERTEnMCUGA1UECgweRGV1dHNjaGUgVGVsZWtvbSBTZWN1cml0eSBH\n" ++ "bWJIMSswKQYDVQQDDCJUZWxla29tIFNlY3VyaXR5IFRMUyBFQ0MgUm9vdCAyMDIw\n" ++ "MB4XDTIwMDgyNTA3NDgyMFoXDTQ1MDgyNTIzNTk1OVowYzELMAkGA1UEBhMCREUx\n" ++ "JzAlBgNVBAoMHkRldXRzY2hlIFRlbGVrb20gU2VjdXJpdHkgR21iSDErMCkGA1UE\n" ++ "AwwiVGVsZWtvbSBTZWN1cml0eSBUTFMgRUNDIFJvb3QgMjAyMDB2MBAGByqGSM49\n" ++ "AgEGBSuBBAAiA2IABM6//leov9Wq9xCazbzREaK9Z0LMkOsVGJDZos0MKiXrPk/O\n" ++ "tdKPD/M12kOLAoC+b1EkHQ9rK8qfwm9QMuU3ILYg/4gND21Ju9sGpIeQkpT0CdDP\n" ++ "f8iAC8GXs7s1J8nCG6NCMEAwHQYDVR0OBBYEFONyzG6VmUex5rNhTNHLq+O6zd6f\n" ++ "MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2cA\n" ++ "MGQCMHVSi7ekEE+uShCLsoRbQuHmKjYC2qBuGT8lv9pZMo7k+5Dck2TOrbRBR2Di\n" ++ "z6fLHgIwN0GMZt9Ba9aDAEH9L1r3ULRn0SyocddDypwnJJGDSA3PzfdUga/sf+Rn\n" ++ "27iQ7t0l\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFszCCA5ugAwIBAgIQIZxULej27HF3+k7ow3BXlzANBgkqhkiG9w0BAQwFADBj\n" ++ "MQswCQYDVQQGEwJERTEnMCUGA1UECgweRGV1dHNjaGUgVGVsZWtvbSBTZWN1cml0\n" ++ "eSBHbWJIMSswKQYDVQQDDCJUZWxla29tIFNlY3VyaXR5IFRMUyBSU0EgUm9vdCAy\n" ++ "MDIzMB4XDTIzMDMyODEyMTY0NVoXDTQ4MDMyNzIzNTk1OVowYzELMAkGA1UEBhMC\n" ++ "REUxJzAlBgNVBAoMHkRldXRzY2hlIFRlbGVrb20gU2VjdXJpdHkgR21iSDErMCkG\n" ++ "A1UEAwwiVGVsZWtvbSBTZWN1cml0eSBUTFMgUlNBIFJvb3QgMjAyMzCCAiIwDQYJ\n" ++ "KoZIhvcNAQEBBQADggIPADCCAgoCggIBAO01oYGA88tKaVvC+1GDrib94W7zgRJ9\n" ++ "cUD/h3VCKSHtgVIs3xLBGYSJwb3FKNXVS2xE1kzbB5ZKVXrKNoIENqil/Cf2SfHV\n" ++ "cp6R+SPWcHu79ZvB7JPPGeplfohwoHP89v+1VmLhc2o0mD6CuKyVU/QBoCcHcqMA\n" ++ "U6DksquDOFczJZSfvkgdmOGjup5czQRxUX11eKvzWarE4GC+j4NSuHUaQTXtvPM6\n" ++ "Y+mpFEXX5lLRbtLevOP1Czvm4MS9Q2QTps70mDdsipWol8hHD/BeEIvnHRz+sTug\n" ++ "BTNoBUGCwQMrAcjnj02r6LX2zWtEtefdi+zqJbQAIldNsLGyMcEWzv/9FIS3R/qy\n" ++ "8XDe24tsNlikfLMR0cN3f1+2JeANxdKz+bi4d9s3cXFH42AYTyS2dTd4uaNir73J\n" ++ "co4vzLuu2+QVUhkHM/tqty1LkCiCc/4YizWN26cEar7qwU02OxY2kTLvtkCJkUPg\n" ++ "8qKrBC7m8kwOFjQgrIfBLX7JZkcXFBGk8/ehJImr2BrIoVyxo/eMbcgByU/J7MT8\n" ++ "rFEz0ciD0cmfHdRHNCk+y7AO+oMLKFjlKdw/fKifybYKu6boRhYPluV75Gp6SG12\n" ++ "mAWl3G0eQh5C2hrgUve1g8Aae3g1LDj1H/1Joy7SWWO/gLCMk3PLNaaZlSJhZQNg\n" ++ "+y+TS/qanIA7AgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUtqeX\n" ++ "gj10hZv3PJ+TmpV5dVKMbUcwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBS2\n" ++ "p5eCPXSFm/c8n5OalXl1UoxtRzANBgkqhkiG9w0BAQwFAAOCAgEAqMxhpr51nhVQ\n" ++ "pGv7qHBFfLp+sVr8WyP6Cnf4mHGCDG3gXkaqk/QeoMPhk9tLrbKmXauw1GLLXrtm\n" ++ "9S3ul0A8Yute1hTWjOKWi0FpkzXmuZlrYrShF2Y0pmtjxrlO8iLpWA1WQdH6DErw\n" ++ "M807u20hOq6OcrXDSvvpfeWxm4bu4uB9tPcy/SKE8YXJN3nptT+/XOR0so8RYgDd\n" ++ "GGah2XsjX/GO1WfoVNpbOms2b/mBsTNHM3dA+VKq3dSDz4V4mZqTuXNnQkYRIer+\n" ++ "CqkbGmVps4+uFrb2S1ayLfmlyOw7YqPta9BO1UAJpB+Y1zqlklkg5LB9zVtzaL1t\n" ++ "xKITDmcZuI1CfmwMmm6gJC3VRRvcxAIU/oVbZZfKTpBQCHpCNfnqwmbU+AGuHrS+\n" ++ "w6jv/naaoqYfRvaE7fzbzsQCzndILIyy7MMAo+wsVRjBfhnu4S/yrYObnqsZ38aK\n" ++ "L4x35bcF7DvB7L6Gs4a8wPfc5+pbrrLMtTWGS9DiP7bY+A4A7l3j941Y/8+LN+lj\n" ++ "X273CXE2whJdV/LItM3z7gLfEdxquVeEHVlNjM7IDiPCtyaaEBRx/pOyiriA8A4Q\n" ++ "ntOoUAw3gi/q4Iqd4Sw5/7W0cwDk90imc6y/st53BIe0o82bNSQ3+pCTE4FCxpgm\n" ++ "dTdmQRCsu/WU48IxK63nI1bMNSWSs1A=\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFODCCAyCgAwIBAgIRAJW+FqD3LkbxezmCcvqLzZYwDQYJKoZIhvcNAQEFBQAw\n" ++ "NzEUMBIGA1UECgwLVGVsaWFTb25lcmExHzAdBgNVBAMMFlRlbGlhU29uZXJhIFJv\n" ++ "b3QgQ0EgdjEwHhcNMDcxMDE4MTIwMDUwWhcNMzIxMDE4MTIwMDUwWjA3MRQwEgYD\n" ++ "VQQKDAtUZWxpYVNvbmVyYTEfMB0GA1UEAwwWVGVsaWFTb25lcmEgUm9vdCBDQSB2\n" ++ "MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMK+6yfwIaPzaSZVfp3F\n" ++ "VRaRXP3vIb9TgHot0pGMYzHw7CTww6XScnwQbfQ3t+XmfHnqjLWCi65ItqwA3GV1\n" ++ "7CpNX8GH9SBlK4GoRz6JI5UwFpB/6FcHSOcZrr9FZ7E3GwYq/t75rH2D+1665I+X\n" ++ "Z75Ljo1kB1c4VWk0Nj0TSO9P4tNmHqTPGrdeNjPUtAa9GAH9d4RQAEX1jF3oI7x+\n" ++ "/jXh7VB7qTCNGdMJjmhnXb88lxhTuylixcpecsHHltTbLaC0H2kD7OriUPEMPPCs\n" ++ "81Mt8Bz17Ww5OXOAFshSsCPN4D7c3TxHoLs1iuKYaIu+5b9y7tL6pe0S7fyYGKkm\n" ++ "dtwoSxAgHNN/Fnct7W+A90m7UwW7XWjH1Mh1Fj+JWov3F0fUTPHSiXk+TT2YqGHe\n" ++ "Oh7S+F4D4MHJHIzTjU3TlTazN19jY5szFPAtJmtTfImMMsJu7D0hADnJoWjiUIMu\n" ++ "sDor8zagrC/kb2HCUQk5PotTubtn2txTuXZZNp1D5SDgPTJghSJRt8czu90VL6R4\n" ++ "pgd7gUY2BIbdeTXHlSw7sKMXNeVzH7RcWe/a6hBle3rQf5+ztCo3O3CLm1u5K7fs\n" ++ "slESl1MpWtTwEhDcTwK7EpIvYtQ/aUN8Ddb8WHUBiJ1YFkveupD/RwGJBmr2X7KQ\n" ++ "arMCpgKIv7NHfirZ1fpoeDVNAgMBAAGjPzA9MA8GA1UdEwEB/wQFMAMBAf8wCwYD\n" ++ "VR0PBAQDAgEGMB0GA1UdDgQWBBTwj1k4ALP1j5qWDNXr+nuqF+gTEjANBgkqhkiG\n" ++ "9w0BAQUFAAOCAgEAvuRcYk4k9AwI//DTDGjkk0kiP0Qnb7tt3oNmzqjMDfz1mgbl\n" ++ "dxSR651Be5kqhOX//CHBXfDkH1e3damhXwIm/9fH907eT/j3HEbAek9ALCI18Bmx\n" ++ "0GtnLLCo4MBANzX2hFxc469CeP6nyQ1Q6g2EdvZR74NTxnr/DlZJLo961gzmJ1Tj\n" ++ "TQpgcmLNkQfWpb/ImWvtxBnmq0wROMVvMeJuScg/doAmAyYp4Db29iBT4xdwNBed\n" ++ "Y2gea+zDTYa4EzAvXUYNR0PVG6pZDrlcjQZIrXSHX8f8MVRBE+LHIQ6e4B4N4cB7\n" ++ "Q4WQxYpYxmUKeFfyxiMPAdkgS94P+5KFdSpcc41teyWRyu5FrgZLAMzTsVlQ2jqI\n" ++ "OylDRl6XK1TOU2+NSueW+r9xDkKLfP0ooNBIytrEgUy7onOTJsjrDNYmiLbAJM+7\n" ++ "vVvrdX3pCI6GMyx5dwlppYn8s3CQh3aP0yK7Qs69cwsgJirQmz1wHiRszYd2qReW\n" ++ "t88NkvuOGKmYSdGe/mBEciG5Ge3C9THxOUiIkCR1VBatzvT4aRRkOfujuLpwQMcn\n" ++ "HL/EVlP6Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVx\n" ++ "SK236thZiNSQvxaz2emsWWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY=\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFdDCCA1ygAwIBAgIPAWdfJ9b+euPkrL4JWwWeMA0GCSqGSIb3DQEBCwUAMEQx\n" ++ "CzAJBgNVBAYTAkZJMRowGAYDVQQKDBFUZWxpYSBGaW5sYW5kIE95ajEZMBcGA1UE\n" ++ "AwwQVGVsaWEgUm9vdCBDQSB2MjAeFw0xODExMjkxMTU1NTRaFw00MzExMjkxMTU1\n" ++ "NTRaMEQxCzAJBgNVBAYTAkZJMRowGAYDVQQKDBFUZWxpYSBGaW5sYW5kIE95ajEZ\n" ++ "MBcGA1UEAwwQVGVsaWEgUm9vdCBDQSB2MjCCAiIwDQYJKoZIhvcNAQEBBQADggIP\n" ++ "ADCCAgoCggIBALLQPwe84nvQa5n44ndp586dpAO8gm2h/oFlH0wnrI4AuhZ76zBq\n" ++ "AMCzdGh+sq/H1WKzej9Qyow2RCRj0jbpDIX2Q3bVTKFgcmfiKDOlyzG4OiIjNLh9\n" ++ "vVYiQJ3q9HsDrWj8soFPmNB06o3lfc1jw6P23pLCWBnglrvFxKk9pXSW/q/5iaq9\n" ++ "lRdU2HhE8Qx3FZLgmEKnpNaqIJLNwaCzlrI6hEKNfdWV5Nbb6WLEWLN5xYzTNTOD\n" ++ "n3WhUidhOPFZPY5Q4L15POdslv5e2QJltI5c0BE0312/UqeBAMN/mUWZFdUXyApT\n" ++ "7GPzmX3MaRKGwhfwAZ6/hLzRUssbkmbOpFPlob/E2wnW5olWK8jjfN7j/4nlNW4o\n" ++ "6GwLI1GpJQXrSPjdscr6bAhR77cYbETKJuFzxokGgeWKrLDiKca5JLNrRBH0pUPC\n" ++ "TEPlcDaMtjNXepUugqD0XBCzYYP2AgWGLnwtbNwDRm41k9V6lS/eINhbfpSQBGq6\n" ++ "WT0EBXWdN6IOLj3rwaRSg/7Qa9RmjtzG6RJOHSpXqhC8fF6CfaamyfItufUXJ63R\n" ++ "DolUK5X6wK0dmBR4M0KGCqlztft0DbcbMBnEWg4cJ7faGND/isgFuvGqHKI3t+ZI\n" ++ "pEYslOqodmJHixBTB0hXbOKSTbauBcvcwUpej6w9GU7C7WB1K9vBykLVAgMBAAGj\n" ++ "YzBhMB8GA1UdIwQYMBaAFHKs5DN5qkWH9v2sHZ7Wxy+G2CQ5MB0GA1UdDgQWBBRy\n" ++ "rOQzeapFh/b9rB2e1scvhtgkOTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUw\n" ++ "AwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAoDtZpwmUPjaE0n4vOaWWl/oRrfxn83EJ\n" ++ "8rKJhGdEr7nv7ZbsnGTbMjBvZ5qsfl+yqwE2foH65IRe0qw24GtixX1LDoJt0nZi\n" ++ "0f6X+J8wfBj5tFJ3gh1229MdqfDBmgC9bXXYfef6xzijnHDoRnkDry5023X4blMM\n" ++ "A8iZGok1GTzTyVR8qPAs5m4HeW9q4ebqkYJpCh3DflminmtGFZhb069GHWLIzoBS\n" ++ "SRE/yQQSwxN8PzuKlts8oB4KtItUsiRnDe+Cy748fdHif64W1lZYudogsYMVoe+K\n" ++ "TTJvQS8TUoKU1xrBeKJR3Stwbbca+few4GeXVtt8YVMJAygCQMez2P2ccGrGKMOF\n" ++ "6eLtGpOg3kuYooQ+BXcBlj37tCAPnHICehIv1aO6UXivKitEZU61/Qrowc15h2Er\n" ++ "3oBXRb9n8ZuRXqWk7FlIEA04x7D6w0RtBPV4UBySllva9bguulvP5fBqnUsvWHMt\n" ++ "Ty3EHD70sz+rFQ47GUGKpMFXEmZxTPpT41frYpUJnlTd0cI8Vzy9OK2YZLe4A5pT\n" ++ "VmBds9hCG1xLEooc6+t9xnppxyd/pPiL8uSUZodL6ZQHCRJ5irLrdATczvREWeAW\n" ++ "ysUsWNc8e89ihmpQfTU2Zqf7N+cox9jQraVplI/owd8k+BsHMYeB2F326CjYSlKA\n" ++ "rBPuUBQemMc=\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFpTCCA42gAwIBAgIUZPYOZXdhaqs7tOqFhLuxibhxkw8wDQYJKoZIhvcNAQEM\n" ++ "BQAwWjELMAkGA1UEBhMCQ04xJTAjBgNVBAoMHFRydXN0QXNpYSBUZWNobm9sb2dp\n" ++ "ZXMsIEluYy4xJDAiBgNVBAMMG1RydXN0QXNpYSBHbG9iYWwgUm9vdCBDQSBHMzAe\n" ++ "Fw0yMTA1MjAwMjEwMTlaFw00NjA1MTkwMjEwMTlaMFoxCzAJBgNVBAYTAkNOMSUw\n" ++ "IwYDVQQKDBxUcnVzdEFzaWEgVGVjaG5vbG9naWVzLCBJbmMuMSQwIgYDVQQDDBtU\n" ++ "cnVzdEFzaWEgR2xvYmFsIFJvb3QgQ0EgRzMwggIiMA0GCSqGSIb3DQEBAQUAA4IC\n" ++ "DwAwggIKAoICAQDAMYJhkuSUGwoqZdC+BqmHO1ES6nBBruL7dOoKjbmzTNyPtxNS\n" ++ "T1QY4SxzlZHFZjtqz6xjbYdT8PfxObegQ2OwxANdV6nnRM7EoYNl9lA+sX4WuDqK\n" ++ "AtCWHwDNBSHvBm3dIZwZQ0WhxeiAysKtQGIXBsaqvPPW5vxQfmZCHzyLpnl5hkA1\n" ++ "nyDvP+uLRx+PjsXUjrYsyUQE49RDdT/VP68czH5GX6zfZBCK70bwkPAPLfSIC7Ep\n" ++ "qq+FqklYqL9joDiR5rPmd2jE+SoZhLsO4fWvieylL1AgdB4SQXMeJNnKziyhWTXA\n" ++ "yB1GJ2Faj/lN03J5Zh6fFZAhLf3ti1ZwA0pJPn9pMRJpxx5cynoTi+jm9WAPzJMs\n" ++ "hH/x/Gr8m0ed262IPfN2dTPXS6TIi/n1Q1hPy8gDVI+lhXgEGvNz8teHHUGf59gX\n" ++ "zhqcD0r83ERoVGjiQTz+LISGNzzNPy+i2+f3VANfWdP3kXjHi3dqFuVJhZBFcnAv\n" ++ "kV34PmVACxmZySYgWmjBNb9Pp1Hx2BErW+Canig7CjoKH8GB5S7wprlppYiU5msT\n" ++ "f9FkPz2ccEblooV7WIQn3MSAPmeamseaMQ4w7OYXQJXZRe0Blqq/DPNL0WP3E1jA\n" ++ "uPP6Z92bfW1K/zJMtSU7/xxnD4UiWQWRkUF3gdCFTIcQcf+eQxuulXUtgQIDAQAB\n" ++ "o2MwYTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFEDk5PIj7zjKsK5Xf/Ih\n" ++ "MBY027ySMB0GA1UdDgQWBBRA5OTyI+84yrCuV3/yITAWNNu8kjAOBgNVHQ8BAf8E\n" ++ "BAMCAQYwDQYJKoZIhvcNAQEMBQADggIBACY7UeFNOPMyGLS0XuFlXsSUT9SnYaP4\n" ++ "wM8zAQLpw6o1D/GUE3d3NZ4tVlFEbuHGLige/9rsR82XRBf34EzC4Xx8MnpmyFq2\n" ++ "XFNFV1pF1AWZLy4jVe5jaN/TG3inEpQGAHUNcoTpLrxaatXeL1nHo+zSh2bbt1S1\n" ++ "JKv0Q3jbSwTEb93mPmY+KfJLaHEih6D4sTNjduMNhXJEIlU/HHzp/LgV6FL6qj6j\n" ++ "ITk1dImmasI5+njPtqzn59ZW/yOSLlALqbUHM/Q4X6RJpstlcHboCoWASzY9M/eV\n" ++ "VHUl2qzEc4Jl6VL1XP04lQJqaTDFHApXB64ipCz5xUG3uOyfT0gA+QEEVcys+TIx\n" ++ "xHWVBqB/0Y0n3bOppHKH/lmLmnp0Ft0WpWIp6zqW3IunaFnT63eROfjXy9mPX1on\n" ++ "AX1daBli2MjN9LdyR75bl87yraKZk62Uy5P2EgmVtqvXO9A/EcswFi55gORngS1d\n" ++ "7XB4tmBZrOFdRWOPyN9yaFvqHbgB8X7754qz41SgOAngPN5C8sLtLpvzHzW2Ntjj\n" ++ "gKGLzZlkD8Kqq7HK9W+eQ42EVJmzbsASZthwEPEGNTNDqJwuuhQxzhB/HIbjj9LV\n" ++ "+Hfsm6vxL2PZQl/gZ4FkkfGXL/xuJvYz+NO1+MRiqzFRJQJ6+N1rZdVtTTDIZbpo\n" ++ "FGWsJwt0ivKH\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIICVTCCAdygAwIBAgIUTyNkuI6XY57GU4HBdk7LKnQV1tcwCgYIKoZIzj0EAwMw\n" ++ "WjELMAkGA1UEBhMCQ04xJTAjBgNVBAoMHFRydXN0QXNpYSBUZWNobm9sb2dpZXMs\n" ++ "IEluYy4xJDAiBgNVBAMMG1RydXN0QXNpYSBHbG9iYWwgUm9vdCBDQSBHNDAeFw0y\n" ++ "MTA1MjAwMjEwMjJaFw00NjA1MTkwMjEwMjJaMFoxCzAJBgNVBAYTAkNOMSUwIwYD\n" ++ "VQQKDBxUcnVzdEFzaWEgVGVjaG5vbG9naWVzLCBJbmMuMSQwIgYDVQQDDBtUcnVz\n" ++ "dEFzaWEgR2xvYmFsIFJvb3QgQ0EgRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATx\n" ++ "s8045CVD5d4ZCbuBeaIVXxVjAd7Cq92zphtnS4CDr5nLrBfbK5bKfFJV4hrhPVbw\n" ++ "LxYI+hW8m7tH5j/uqOFMjPXTNvk4XatwmkcN4oFBButJ+bAp3TPsUKV/eSm4IJij\n" ++ "YzBhMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUpbtKl86zK3+kMd6Xg1mD\n" ++ "pm9xy94wHQYDVR0OBBYEFKW7SpfOsyt/pDHel4NZg6ZvccveMA4GA1UdDwEB/wQE\n" ++ "AwIBBjAKBggqhkjOPQQDAwNnADBkAjBe8usGzEkxn0AAbbd+NvBNEU/zy4k6LHiR\n" ++ "UKNbwMp1JvK/kF0LgoxgKJ/GcJpo5PECMFxYDlZ2z1jD1xCMuo6u47xkdUfFVZDj\n" ++ "/bpV6wfEU6s3qe4hsiFbYI89MvHVI5TWWA==\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIF2jCCA8KgAwIBAgIMBfcOhtpJ80Y1LrqyMA0GCSqGSIb3DQEBCwUAMIGIMQsw\n" ++ "CQYDVQQGEwJVUzERMA8GA1UECAwISWxsaW5vaXMxEDAOBgNVBAcMB0NoaWNhZ28x\n" ++ "ITAfBgNVBAoMGFRydXN0d2F2ZSBIb2xkaW5ncywgSW5jLjExMC8GA1UEAwwoVHJ1\n" ++ "c3R3YXZlIEdsb2JhbCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0xNzA4MjMx\n" ++ "OTM0MTJaFw00MjA4MjMxOTM0MTJaMIGIMQswCQYDVQQGEwJVUzERMA8GA1UECAwI\n" ++ "SWxsaW5vaXMxEDAOBgNVBAcMB0NoaWNhZ28xITAfBgNVBAoMGFRydXN0d2F2ZSBI\n" ++ "b2xkaW5ncywgSW5jLjExMC8GA1UEAwwoVHJ1c3R3YXZlIEdsb2JhbCBDZXJ0aWZp\n" ++ "Y2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB\n" ++ "ALldUShLPDeS0YLOvR29zd24q88KPuFd5dyqCblXAj7mY2Hf8g+CY66j96xz0Xzn\n" ++ "swuvCAAJWX/NKSqIk4cXGIDtiLK0thAfLdZfVaITXdHG6wZWiYj+rDKd/VzDBcdu\n" ++ "7oaJuogDnXIhhpCujwOl3J+IKMujkkkP7NAP4m1ET4BqstTnoApTAbqOl5F2brz8\n" ++ "1Ws25kCI1nsvXwXoLG0R8+eyvpJETNKXpP7ScoFDB5zpET71ixpZfR9oWN0EACyW\n" ++ "80OzfpgZdNmcc9kYvkHHNHnZ9GLCQ7mzJ7Aiy/k9UscwR7PJPrhq4ufogXBeQotP\n" ++ "JqX+OsIgbrv4Fo7NDKm0G2x2EOFYeUY+VM6AqFcJNykbmROPDMjWLBz7BegIlT1l\n" ++ "RtzuzWniTY+HKE40Cz7PFNm73bZQmq131BnW2hqIyE4bJ3XYsgjxroMwuREOzYfw\n" ++ "hI0Vcnyh78zyiGG69Gm7DIwLdVcEuE4qFC49DxweMqZiNu5m4iK4BUBjECLzMx10\n" ++ "coos9TkpoNPnG4CELcU9402x/RpvumUHO1jsQkUm+9jaJXLE9gCxInm943xZYkqc\n" ++ "BW89zubWR2OZxiRvchLIrH+QtAuRcOi35hYQcRfO3gZPSEF9NUqjifLJS3tBEW1n\n" ++ "twiYTOURGa5CgNz7kAXU+FDKvuStx8KU1xad5hePrzb7AgMBAAGjQjBAMA8GA1Ud\n" ++ "EwEB/wQFMAMBAf8wHQYDVR0OBBYEFJngGWcNYtt2s9o9uFvo/ULSMQ6HMA4GA1Ud\n" ++ "DwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAmHNw4rDT7TnsTGDZqRKGFx6W\n" ++ "0OhUKDtkLSGm+J1WE2pIPU/HPinbbViDVD2HfSMF1OQc3Og4ZYbFdada2zUFvXfe\n" ++ "uyk3QAUHw5RSn8pk3fEbK9xGChACMf1KaA0HZJDmHvUqoai7PF35owgLEQzxPy0Q\n" ++ "lG/+4jSHg9bP5Rs1bdID4bANqKCqRieCNqcVtgimQlRXtpla4gt5kNdXElE1GYhB\n" ++ "aCXUNxeEFfsBctyV3lImIJgm4nb1J2/6ADtKYdkNy1GTKv0WBpanI5ojSP5RvbbE\n" ++ "sLFUzt5sQa0WZ37b/TjNuThOssFgy50X31ieemKyJo90lZvkWx3SD92YHJtZuSPT\n" ++ "MaCm/zjdzyBP6VhWOmfD0faZmZ26NraAL4hHT4a/RDqA5Dccprrql5gR0IRiR2Qe\n" ++ "qu5AvzSxnI9O4fKSTx+O856X3vOmeWqJcU9LJxdI/uz0UA9PSX3MReO9ekDFQdxh\n" ++ "VicGaeVyQYHTtgGJoC86cnn+OjC/QezHYj6RS8fZMXZC+fc8Y+wmjHMMfRod6qh8\n" ++ "h6jCJ3zhM0EPz8/8AKAigJ5Kp28AsEFFtyLKaEjFQqKu3R3y4G5OBVixwJAWKqQ9\n" ++ "EEC+j2Jjg6mcgn0tAumDMHzLJ8n9HmYAsC7TIS+OMxZsmO0QqAfWzJPP29FpHOTK\n" ++ "yeC2nOnOcXHebD8WpHk=\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIICYDCCAgegAwIBAgIMDWpfCD8oXD5Rld9dMAoGCCqGSM49BAMCMIGRMQswCQYD\n" ++ "VQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAf\n" ++ "BgNVBAoTGFRydXN0d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3\n" ++ "YXZlIEdsb2JhbCBFQ0MgUDI1NiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0x\n" ++ "NzA4MjMxOTM1MTBaFw00MjA4MjMxOTM1MTBaMIGRMQswCQYDVQQGEwJVUzERMA8G\n" ++ "A1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRydXN0\n" ++ "d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBF\n" ++ "Q0MgUDI1NiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTBZMBMGByqGSM49AgEGCCqG\n" ++ "SM49AwEHA0IABH77bOYj43MyCMpg5lOcunSNGLB4kFKA3TjASh3RqMyTpJcGOMoN\n" ++ "FWLGjgEqZZ2q3zSRLoHB5DOSMcT9CTqmP62jQzBBMA8GA1UdEwEB/wQFMAMBAf8w\n" ++ "DwYDVR0PAQH/BAUDAwcGADAdBgNVHQ4EFgQUo0EGrJBt0UrrdaVKEJmzsaGLSvcw\n" ++ "CgYIKoZIzj0EAwIDRwAwRAIgB+ZU2g6gWrKuEZ+Hxbb/ad4lvvigtwjzRM4q3wgh\n" ++ "DDcCIC0mA6AFvWvR9lz4ZcyGbbOcNEhjhAnFjXca4syc4XR7\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIICnTCCAiSgAwIBAgIMCL2Fl2yZJ6SAaEc7MAoGCCqGSM49BAMDMIGRMQswCQYD\n" ++ "VQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAf\n" ++ "BgNVBAoTGFRydXN0d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3\n" ++ "YXZlIEdsb2JhbCBFQ0MgUDM4NCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0x\n" ++ "NzA4MjMxOTM2NDNaFw00MjA4MjMxOTM2NDNaMIGRMQswCQYDVQQGEwJVUzERMA8G\n" ++ "A1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRydXN0\n" ++ "d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBF\n" ++ "Q0MgUDM4NCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTB2MBAGByqGSM49AgEGBSuB\n" ++ "BAAiA2IABGvaDXU1CDFHBa5FmVXxERMuSvgQMSOjfoPTfygIOiYaOs+Xgh+AtycJ\n" ++ "j9GOMMQKmw6sWASr9zZ9lCOkmwqKi6vr/TklZvFe/oyujUF5nQlgziip04pt89ZF\n" ++ "1PKYhDhloKNDMEEwDwYDVR0TAQH/BAUwAwEB/zAPBgNVHQ8BAf8EBQMDBwYAMB0G\n" ++ "A1UdDgQWBBRVqYSJ0sEyvRjLbKYHTsjnnb6CkDAKBggqhkjOPQQDAwNnADBkAjA3\n" ++ "AZKXRRJ+oPM+rRk6ct30UJMDEr5E0k9BpIycnR+j9sKS50gU/k6bpZFXrsY3crsC\n" ++ "MGclCrEMXu6pY5Jv5ZAL/mYiykf9ijH3g/56vxC+GCsej/YpHpRZ744hN8tRmKVu\n" ++ "Sw==\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFszCCA5ugAwIBAgIUEwLV4kBMkkaGFmddtLu7sms+/BMwDQYJKoZIhvcNAQEL\n" ++ "BQAwYTELMAkGA1UEBhMCVE4xNzA1BgNVBAoMLkFnZW5jZSBOYXRpb25hbGUgZGUg\n" ++ "Q2VydGlmaWNhdGlvbiBFbGVjdHJvbmlxdWUxGTAXBgNVBAMMEFR1blRydXN0IFJv\n" ++ "b3QgQ0EwHhcNMTkwNDI2MDg1NzU2WhcNNDQwNDI2MDg1NzU2WjBhMQswCQYDVQQG\n" ++ "EwJUTjE3MDUGA1UECgwuQWdlbmNlIE5hdGlvbmFsZSBkZSBDZXJ0aWZpY2F0aW9u\n" ++ "IEVsZWN0cm9uaXF1ZTEZMBcGA1UEAwwQVHVuVHJ1c3QgUm9vdCBDQTCCAiIwDQYJ\n" ++ "KoZIhvcNAQEBBQADggIPADCCAgoCggIBAMPN0/y9BFPdDCA61YguBUtB9YOCfvdZ\n" ++ "n56eY+hz2vYGqU8ftPkLHzmMmiDQfgbU7DTZhrx1W4eI8NLZ1KMKsmwb60ksPqxd\n" ++ "2JQDoOw05TDENX37Jk0bbjBU2PWARZw5rZzJJQRNmpA+TkBuimvNKWfGzC3gdOgF\n" ++ "VwpIUPp6Q9p+7FuaDmJ2/uqdHYVy7BG7NegfJ7/Boce7SBbdVtfMTqDhuazb1YMZ\n" ++ "GoXRlJfXyqNlC/M4+QKu3fZnz8k/9YosRxqZbwUN/dAdgjH8KcwAWJeRTIAAHDOF\n" ++ "li/LQcKLEITDCSSJH7UP2dl3RxiSlGBcx5kDPP73lad9UKGAwqmDrViWVSHbhlnU\n" ++ "r8a83YFuB9tgYv7sEG7aaAH0gxupPqJbI9dkxt/con3YS7qC0lH4Zr8GRuR5KiY2\n" ++ "eY8fTpkdso8MDhz/yV3A/ZAQprE38806JG60hZC/gLkMjNWb1sjxVj8agIl6qeIb\n" ++ "MlEsPvLfe/ZdeikZjuXIvTZxi11Mwh0/rViizz1wTaZQmCXcI/m4WEEIcb9PuISg\n" ++ "jwBUFfyRbVinljvrS5YnzWuioYasDXxU5mZMZl+QviGaAkYt5IPCgLnPSz7ofzwB\n" ++ "7I9ezX/SKEIBlYrilz0QIX32nRzFNKHsLA4KUiwSVXAkPcvCFDVDXSdOvsC9qnyW\n" ++ "5/yeYa1E0wCXAgMBAAGjYzBhMB0GA1UdDgQWBBQGmpsfU33x9aTI04Y+oXNZtPdE\n" ++ "ITAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFAaamx9TffH1pMjThj6hc1m0\n" ++ "90QhMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAqgVutt0Vyb+z\n" ++ "xiD2BkewhpMl0425yAA/l/VSJ4hxyXT968pk21vvHl26v9Hr7lxpuhbI87mP0zYu\n" ++ "QEkHDVneixCwSQXi/5E/S7fdAo74gShczNxtr18UnH1YeA32gAm56Q6XKRm4t+v4\n" ++ "FstVEuTGfbvE7Pi1HE4+Z7/FXxttbUcoqgRYYdZ2vyJ/0Adqp2RT8JeNnYA/u8EH\n" ++ "22Wv5psymsNUk8QcCMNE+3tjEUPRahphanltkE8pjkcFwRJpadbGNjHh/PqAulxP\n" ++ "xOu3Mqz4dWEX1xAZufHSCe96Qp1bWgvUxpVOKs7/B9dPfhgGiPEZtdmYu65xxBzn\n" ++ "dFlY7wyJz4sfdZMaBBSSSFCp61cpABbjNhzI+L/wM9VBD8TMPN3pM0MBkRArHtG5\n" ++ "Xc0yGYuPjCB31yLEQtyEFpslbei0VXF/sHyz03FJuc9SpAQ/3D2gu68zngowYI7b\n" ++ "nV2UqL1g52KAdoGDDIzMMEZJ4gzSqK/rYXHv5yJiqfdcZGyfFoxnNidF9Ql7v/YQ\n" ++ "CvGwjVRDjAS6oz/v4jXH+XTgbzRB0L9zZVcg+ZtnemZoJE6AZb0QmQZZ8mWvuMZH\n" ++ "u/2QeItBcy6vVR/cO5JyboTT0GFMDcx2V+IthSIVNg3rAZ3r2OvEhJn7wAzMMujj\n" ++ "d9qDRIueVSjAi1jTkD5OGwDxFa2DK5o=\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFWjCCA0KgAwIBAgIQT9Irj/VkyDOeTzRYZiNwYDANBgkqhkiG9w0BAQsFADBH\n" ++ "MQswCQYDVQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxJTAjBgNVBAMMHFVDQSBF\n" ++ "eHRlbmRlZCBWYWxpZGF0aW9uIFJvb3QwHhcNMTUwMzEzMDAwMDAwWhcNMzgxMjMx\n" ++ "MDAwMDAwWjBHMQswCQYDVQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxJTAjBgNV\n" ++ "BAMMHFVDQSBFeHRlbmRlZCBWYWxpZGF0aW9uIFJvb3QwggIiMA0GCSqGSIb3DQEB\n" ++ "AQUAA4ICDwAwggIKAoICAQCpCQcoEwKwmeBkqh5DFnpzsZGgdT6o+uM4AHrsiWog\n" ++ "D4vFsJszA1qGxliG1cGFu0/GnEBNyr7uaZa4rYEwmnySBesFK5pI0Lh2PpbIILvS\n" ++ "sPGP2KxFRv+qZ2C0d35qHzwaUnoEPQc8hQ2E0B92CvdqFN9y4zR8V05WAT558aop\n" ++ "O2z6+I9tTcg1367r3CTueUWnhbYFiN6IXSV8l2RnCdm/WhUFhvMJHuxYMjMR83dk\n" ++ "sHYf5BA1FxvyDrFspCqjc/wJHx4yGVMR59mzLC52LqGj3n5qiAno8geK+LLNEOfi\n" ++ "c0CTuwjRP+H8C5SzJe98ptfRr5//lpr1kXuYC3fUfugH0mK1lTnj8/FtDw5lhIpj\n" ++ "VMWAtuCeS31HJqcBCF3RiJ7XwzJE+oJKCmhUfzhTA8ykADNkUVkLo4KRel7sFsLz\n" ++ "KuZi2irbWWIQJUoqgQtHB0MGcIfS+pMRKXpITeuUx3BNr2fVUbGAIAEBtHoIppB/\n" ++ "TuDvB0GHr2qlXov7z1CymlSvw4m6WC31MJixNnI5fkkE/SmnTHnkBVfblLkWU41G\n" ++ "sx2VYVdWf6/wFlthWG82UBEL2KwrlRYaDh8IzTY0ZRBiZtWAXxQgXy0MoHgKaNYs\n" ++ "1+lvK9JKBZP8nm9rZ/+I8U6laUpSNwXqxhaN0sSZ0YIrO7o1dfdRUVjzyAfd5LQD\n" ++ "fwIDAQABo0IwQDAdBgNVHQ4EFgQU2XQ65DA9DfcS3H5aBZ8eNJr34RQwDwYDVR0T\n" ++ "AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggIBADaN\n" ++ "l8xCFWQpN5smLNb7rhVpLGsaGvdftvkHTFnq88nIua7Mui563MD1sC3AO6+fcAUR\n" ++ "ap8lTwEpcOPlDOHqWnzcSbvBHiqB9RZLcpHIojG5qtr8nR/zXUACE/xOHAbKsxSQ\n" ++ "VBcZEhrxH9cMaVr2cXj0lH2RC47skFSOvG+hTKv8dGT9cZr4QQehzZHkPJrgmzI5\n" ++ "c6sq1WnIeJEmMX3ixzDx/BR4dxIOE/TdFpS/S2d7cFOFyrC78zhNLJA5wA3CXWvp\n" ++ "4uXViI3WLL+rG761KIcSF3Ru/H38j9CHJrAb+7lsq+KePRXBOy5nAliRn+/4Qh8s\n" ++ "t2j1da3Ptfb/EX3C8CSlrdP6oDyp+l3cpaDvRKS+1ujl5BOWF3sGPjLtx7dCvHaj\n" ++ "2GU4Kzg1USEODm8uNBNA4StnDG1KQTAYI1oyVZnJF+A83vbsea0rWBmirSwiGpWO\n" ++ "vpaQXUJXxPkUAzUrHC1RVwinOt4/5Mi0A3PCwSaAuwtCH60NryZy2sy+s6ODWA2C\n" ++ "xR9GUeOcGMyNm43sSet1UNWMKFnKdDTajAshqx7qG+XH/RU+wBeq+yNuJkbL+vmx\n" ++ "cmtpzyKEC2IPrNkZAJSidjzULZrtBJ4tBmIQN1IchXIbJ+XMxjHsN+xjWZsLHXbM\n" ++ "fjKaiJUINlK73nZfdklJrX+9ZSCyycErdhh2n1ax\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFRjCCAy6gAwIBAgIQXd+x2lqj7V2+WmUgZQOQ7zANBgkqhkiG9w0BAQsFADA9\n" ++ "MQswCQYDVQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxGzAZBgNVBAMMElVDQSBH\n" ++ "bG9iYWwgRzIgUm9vdDAeFw0xNjAzMTEwMDAwMDBaFw00MDEyMzEwMDAwMDBaMD0x\n" ++ "CzAJBgNVBAYTAkNOMREwDwYDVQQKDAhVbmlUcnVzdDEbMBkGA1UEAwwSVUNBIEds\n" ++ "b2JhbCBHMiBSb290MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxeYr\n" ++ "b3zvJgUno4Ek2m/LAfmZmqkywiKHYUGRO8vDaBsGxUypK8FnFyIdK+35KYmToni9\n" ++ "kmugow2ifsqTs6bRjDXVdfkX9s9FxeV67HeToI8jrg4aA3++1NDtLnurRiNb/yzm\n" ++ "VHqUwCoV8MmNsHo7JOHXaOIxPAYzRrZUEaalLyJUKlgNAQLx+hVRZ2zA+te2G3/R\n" ++ "VogvGjqNO7uCEeBHANBSh6v7hn4PJGtAnTRnvI3HLYZveT6OqTwXS3+wmeOwcWDc\n" ++ "C/Vkw85DvG1xudLeJ1uK6NjGruFZfc8oLTW4lVYa8bJYS7cSN8h8s+1LgOGN+jIj\n" ++ "tm+3SJUIsUROhYw6AlQgL9+/V087OpAh18EmNVQg7Mc/R+zvWr9LesGtOxdQXGLY\n" ++ "D0tK3Cv6brxzks3sx1DoQZbXqX5t2Okdj4q1uViSukqSKwxW/YDrCPBeKW4bHAyv\n" ++ "j5OJrdu9o54hyokZ7N+1wxrrFv54NkzWbtA+FxyQF2smuvt6L78RHBgOLXMDj6Dl\n" ++ "NaBa4kx1HXHhOThTeEDMg5PXCp6dW4+K5OXgSORIskfNTip1KnvyIvbJvgmRlld6\n" ++ "iIis7nCs+dwp4wwcOxJORNanTrAmyPPZGpeRaOrvjUYG0lZFWJo8DA+DuAUlwznP\n" ++ "O6Q0ibd5Ei9Hxeepl2n8pndntd978XplFeRhVmUCAwEAAaNCMEAwDgYDVR0PAQH/\n" ++ "BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFIHEjMz15DD/pQwIX4wV\n" ++ "ZyF0Ad/fMA0GCSqGSIb3DQEBCwUAA4ICAQATZSL1jiutROTL/7lo5sOASD0Ee/oj\n" ++ "L3rtNtqyzm325p7lX1iPyzcyochltq44PTUbPrw7tgTQvPlJ9Zv3hcU2tsu8+Mg5\n" ++ "1eRfB70VVJd0ysrtT7q6ZHafgbiERUlMjW+i67HM0cOU2kTC5uLqGOiiHycFutfl\n" ++ "1qnN3e92mI0ADs0b+gO3joBYDic/UvuUospeZcnWhNq5NXHzJsBPd+aBJ9J3O5oU\n" ++ "b3n09tDh05S60FdRvScFDcH9yBIw7m+NESsIndTUv4BFFJqIRNow6rSn4+7vW4LV\n" ++ "PtateJLbXDzz2K36uGt/xDYotgIVilQsnLAXc47QN6MUPJiVAAwpBVueSUmxX8fj\n" ++ "y88nZY41F7dXyDDZQVu5FLbowg+UMaeUmMxq67XhJ/UQqAHojhJi6IjMtX9Gl8Cb\n" ++ "EGY4GjZGXyJoPd/JxhMnq1MGrKI8hgZlb7F+sSlEmqO6SWkoaY/X5V+tBIZkbxqg\n" ++ "DMUIYs6Ao9Dz7GjevjPHF1t/gMRMTLGmhIrDO7gJzRSBuhjjVFc2/tsvfEehOjPI\n" ++ "+Vg7RE+xygKJBJYoaMVLuCaJu9YzL1DV/pqJuhgyklTGW+Cd+V7lDSKb9triyCGy\n" ++ "YiGqhkCyLmTTX8jjfhFnRR8F/uOi77Oos/N9j/gMHyIfLXC0uAE0djAA5SN4p1bX\n" ++ "UB+K+wb1whnw0A==\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDEL\n" ++ "MAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNl\n" ++ "eSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMT\n" ++ "JVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMjAx\n" ++ "MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgT\n" ++ "Ck5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVUaGUg\n" ++ "VVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlm\n" ++ "aWNhdGlvbiBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqflo\n" ++ "I+d61SRvU8Za2EurxtW20eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinng\n" ++ "o4N+LZfQYcTxmdwlkWOrfzCjtHDix6EznPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0G\n" ++ "A1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNVHQ8BAf8EBAMCAQYwDwYD\n" ++ "VR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBBHU6+4WMB\n" ++ "zzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbW\n" ++ "RNZu9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg=\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB\n" ++ "iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl\n" ++ "cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV\n" ++ "BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw\n" ++ "MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV\n" ++ "BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU\n" ++ "aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy\n" ++ "dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK\n" ++ "AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B\n" ++ "3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY\n" ++ "tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/\n" ++ "Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2\n" ++ "VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT\n" ++ "79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6\n" ++ "c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT\n" ++ "Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l\n" ++ "c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee\n" ++ "UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE\n" ++ "Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd\n" ++ "BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G\n" ++ "A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF\n" ++ "Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO\n" ++ "VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3\n" ++ "ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs\n" ++ "8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR\n" ++ "iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze\n" ++ "Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ\n" ++ "XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/\n" ++ "qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB\n" ++ "VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB\n" ++ "L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG\n" ++ "jjxDah2nGN59PRbxYvnKkKj9\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCB\n" ++ "gjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEk\n" ++ "MCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRY\n" ++ "UmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQxMTAxMTcx\n" ++ "NDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3\n" ++ "dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2Vy\n" ++ "dmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB\n" ++ "dXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS6\n" ++ "38eMpSe2OAtp87ZOqCwuIR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCP\n" ++ "KZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMxfoArtYzAQDsRhtDLooY2YKTVMIJt2W7Q\n" ++ "DxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FEzG+gSqmUsE3a56k0enI4\n" ++ "qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqsAxcZZPRa\n" ++ "JSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNVi\n" ++ "PvryxS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0P\n" ++ "BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASs\n" ++ "jVy16bYbMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0\n" ++ "eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQEwDQYJKoZIhvcNAQEFBQAD\n" ++ "ggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc/Kh4ZzXxHfAR\n" ++ "vbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt\n" ++ "qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLa\n" ++ "IR9NmXmd4c8nnxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSy\n" ++ "i6mx5O+aGtA9aZnuqCij4Tyz8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQ\n" ++ "O+7ETPTsJ3xCwnR8gooJybQDJbw=\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYT\n" ++ "AlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBD\n" ++ "QTAeFw0wNjA3MDQxNzIwMDRaFw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJP\n" ++ "MREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTCC\n" ++ "ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7IJUqOtdu0KBuqV5Do\n" ++ "0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHHrfAQ\n" ++ "UySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5d\n" ++ "RdY4zTW2ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQ\n" ++ "OA7+j0xbm0bqQfWwCHTD0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwv\n" ++ "JoIQ4uNllAoEwF73XVv4EOLQunpL+943AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08C\n" ++ "AwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAcYwHQYDVR0O\n" ++ "BBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IBAQA+0hyJ\n" ++ "LjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecY\n" ++ "MnQ8SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ\n" ++ "44gx+FkagQnIl6Z0x2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6I\n" ++ "Jd1hJyMctTEHBDa0GpC9oHRxUIltvBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNw\n" ++ "i/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7NzTogVZ96edhBiIL5VaZVDADlN\n" ++ "9u6wWk5JRFRYX0KD\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFRzCCAy+gAwIBAgIJEQA0tk7GNi02MA0GCSqGSIb3DQEBCwUAMEExCzAJBgNV\n" ++ "BAYTAlJPMRQwEgYDVQQKEwtDRVJUU0lHTiBTQTEcMBoGA1UECxMTY2VydFNJR04g\n" ++ "Uk9PVCBDQSBHMjAeFw0xNzAyMDYwOTI3MzVaFw00MjAyMDYwOTI3MzVaMEExCzAJ\n" ++ "BgNVBAYTAlJPMRQwEgYDVQQKEwtDRVJUU0lHTiBTQTEcMBoGA1UECxMTY2VydFNJ\n" ++ "R04gUk9PVCBDQSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMDF\n" ++ "dRmRfUR0dIf+DjuW3NgBFszuY5HnC2/OOwppGnzC46+CjobXXo9X69MhWf05N0Iw\n" ++ "vlDqtg+piNguLWkh59E3GE59kdUWX2tbAMI5Qw02hVK5U2UPHULlj88F0+7cDBrZ\n" ++ "uIt4ImfkabBoxTzkbFpG583H+u/E7Eu9aqSs/cwoUe+StCmrqzWaTOTECMYmzPhp\n" ++ "n+Sc8CnTXPnGFiWeI8MgwT0PPzhAsP6CRDiqWhqKa2NYOLQV07YRaXseVO6MGiKs\n" ++ "cpc/I1mbySKEwQdPzH/iV8oScLumZfNpdWO9lfsbl83kqK/20U6o2YpxJM02PbyW\n" ++ "xPFsqa7lzw1uKA2wDrXKUXt4FMMgL3/7FFXhEZn91QqhngLjYl/rNUssuHLoPj1P\n" ++ "rCy7Lobio3aP5ZMqz6WryFyNSwb/EkaseMsUBzXgqd+L6a8VTxaJW732jcZZroiF\n" ++ "DsGJ6x9nxUWO/203Nit4ZoORUSs9/1F3dmKh7Gc+PoGD4FapUB8fepmrY7+EF3fx\n" ++ "DTvf95xhszWYijqy7DwaNz9+j5LP2RIUZNoQAhVB/0/E6xyjyfqZ90bp4RjZsbgy\n" ++ "LcsUDFDYg2WD7rlcz8sFWkz6GZdr1l0T08JcVLwyc6B49fFtHsufpaafItzRUZ6C\n" ++ "eWRgKRM+o/1Pcmqr4tTluCRVLERLiohEnMqE0yo7AgMBAAGjQjBAMA8GA1UdEwEB\n" ++ "/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSCIS1mxteg4BXrzkwJ\n" ++ "d8RgnlRuAzANBgkqhkiG9w0BAQsFAAOCAgEAYN4auOfyYILVAzOBywaK8SJJ6ejq\n" ++ "kX/GM15oGQOGO0MBzwdw5AgeZYWR5hEit/UCI46uuR59H35s5r0l1ZUa8gWmr4UC\n" ++ "b6741jH/JclKyMeKqdmfS0mbEVeZkkMR3rYzpMzXjWR91M08KCy0mpbqTfXERMQl\n" ++ "qiCA2ClV9+BB/AYm/7k29UMUA2Z44RGx2iBfRgB4ACGlHgAoYXhvqAEBj500mv/0\n" ++ "OJD7uNGzcgbJceaBxXntC6Z58hMLnPddDnskk7RI24Zf3lCGeOdA5jGokHZwYa+c\n" ++ "NywRtYK3qq4kNFtyDGkNzVmf9nGvnAvRCjj5BiKDUyUM/FHE5r7iOZULJK2v0ZXk\n" ++ "ltd0ZGtxTgI8qoXzIKNDOXZbbFD+mpwUHmUUihW9o4JFWklWatKcsWMy5WHgUyIO\n" ++ "pwpJ6st+H6jiYoD2EEVSmAYY3qXNL3+q1Ok+CHLsIwMCPKaq2LxndD0UF/tUSxfj\n" ++ "03k9bWtJySgOLnRQvwzZRjoQhsmnP+mg7H/rpXdYaXHmgwo38oZJar55CJD2AhZk\n" ++ "PuXaTH4MNMn5X7azKFGnpyuqSfqNZSlO42sTp5SjLVFteAxEy9/eCG/Oo2Sr05WE\n" ++ "1LlSVHJ7liXMvGnjSG4N0MedJ5qq+BOS3R7fY581qRY27Iy4g/Q9iY/NtBde17MX\n" ++ "QRBdJ3NghVdJIgc=\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIICQDCCAeWgAwIBAgIMAVRI7yH9l1kN9QQKMAoGCCqGSM49BAMCMHExCzAJBgNV\n" ++ "BAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMgTHRk\n" ++ "LjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25vIFJv\n" ++ "b3QgQ0EgMjAxNzAeFw0xNzA4MjIxMjA3MDZaFw00MjA4MjIxMjA3MDZaMHExCzAJ\n" ++ "BgNVBAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMg\n" ++ "THRkLjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25v\n" ++ "IFJvb3QgQ0EgMjAxNzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJbcPYrYsHtv\n" ++ "xie+RJCxs1YVe45DJH0ahFnuY2iyxl6H0BVIHqiQrb1TotreOpCmYF9oMrWGQd+H\n" ++ "Wyx7xf58etqjYzBhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G\n" ++ "A1UdDgQWBBSHERUI0arBeAyxr87GyZDvvzAEwDAfBgNVHSMEGDAWgBSHERUI0arB\n" ++ "eAyxr87GyZDvvzAEwDAKBggqhkjOPQQDAgNJADBGAiEAtVfd14pVCzbhhkT61Nlo\n" ++ "jbjcI4qKDdQvfepz7L9NbKgCIQDLpbQS+ue16M9+k/zzNY9vTlp8tLxOsvxyqltZ\n" ++ "+efcMQ==\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBe\n" ++ "MQswCQYDVQQGEwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0\n" ++ "ZC4xKjAoBgNVBAsMIWVQS0kgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe\n" ++ "Fw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMxMjdaMF4xCzAJBgNVBAYTAlRXMSMw\n" ++ "IQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEqMCgGA1UECwwhZVBL\n" ++ "SSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEF\n" ++ "AAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAH\n" ++ "SyZbCUNsIZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAh\n" ++ "ijHyl3SJCRImHJ7K2RKilTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3X\n" ++ "DZoTM1PRYfl61dd4s5oz9wCGzh1NlDivqOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1\n" ++ "TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX12ruOzjjK9SXDrkb5wdJ\n" ++ "fzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0OWQqraffA\n" ++ "sgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uU\n" ++ "WH1+ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLS\n" ++ "nT0IFaUQAS2zMnaolQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pH\n" ++ "dmX2Os+PYhcZewoozRrSgx4hxyy/vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJip\n" ++ "NiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXiZo1jDiVN1Rmy5nk3pyKdVDEC\n" ++ "AwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/QkqiMAwGA1UdEwQF\n" ++ "MAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH\n" ++ "ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGB\n" ++ "uvl2ICO1J2B01GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6Yl\n" ++ "PwZpVnPDimZI+ymBV3QGypzqKOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkP\n" ++ "JXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdVxrsStZf0X4OFunHB2WyBEXYKCrC/\n" ++ "gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEPNXubrjlpC2JgQCA2\n" ++ "j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+rGNm6\n" ++ "5ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUB\n" ++ "o2M3IUxExJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS\n" ++ "/jQ6fbjpKdx2qcgw+BRxgMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2z\n" ++ "Gp1iro2C6pSe3VkQw63d4k3jMdXH7OjysP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTE\n" ++ "W9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmODBCEIZ43ygknQW/2xzQ+D\n" ++ "hNQ+IIX3Sj0rnP0qCglN6oH4EZw=\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIICKzCCAbGgAwIBAgIKe3G2gla4EnycqDAKBggqhkjOPQQDAzBaMQswCQYDVQQG\n" ++ "EwJVUzETMBEGA1UECxMKZW1TaWduIFBLSTEUMBIGA1UEChMLZU11ZGhyYSBJbmMx\n" ++ "IDAeBgNVBAMTF2VtU2lnbiBFQ0MgUm9vdCBDQSAtIEMzMB4XDTE4MDIxODE4MzAw\n" ++ "MFoXDTQzMDIxODE4MzAwMFowWjELMAkGA1UEBhMCVVMxEzARBgNVBAsTCmVtU2ln\n" ++ "biBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMSAwHgYDVQQDExdlbVNpZ24gRUND\n" ++ "IFJvb3QgQ0EgLSBDMzB2MBAGByqGSM49AgEGBSuBBAAiA2IABP2lYa57JhAd6bci\n" ++ "MK4G9IGzsUJxlTm801Ljr6/58pc1kjZGDoeVjbk5Wum739D+yAdBPLtVb4Ojavti\n" ++ "sIGJAnB9SMVK4+kiVCJNk7tCDK93nCOmfddhEc5lx/h//vXyqaNCMEAwHQYDVR0O\n" ++ "BBYEFPtaSNCAIEDyqOkAB2kZd6fmw/TPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB\n" ++ "Af8EBTADAQH/MAoGCCqGSM49BAMDA2gAMGUCMQC02C8Cif22TGK6Q04ThHK1rt0c\n" ++ "3ta13FaPWEBaLd4gTCKDypOofu4SQMfWh0/434UCMBwUZOR8loMRnLDRWmFLpg9J\n" ++ "0wD8ofzkpf9/rdcw0Md3f76BB1UwUCAU9Vc4CqgxUQ==\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIICTjCCAdOgAwIBAgIKPPYHqWhwDtqLhDAKBggqhkjOPQQDAzBrMQswCQYDVQQG\n" ++ "EwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNo\n" ++ "bm9sb2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0g\n" ++ "RzMwHhcNMTgwMjE4MTgzMDAwWhcNNDMwMjE4MTgzMDAwWjBrMQswCQYDVQQGEwJJ\n" ++ "TjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNobm9s\n" ++ "b2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0gRzMw\n" ++ "djAQBgcqhkjOPQIBBgUrgQQAIgNiAAQjpQy4LRL1KPOxst3iAhKAnjlfSU2fySU0\n" ++ "WXTsuwYc58Byr+iuL+FBVIcUqEqy6HyC5ltqtdyzdc6LBtCGI79G1Y4PPwT01xyS\n" ++ "fvalY8L1X44uT6EYGQIrMgqCZH0Wk9GjQjBAMB0GA1UdDgQWBBR8XQKEE9TMipuB\n" ++ "zhccLikenEhjQjAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggq\n" ++ "hkjOPQQDAwNpADBmAjEAvvNhzwIQHWSVB7gYboiFBS+DCBeQyh+KTOgNG3qxrdWB\n" ++ "CUfvO6wIBHxcmbHtRwfSAjEAnbpV/KlK6O3t5nYBQnvI+GDZjVGLVTv7jHvrZQnD\n" ++ "+JbNR6iC8hZVdyR+EhCVBCyj\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIDczCCAlugAwIBAgILAK7PALrEzzL4Q7IwDQYJKoZIhvcNAQELBQAwVjELMAkG\n" ++ "A1UEBhMCVVMxEzARBgNVBAsTCmVtU2lnbiBQS0kxFDASBgNVBAoTC2VNdWRocmEg\n" ++ "SW5jMRwwGgYDVQQDExNlbVNpZ24gUm9vdCBDQSAtIEMxMB4XDTE4MDIxODE4MzAw\n" ++ "MFoXDTQzMDIxODE4MzAwMFowVjELMAkGA1UEBhMCVVMxEzARBgNVBAsTCmVtU2ln\n" ++ "biBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMRwwGgYDVQQDExNlbVNpZ24gUm9v\n" ++ "dCBDQSAtIEMxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz+upufGZ\n" ++ "BczYKCFK83M0UYRWEPWgTywS4/oTmifQz/l5GnRfHXk5/Fv4cI7gklL35CX5VIPZ\n" ++ "HdPIWoU/Xse2B+4+wM6ar6xWQio5JXDWv7V7Nq2s9nPczdcdioOl+yuQFTdrHCZH\n" ++ "3DspVpNqs8FqOp099cGXOFgFixwR4+S0uF2FHYP+eF8LRWgYSKVGczQ7/g/IdrvH\n" ++ "GPMF0Ybzhe3nudkyrVWIzqa2kbBPrH4VI5b2P/AgNBbeCsbEBEV5f6f9vtKppa+c\n" ++ "xSMq9zwhbL2vj07FOrLzNBL834AaSaTUqZX3noleoomslMuoaJuvimUnzYnu3Yy1\n" ++ "aylwQ6BpC+S5DwIDAQABo0IwQDAdBgNVHQ4EFgQU/qHgcB4qAzlSWkK+XJGFehiq\n" ++ "TbUwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL\n" ++ "BQADggEBAMJKVvoVIXsoounlHfv4LcQ5lkFMOycsxGwYFYDGrK9HWS8mC+M2sO87\n" ++ "/kOXSTKZEhVb3xEp/6tT+LvBeA+snFOvV71ojD1pM/CjoCNjO2RnIkSt1XHLVip4\n" ++ "kqNPEjE2NuLe/gDEo2APJ62gsIq1NnpSob0n9CAnYuhNlCQT5AoE6TyrLshDCUrG\n" ++ "YQTlSTR+08TI9Q/Aqum6VF7zYytPT1DU/rl7mYw9wC68AivTxEDkigcxHpvOJpkT\n" ++ "+xHqmiIMERnHXhuBUDDIlhJu58tBf5E7oke3VIAb3ADMmpDqw8NQBmIMMMAVSKeo\n" ++ "WXzhriKi4gp6D/piq1JM4fHfyr6DDUI=\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIDlDCCAnygAwIBAgIKMfXkYgxsWO3W2DANBgkqhkiG9w0BAQsFADBnMQswCQYD\n" ++ "VQQGEwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBU\n" ++ "ZWNobm9sb2dpZXMgTGltaXRlZDEcMBoGA1UEAxMTZW1TaWduIFJvb3QgQ0EgLSBH\n" ++ "MTAeFw0xODAyMTgxODMwMDBaFw00MzAyMTgxODMwMDBaMGcxCzAJBgNVBAYTAklO\n" ++ "MRMwEQYDVQQLEwplbVNpZ24gUEtJMSUwIwYDVQQKExxlTXVkaHJhIFRlY2hub2xv\n" ++ "Z2llcyBMaW1pdGVkMRwwGgYDVQQDExNlbVNpZ24gUm9vdCBDQSAtIEcxMIIBIjAN\n" ++ "BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAk0u76WaK7p1b1TST0Bsew+eeuGQz\n" ++ "f2N4aLTNLnF115sgxk0pvLZoYIr3IZpWNVrzdr3YzZr/k1ZLpVkGoZM0Kd0WNHVO\n" ++ "8oG0x5ZOrRkVUkr+PHB1cM2vK6sVmjM8qrOLqs1D/fXqcP/tzxE7lM5OMhbTI0Aq\n" ++ "d7OvPAEsbO2ZLIvZTmmYsvePQbAyeGHWDV/D+qJAkh1cF+ZwPjXnorfCYuKrpDhM\n" ++ "tTk1b+oDafo6VGiFbdbyL0NVHpENDtjVaqSW0RM8LHhQ6DqS0hdW5TUaQBw+jSzt\n" ++ "Od9C4INBdN+jzcKGYEho42kLVACL5HZpIQ15TjQIXhTCzLG3rdd8cIrHhQIDAQAB\n" ++ "o0IwQDAdBgNVHQ4EFgQU++8Nhp6w492pufEhF38+/PB3KxowDgYDVR0PAQH/BAQD\n" ++ "AgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAFn/8oz1h31x\n" ++ "PaOfG1vR2vjTnGs2vZupYeveFix0PZ7mddrXuqe8QhfnPZHr5X3dPpzxz5KsbEjM\n" ++ "wiI/aTvFthUvozXGaCocV685743QNcMYDHsAVhzNixl03r4PEuDQqqE/AjSxcM6d\n" ++ "GNYIAwlG7mDgfrbESQRRfXBgvKqy/3lyeqYdPV8q+Mri/Tm3R7nrft8EI6/6nAYH\n" ++ "6ftjk4BAtcZsCjEozgyfz7MjNYBBjWzEN3uBL4ChQEKF6dk4jeihU80Bv2noWgby\n" ++ "RQuQ+q7hv53yrlc8pa6yVvSLZUDp/TGBLPQ5Cdjua6e0ph0VpZj3AYHYhX3zUVxx\n" ++ "iN66zB+Afko=\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIICDzCCAZWgAwIBAgIUbmq8WapTvpg5Z6LSa6Q75m0c1towCgYIKoZIzj0EAwMw\n" ++ "RzELMAkGA1UEBhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28uLEx0ZC4xGjAY\n" ++ "BgNVBAMTEXZUcnVzIEVDQyBSb290IENBMB4XDTE4MDczMTA3MjY0NFoXDTQzMDcz\n" ++ "MTA3MjY0NFowRzELMAkGA1UEBhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28u\n" ++ "LEx0ZC4xGjAYBgNVBAMTEXZUcnVzIEVDQyBSb290IENBMHYwEAYHKoZIzj0CAQYF\n" ++ "K4EEACIDYgAEZVBKrox5lkqqHAjDo6LN/llWQXf9JpRCux3NCNtzslt188+cToL0\n" ++ "v/hhJoVs1oVbcnDS/dtitN9Ti72xRFhiQgnH+n9bEOf+QP3A2MMrMudwpremIFUd\n" ++ "e4BdS49nTPEQo0IwQDAdBgNVHQ4EFgQUmDnNvtiyjPeyq+GtJK97fKHbH88wDwYD\n" ++ "VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwCgYIKoZIzj0EAwMDaAAwZQIw\n" ++ "V53dVvHH4+m4SVBrm2nDb+zDfSXkV5UTQJtS0zvzQBm8JsctBp61ezaf9SXUY2sA\n" ++ "AjEA6dPGnlaaKsyh2j/IZivTWJwghfqrkYpwcBE4YGQLYgmRWAD5Tfs0aNoJrSEG\n" ++ "GJTO\n" ++ "-----END CERTIFICATE-----\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIFVjCCAz6gAwIBAgIUQ+NxE9izWRRdt86M/TX9b7wFjUUwDQYJKoZIhvcNAQEL\n" ++ "BQAwQzELMAkGA1UEBhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28uLEx0ZC4x\n" ++ "FjAUBgNVBAMTDXZUcnVzIFJvb3QgQ0EwHhcNMTgwNzMxMDcyNDA1WhcNNDMwNzMx\n" ++ "MDcyNDA1WjBDMQswCQYDVQQGEwJDTjEcMBoGA1UEChMTaVRydXNDaGluYSBDby4s\n" ++ "THRkLjEWMBQGA1UEAxMNdlRydXMgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQAD\n" ++ "ggIPADCCAgoCggIBAL1VfGHTuB0EYgWgrmy3cLRB6ksDXhA/kFocizuwZotsSKYc\n" ++ "IrrVQJLuM7IjWcmOvFjai57QGfIvWcaMY1q6n6MLsLOaXLoRuBLpDLvPbmyAhykU\n" ++ "AyyNJJrIZIO1aqwTLDPxn9wsYTwaP3BVm60AUn/PBLn+NvqcwBauYv6WTEN+VRS+\n" ++ "GrPSbcKvdmaVayqwlHeFXgQPYh1jdfdr58tbmnDsPmcF8P4HCIDPKNsFxhQnL4Z9\n" ++ "8Cfe/+Z+M0jnCx5Y0ScrUw5XSmXX+6KAYPxMvDVTAWqXcoKv8R1w6Jz1717CbMdH\n" ++ "flqUhSZNO7rrTOiwCcJlwp2dCZtOtZcFrPUGoPc2BX70kLJrxLT5ZOrpGgrIDajt\n" ++ "J8nU57O5q4IikCc9Kuh8kO+8T/3iCiSn3mUkpF3qwHYw03dQ+A0Em5Q2AXPKBlim\n" ++ "0zvc+gRGE1WKyURHuFE5Gi7oNOJ5y1lKCn+8pu8fA2dqWSslYpPZUxlmPCdiKYZN\n" ++ "pGvu/9ROutW04o5IWgAZCfEF2c6Rsffr6TlP9m8EQ5pV9T4FFL2/s1m02I4zhKOQ\n" ++ "UqqzApVg+QxMaPnu1RcN+HFXtSXkKe5lXa/R7jwXC1pDxaWG6iSe4gUH3DRCEpHW\n" ++ "OXSuTEGC2/KmSNGzm/MzqvOmwMVO9fSddmPmAsYiS8GVP1BkLFTltvA8Kc9XAgMB\n" ++ "AAGjQjBAMB0GA1UdDgQWBBRUYnBj8XWEQ1iO0RYgscasGrz2iTAPBgNVHRMBAf8E\n" ++ "BTADAQH/MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAKbqSSaet\n" ++ "8PFww+SX8J+pJdVrnjT+5hpk9jprUrIQeBqfTNqK2uwcN1LgQkv7bHbKJAs5EhWd\n" ++ "nxEt/Hlk3ODg9d3gV8mlsnZwUKT+twpw1aA08XXXTUm6EdGz2OyC/+sOxL9kLX1j\n" ++ "bhd47F18iMjrjld22VkE+rxSH0Ws8HqA7Oxvdq6R2xCOBNyS36D25q5J08FsEhvM\n" ++ "Kar5CKXiNxTKsbhm7xqC5PD48acWabfbqWE8n/Uxy+QARsIvdLGx14HuqCaVvIiv\n" ++ "TDUHKgLKeBRtRytAVunLKmChZwOgzoy8sHJnxDHO2zTlJQNgJXtxmOTAGytfdELS\n" ++ "S8VZCAeHvsXDf+eW2eHcKJfWjwXj9ZtOyh1QRwVTsMo554WgicEFOwE30z9J4nfr\n" ++ "I8iIZjs9OXYhRvHsXyO466JmdXTBQPfYaJqT4i2pLr0cox7IdMakLXogqzu4sEb9\n" ++ "b91fUlV1YvCXoHzXOP0l382gmxDPi7g4Xl7FtKYCNqEeXxzP4padKar9mK5S4fNB\n" ++ "UvupLnKWnyfjqnN9+BojZns7q2WwMgFLFT49ok8MKzWixtlnEjUwzXYuFrOZnk1P\n" ++ "Ti07NEPhmg4NpGaXutIcSkwsKouLgU9xGqndXHt7CMUADTdA43x7VF8vhV929ven\n" ++ "sBxXVsFy6K2ir40zSbofitzmdHxghm+Hl3s=\n" ++ "-----END CERTIFICATE-----\n" ++}; ++ ++#endif // CERT_H_ +-- +2.54.0 + diff --git a/CMakeLists.txt b/CMakeLists.txt index 32e873f009..00a3b39281 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,15 +1,17 @@ # SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project # SPDX-License-Identifier: GPL-3.0-or-later -cmake_minimum_required(VERSION 3.22) +cmake_minimum_required(VERSION 3.31) project(yuzu) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules") +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules/find") list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externals/cmake-modules") set(CPM_SOURCE_CACHE ${CMAKE_SOURCE_DIR}/.cache/cpm) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +set(CMAKE_CXX_SCAN_FOR_MODULES 0) include(DetectPlatform) include(DetectArchitecture) @@ -61,6 +63,7 @@ if (YUZU_STATIC_ROOM) set(Boost_USE_STATIC_LIBS ON) set(CMAKE_FIND_LIBRARY_SUFFIXES ".a") set(OPENSSL_USE_STATIC_LIBS ON) + set(OpenSSL_FORCE_SYSTEM ON) set(zstd_FORCE_BUNDLED ON) set(fmt_FORCE_BUNDLED ON) @@ -75,6 +78,8 @@ cmake_dependent_option(YUZU_USE_QT_WEB_ENGINE "Use QtWebEngine for web applet im set(YUZU_QT_MIRROR "" CACHE STRING "What mirror to use for downloading the bundled Qt libraries") cmake_dependent_option(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" "${MSVC}" "ENABLE_QT" OFF) +option(ENABLE_DEBUG_TOOLS "Enable debugging tools (maxwell disassembler, SPIRV translator, etc)" OFF) + # non-linux bundled qt are static if (YUZU_USE_BUNDLED_QT AND (APPLE OR NOT UNIX)) set(YUZU_STATIC_BUILD ON) @@ -119,13 +124,13 @@ if (YUZU_STATIC_BUILD) set(QuaZip-Qt6_FORCE_BUNDLED ON) set(YUZU_USE_BUNDLED_FFMPEG ON) - set(YUZU_USE_BUNDLED_SDL2 ON) + set(YUZU_USE_BUNDLED_SDL3 ON) set(YUZU_USE_BUNDLED_OPENSSL ON) set(HTTPLIB_USE_BROTLI_IF_AVAILABLE OFF) elseif(APPLE) set(YUZU_USE_BUNDLED_FFMPEG ON) - set(YUZU_USE_BUNDLED_SDL2 ON) + set(YUZU_USE_BUNDLED_SDL3 ON) set(YUZU_USE_BUNDLED_OPENSSL ON) # these libs do not properly provide static libs/let you do it with cmake @@ -190,13 +195,11 @@ if(MSVC) $<$:$<$:/${libflag}d>> $<$:$<$:/${libflag}>> $<$:$<$:/${libflag}>> - $<$:$<$:/${libflag}>> - ) + $<$:$<$:/${libflag}>>) endif() # TODO(crueter): Cleanup, each dep that has a bundled option should allow to choose between bundled, external, system -cmake_dependent_option(YUZU_USE_EXTERNAL_SDL2 "Build SDL2 from external source" OFF "NOT MSVC;NOT ANDROID" OFF) -cmake_dependent_option(YUZU_USE_BUNDLED_SDL2 "Download bundled SDL2 build" "${MSVC}" "NOT ANDROID" OFF) +cmake_dependent_option(YUZU_USE_BUNDLED_SDL3 "Download bundled SDL3 build" "${MSVC}" "NOT ANDROID" OFF) option(ENABLE_CUBEB "Enables the cubeb audio backend" ON) @@ -230,6 +233,10 @@ cmake_dependent_option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF option(YUZU_TESTS "Compile tests" "${BUILD_TESTING}") +# Install udev rules on Linux (mainly for gyros) +# Only acts on joysticks and nothing else. +cmake_dependent_option(YUZU_INSTALL_UDEV_RULES "Install udev rules for gyro access" OFF "PLATFORM_LINUX" OFF) + option(YUZU_DOWNLOAD_ANDROID_VVL "Download validation layer binary for android" ON) option(YUZU_LEGACY "Apply patches that improve compatibility with older GPUs (e.g. Snapdragon 865) at the cost of performance" OFF) @@ -302,7 +309,7 @@ if (YUZU_ROOM) add_compile_definitions(YUZU_ROOM) endif() -if ((ANDROID OR APPLE OR UNIX) AND (NOT PLATFORM_LINUX OR ANDROID) AND NOT WIN32) +if (UNIX AND NOT (PLATFORM_LINUX OR WIN32)) if(CXX_APPLE OR CXX_CLANG) # libc++ has stop_token and jthread as experimental set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexperimental-library") @@ -358,10 +365,6 @@ if (CXX_GCC OR CXX_CLANG) endif() endif() -# Other presets, e.g. steamdeck -# TODO(crueter): Just have every Linux/Windows use old sdl2 -set(YUZU_SYSTEM_PROFILE "generic" CACHE STRING "CMake and Externals profile to use. One of: generic, steamdeck") - # Configure C++ standard # =========================== @@ -383,13 +386,20 @@ find_package(RenderDoc MODULE) # openssl funniness if (YUZU_USE_BUNDLED_OPENSSL) set(BUILD_SHARED_LIBS OFF) + AddJsonPackage(openssl-ci) +else() AddJsonPackage(openssl) + set(OPENSSL_BUILD_VERBOSE ON) + set(OPENSSL_CONFIGURE_VERBOSE ON) + if (OpenSSL_ADDED) - add_compile_definitions(YUZU_BUNDLED_OPENSSL) + AddJsonPackage(openssl-cmake) endif() endif() -find_package(OpenSSL 3 REQUIRED) +if (OpenSSL_ADDED) + add_compile_definitions(YUZU_BUNDLED_OPENSSL) +endif() message(STATUS "Fetching needed dependencies with CPM") @@ -505,7 +515,7 @@ endfunction() # ============================================= if (APPLE) - foreach(fw Carbon Metal Cocoa IOKit CoreVideo CoreMedia Security) + foreach(fw Carbon Metal Cocoa IOKit CoreVideo CoreMedia Security UniformTypeIdentifiers) find_library(${fw}_LIBRARY ${fw} REQUIRED) list(APPEND PLATFORM_LIBRARIES ${${fw}_LIBRARY}) endforeach() @@ -517,6 +527,8 @@ elseif (WIN32) # PSAPI is the Process Status API set(PLATFORM_LIBRARIES ${PLATFORM_LIBRARIES} psapi imm32 version crypt32 rpcrt4 gdi32 wldap32 mswsock) endif() +elseif (PLATFORM_MANAGARM) + set(PLATFORM_LIBRARIES iconv intl) elseif (PLATFORM_HAIKU) # Haiku is so special :) set(PLATFORM_LIBRARIES bsd /boot/system/lib/libnetwork.so) @@ -559,7 +571,7 @@ if (NOT YUZU_STATIC_ROOM) endif() if (NOT ANDROID) - find_package(SDL2) + find_package(SDL3) endif() if (USE_DISCORD_PRESENCE) @@ -578,14 +590,7 @@ endif() # Qt stuff if (ENABLE_QT) if (YUZU_USE_BUNDLED_QT) - # Qt 6.8+ is broken on macOS (??) - if (APPLE) - AddQt(6.7.3) - else() - AddQt(6.9.3) - endif() - - set(YUZU_STATIC_BUILD ON) + AddQt(Eden-CI/Qt 6.11.1) else() message(STATUS "Using system Qt") if (NOT Qt6_DIR) @@ -594,23 +599,7 @@ if (ENABLE_QT) list(APPEND CMAKE_PREFIX_PATH "${Qt6_DIR}") endif() - find_package(Qt6 CONFIG REQUIRED COMPONENTS Widgets Charts Concurrent) - - if (YUZU_USE_QT_MULTIMEDIA) - find_package(Qt6 REQUIRED COMPONENTS Multimedia) - endif() - - if (PLATFORM_LINUX OR PLATFORM_FREEBSD) - # yes Qt, we get it - set(QT_NO_PRIVATE_MODULE_WARNING ON) - find_package(Qt6 REQUIRED COMPONENTS DBus OPTIONAL_COMPONENTS GuiPrivate) - elseif (UNIX AND NOT APPLE) - find_package(Qt6 REQUIRED COMPONENTS DBus Gui) - endif() - - if (ENABLE_QT_TRANSLATION) - find_package(Qt6 REQUIRED COMPONENTS LinguistTools) - endif() + find_package(Qt6 REQUIRED COMPONENTS Core) if (NOT DEFINED QT_TARGET_PATH) get_target_property(qtcore_path Qt6::Core LOCATION_Release) @@ -633,21 +622,27 @@ if (ENABLE_QT) ## Components ## # Best practice is to ask for all components at once, so they are from the same version - set(YUZU_QT_COMPONENTS Core Widgets Charts Concurrent) - if (PLATFORM_LINUX) + set(YUZU_QT_COMPONENTS Core Widgets Charts Concurrent Gui) + if (PLATFORM_LINUX OR PLATFORM_FREEBSD) list(APPEND YUZU_QT_COMPONENTS DBus) + # yes Qt, we get it + set(QT_NO_PRIVATE_MODULE_WARNING ON) + list(APPEND YUZU_QT_OPTIONAL GuiPrivate) endif() + if (YUZU_USE_QT_MULTIMEDIA) list(APPEND YUZU_QT_COMPONENTS Multimedia) endif() + if (YUZU_USE_QT_WEB_ENGINE) list(APPEND YUZU_QT_COMPONENTS WebEngineCore WebEngineWidgets) endif() + if (ENABLE_QT_TRANSLATION) list(APPEND YUZU_QT_COMPONENTS LinguistTools) endif() - find_package(Qt6 REQUIRED COMPONENTS ${YUZU_QT_COMPONENTS}) + find_package(Qt6 REQUIRED COMPONENTS ${YUZU_QT_COMPONENTS} OPTIONAL_COMPONENTS ${YUZU_QT_OPTIONAL}) set(QT_MAJOR_VERSION 6) # Qt6 sets cxx_std_17 and we need to undo that set_target_properties(Qt6::Platform PROPERTIES INTERFACE_COMPILE_FEATURES "") @@ -715,6 +710,12 @@ endif() add_subdirectory(src) +if (ENABLE_DEBUG_TOOLS) + add_subdirectory(tools/maxwell-disas) + add_subdirectory(tools/maxwell-spirv) + add_subdirectory(tools/maxwell-ir) +endif() + # Set yuzu project or yuzu-cmd project as default StartUp Project in Visual Studio depending on whether QT is enabled or not if(ENABLE_QT) set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT yuzu) @@ -730,6 +731,8 @@ endif() # https://specifications.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html # https://specifications.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-latest.html # https://www.freedesktop.org/software/appstream/docs/ + +# TODO: Icon/install handling n such should be put into dist/CMakeLists.txt if(ENABLE_QT AND UNIX AND NOT APPLE) install(FILES "dist/dev.eden_emu.eden.desktop" DESTINATION "share/applications") @@ -742,3 +745,10 @@ if(ENABLE_QT AND UNIX AND NOT APPLE) install(FILES "dist/dev.eden_emu.eden.metainfo.xml" DESTINATION "share/metainfo") endif() + +if (YUZU_INSTALL_UDEV_RULES) + include(GNUInstallDirs) + + install(FILES "dist/72-eden-input.rules" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/udev/rules.d") +endif() diff --git a/CMakeModules/CPMUtil.cmake b/CMakeModules/CPMUtil.cmake index b992f24083..94b0489432 100644 --- a/CMakeModules/CPMUtil.cmake +++ b/CMakeModules/CPMUtil.cmake @@ -1,23 +1,48 @@ # SPDX-FileCopyrightText: Copyright 2026 crueter # SPDX-License-Identifier: LGPL-3.0-or-later -set(CPM_SOURCE_CACHE "${PROJECT_SOURCE_DIR}/.cache/cpm" CACHE STRING "" FORCE) +cmake_minimum_required(VERSION 3.31) -if(MSVC OR ANDROID) +if(MSVC OR ANDROID OR IOS) set(BUNDLED_DEFAULT ON) else() set(BUNDLED_DEFAULT OFF) endif() +set(CPM_SOURCE_CACHE "${PROJECT_SOURCE_DIR}/.cache/cpm" CACHE STRING "" FORCE) + option(CPMUTIL_FORCE_BUNDLED "Force bundled packages for all CPM depdendencies" ${BUNDLED_DEFAULT}) option(CPMUTIL_FORCE_SYSTEM - "Force system packages for all CPM dependencies (NOT RECOMMENDED)" OFF) + "Force system packages for all CPM dependencies" OFF) + +set(CPMUTIL_PATCH_DIR "${PROJECT_SOURCE_DIR}/.patch" CACHE STRING + "Directory containing patches for packages") -cmake_minimum_required(VERSION 3.22) include(CPM) +# Rudimentary target architecture detection +if (NOT DEFINED ARCHITECTURE) + string(TOLOWER ${CMAKE_SYSTEM_PROCESSOR} processor) + if (processor MATCHES "x86|amd64") + set(CPMUTIL_AMD64 ON) + elseif(processor MATCHES "^aarch64|^arm64|^armv8\.*") + set(CPMUTIL_ARM64 ON) + elseif(processor MATCHES "riscv") + set(CPMUTIL_RISCV64 ON) + endif() +else() + # This block exists for compatibility with my own DetectArchitecture.cmake. + if (ARCHITECTURE_x86_64) + set(CPMUTIL_AMD64 ON) + elseif(ARCHITECTURE_arm64) + set(CPMUTIL_ARM64 ON) + elseif(ARCHITECTURE_riscv64) + set(CPMUTIL_RISCV64 ON) + endif() +endif() + # cpmfile parsing set(CPMUTIL_JSON_FILE "${CMAKE_CURRENT_SOURCE_DIR}/cpmfile.json") @@ -70,7 +95,6 @@ function(get_json_element object out member default) if(out_type STREQUAL "ARRAY") string(JSON _len LENGTH "${object}" ${member}) - # array_to_list("${outvar}" ${_len} outvar) set("${out}_LENGTH" "${_len}" PARENT_SCOPE) endif() @@ -128,7 +152,8 @@ function(AddDependentPackages) message(FATAL_ERROR "Partial dependency installation detected " "for the following packages:\n${package_names}\n" "You can solve this in one of two ways:\n" - "1. Install the following packages to your system if available:" + "1. Install or upgrade the following packages " + "to your system if available:" "\n\t${bundled_names}\n" "2. Set the following variables to ON:" "\n\t${system_names}\n" @@ -147,11 +172,12 @@ macro(parse_object object) get_json_element("${object}" repo repo "") get_json_element("${object}" ci ci OFF) get_json_element("${object}" version version "") + get_json_element("${object}" min_version min_version "") + get_json_element("${object}" git_host git_host "github.com") if(ci) get_json_element("${object}" name name "${JSON_NAME}") get_json_element("${object}" extension extension "tar.zst") - get_json_element("${object}" min_version min_version "") get_json_element("${object}" raw_disabled disabled_platforms "") if(raw_disabled) @@ -162,14 +188,10 @@ macro(parse_object object) endif() else() get_json_element("${object}" hash hash "") - get_json_element("${object}" hash_suffix hash_suffix "") get_json_element("${object}" sha sha "") get_json_element("${object}" url url "") - get_json_element("${object}" key key "") get_json_element("${object}" tag tag "") get_json_element("${object}" artifact artifact "") - get_json_element("${object}" git_version git_version "") - get_json_element("${object}" git_host git_host "") get_json_element("${object}" source_subdir source_subdir "") get_json_element("${object}" bundled bundled "unset") get_json_element("${object}" find_args find_args "") @@ -177,23 +199,17 @@ macro(parse_object object) # okay here comes the fun part: REPLACEMENTS! # first: tag gets %VERSION% replaced if applicable, - # with either git_version (preferred) or version + # with version # second: artifact gets %VERSION% and %TAG% replaced # accordingly (same rules for VERSION) - if(git_version) - set(version_replace ${git_version}) - else() - set(version_replace ${version}) - endif() - # TODO(crueter): fmt module for cmake if(tag) - string(REPLACE "%VERSION%" "${version_replace}" tag ${tag}) + string(REPLACE "%VERSION%" "${version}" tag ${tag}) endif() if(artifact) - string(REPLACE "%VERSION%" "${version_replace}" + string(REPLACE "%VERSION%" "${version}" artifact ${artifact}) string(REPLACE "%TAG%" "${tag}" artifact ${artifact}) endif() @@ -206,7 +222,7 @@ macro(parse_object object) string(JSON _patch GET "${raw_patches}" "${IDX}") set(full_patch - "${PROJECT_SOURCE_DIR}/.patch/${JSON_NAME}/${_patch}") + "${CPMUTIL_PATCH_DIR}/${JSON_NAME}/${_patch}") if(NOT EXISTS ${full_patch}) cpm_utils_message(FATAL_ERROR ${JSON_NAME} "specifies patch ${full_patch} which does not exist") @@ -241,13 +257,16 @@ function(AddJsonPackage) # these are overrides that can be generated at runtime, # so can be defined separately from the json - DOWNLOAD_ONLY - BUNDLED_PACKAGE FORCE_BUNDLED_PACKAGE) set(multiValueArgs OPTIONS) - cmake_parse_arguments(JSON "" "${oneValueArgs}" "${multiValueArgs}" + set(optionArgs MODULE_PATH DOWNLOAD_ONLY) + + cmake_parse_arguments(JSON + "${optionArgs}" + "${oneValueArgs}" + "${multiValueArgs}" "${ARGN}") list(LENGTH ARGN argnLength) @@ -258,8 +277,8 @@ function(AddJsonPackage) endif() if(NOT DEFINED CPMFILE_CONTENT) - cpm_utils_message(WARNING ${name} - "No cpmfile, AddJsonPackage is a no-op") + cpm_utils_message(FATAL_ERROR ${name} + "No cpmfile present") return() endif() @@ -276,16 +295,27 @@ function(AddJsonPackage) parse_object(${object}) + if (JSON_MODULE_PATH) + list(APPEND EXTRA_ARGS MODULE_PATH) + endif() + + if (JSON_DOWNLOAD_ONLY) + list(APPEND EXTRA_ARGS DOWNLOAD_ONLY) + endif() + if(ci) AddCIPackage( - VERSION ${version} - NAME ${name} - REPO ${repo} - PACKAGE ${package} - EXTENSION ${extension} - MIN_VERSION ${min_version} - DISABLED_PLATFORMS ${disabled_platforms}) + VERSION "${version}" + NAME "${name}" + REPO "${repo}" + PACKAGE "${package}" + EXTENSION "${extension}" + MIN_VERSION "${min_version}" + DISABLED_PLATFORMS "${disabled_platforms}" + GIT_HOST "${git_host}" + + ${EXTRA_ARGS}) else() if (NOT DEFINED JSON_FORCE_BUNDLED_PACKAGE) set(JSON_FORCE_BUNDLED_PACKAGE OFF) @@ -294,12 +324,11 @@ function(AddJsonPackage) AddPackage( NAME "${package}" VERSION "${version}" + MIN_VERSION "${min_version}" URL "${url}" HASH "${hash}" - HASH_SUFFIX "${hash_suffix}" SHA "${sha}" REPO "${repo}" - KEY "${key}" PATCHES "${patches}" OPTIONS "${options}" FIND_PACKAGE_ARGUMENTS "${find_args}" @@ -307,68 +336,53 @@ function(AddJsonPackage) FORCE_BUNDLED_PACKAGE "${JSON_FORCE_BUNDLED_PACKAGE}" SOURCE_SUBDIR "${source_subdir}" - GIT_VERSION ${git_version} - GIT_HOST ${git_host} + GIT_HOST "${git_host}" - ARTIFACT ${artifact} - TAG ${tag}) + ARTIFACT "${artifact}" + TAG "${tag}" + + ${EXTRA_ARGS}) endif() # pass stuff to parent scope Propagate(${package}_ADDED) Propagate(${package}_SOURCE_DIR) Propagate(${package}_BINARY_DIR) + Propagate(CMAKE_PREFIX_PATH) endfunction() function(AddPackage) cpm_set_policies() + set(EXTRA_ARGS "") - # TODO(crueter): git clone? - - #[[ - URL configurations, descending order of precedence: - - URL [+ GIT_URL] -> bare URL fetch - - REPO + TAG + ARTIFACT -> github release artifact - - REPO + TAG -> github release archive - - REPO + SHA -> github commit archive - - REPO + BRANCH -> github branch - - Hash configurations, descending order of precedence: - - HASH -> bare sha512sum - - HASH_SUFFIX -> hash grabbed from the URL + this suffix - - HASH_URL -> hash grabbed from a URL - * technically this is unsafe since a hacker can attack that url - - NOTE: hash algo defaults to sha512 - #]] set(oneValueArgs NAME VERSION - GIT_VERSION + MIN_VERSION GIT_HOST REPO TAG ARTIFACT SHA - BRANCH HASH - HASH_SUFFIX - HASH_URL - HASH_ALGO URL - GIT_URL - KEY + SOURCE_SUBDIR BUNDLED_PACKAGE FORCE_BUNDLED_PACKAGE FIND_PACKAGE_ARGUMENTS) set(multiValueArgs OPTIONS PATCHES) - cmake_parse_arguments(PKG_ARGS "" "${oneValueArgs}" "${multiValueArgs}" + set(optionArgs MODULE_PATH DOWNLOAD_ONLY) + + cmake_parse_arguments(PKG_ARGS + "${optionArgs}" + "${oneValueArgs}" + "${multiValueArgs}" "${ARGN}") if(NOT DEFINED PKG_ARGS_NAME) @@ -387,6 +401,7 @@ function(AddPackage) set(CPM_${PKG_ARGS_NAME}_SOURCE ${${PKG_ARGS_NAME}_CUSTOM_DIR}) endif() + # TODO: See if this can be delegated to subshells if(NOT DEFINED PKG_ARGS_GIT_HOST) set(git_host github.com) else() @@ -395,42 +410,22 @@ function(AddPackage) if(DEFINED PKG_ARGS_URL) set(pkg_url ${PKG_ARGS_URL}) - - if(DEFINED PKG_ARGS_REPO) - set(pkg_git_url https://${git_host}/${PKG_ARGS_REPO}) - else() - if(DEFINED PKG_ARGS_GIT_URL) - set(pkg_git_url ${PKG_ARGS_GIT_URL}) - else() - set(pkg_git_url ${pkg_url}) - endif() - endif() + set(pkg_git_url ${pkg_url}) elseif(DEFINED PKG_ARGS_REPO) set(pkg_git_url https://${git_host}/${PKG_ARGS_REPO}) - if(DEFINED PKG_ARGS_TAG) - set(pkg_key ${PKG_ARGS_TAG}) - - if(DEFINED PKG_ARGS_ARTIFACT) - set(pkg_url - "${pkg_git_url}/releases/download/${PKG_ARGS_TAG}/${PKG_ARGS_ARTIFACT}") - else() - set(pkg_url - ${pkg_git_url}/archive/refs/tags/${PKG_ARGS_TAG}.tar.gz) - endif() - elseif(DEFINED PKG_ARGS_SHA) + if(DEFINED PKG_ARGS_SHA) set(pkg_url "${pkg_git_url}/archive/${PKG_ARGS_SHA}.tar.gz") - else() - if(DEFINED PKG_ARGS_BRANCH) - set(PKG_BRANCH ${PKG_ARGS_BRANCH}) + elseif(DEFINED PKG_ARGS_TAG) + set(tag "${PKG_ARGS_TAG}") + if(DEFINED PKG_ARGS_ARTIFACT) + set(artifact "${PKG_ARGS_ARTIFACT}") + set(pkg_url + "${pkg_git_url}/releases/download/${tag}/${artifact}") else() - cpm_utils_message(WARNING ${PKG_ARGS_NAME} - "REPO defined but no TAG, SHA, BRANCH, or URL" - "specified, defaulting to master") - set(PKG_BRANCH master) + set(pkg_url + "${pkg_git_url}/archive/refs/tags/${tag}.tar.gz") endif() - - set(pkg_url ${pkg_git_url}/archive/refs/heads/${PKG_BRANCH}.tar.gz) endif() else() cpm_utils_message(FATAL_ERROR ${PKG_ARGS_NAME} @@ -439,75 +434,24 @@ function(AddPackage) cpm_utils_message(DEBUG ${PKG_ARGS_NAME} "Download URL is ${pkg_url}") - if(NOT DEFINED PKG_ARGS_KEY) - if(DEFINED PKG_ARGS_SHA) - string(SUBSTRING ${PKG_ARGS_SHA} 0 4 pkg_key) - cpm_utils_message(DEBUG ${PKG_ARGS_NAME} - "No custom key defined, using ${pkg_key} from sha") - elseif(DEFINED PKG_ARGS_GIT_VERSION) - set(pkg_key ${PKG_ARGS_GIT_VERSION}) - cpm_utils_message(DEBUG ${PKG_ARGS_NAME} - "No custom key defined, using ${pkg_key}") - elseif(DEFINED PKG_ARGS_TAG) - set(pkg_key ${PKG_ARGS_TAG}) - cpm_utils_message(DEBUG ${PKG_ARGS_NAME} - "No custom key defined, using ${pkg_key}") - elseif(DEFINED PKG_ARGS_VERSION) - set(pkg_key ${PKG_ARGS_VERSION}) - cpm_utils_message(DEBUG ${PKG_ARGS_NAME} - "No custom key defined, using ${pkg_key}") - else() - cpm_utils_message(WARNING ${PKG_ARGS_NAME} - "Could not determine cache key, using CPM defaults") - endif() + if(DEFINED PKG_ARGS_SHA) + string(SUBSTRING ${PKG_ARGS_SHA} 0 4 pkg_key) + elseif(DEFINED PKG_ARGS_VERSION) + set(pkg_key ${PKG_ARGS_VERSION}) + elseif(DEFINED PKG_ARGS_TAG) + set(pkg_key ${PKG_ARGS_TAG}) + elseif(DEFINED PKG_ARGS_MIN_VERSION) + set(pkg_key ${PKG_ARGS_MIN_VERSION}) else() - set(pkg_key ${PKG_ARGS_KEY}) - endif() - - if(DEFINED PKG_ARGS_HASH_ALGO) - set(hash_algo ${PKG_ARGS_HASH_ALGO}) - else() - set(hash_algo SHA512) + cpm_utils_message(FATAL_ERROR ${PKG_ARGS_NAME} + "Could not determine cache key") endif() if(DEFINED PKG_ARGS_HASH) - set(pkg_hash "${hash_algo}=${PKG_ARGS_HASH}") - elseif(DEFINED PKG_ARGS_HASH_SUFFIX) - # funny sanity check - string(TOLOWER ${hash_algo} hash_algo_lower) - string(TOLOWER ${PKG_ARGS_HASH_SUFFIX} suffix_lower) - if(NOT ${suffix_lower} MATCHES ${hash_algo_lower}) - cpm_utils_message(WARNING - "Hash algorithm and hash suffix do not match, errors may occur") - endif() - - set(hash_url ${pkg_url}.${PKG_ARGS_HASH_SUFFIX}) - elseif(DEFINED PKG_ARGS_HASH_URL) - set(hash_url ${PKG_ARGS_HASH_URL}) + set(pkg_hash "SHA512=${PKG_ARGS_HASH}") else() - cpm_utils_message(WARNING ${PKG_ARGS_NAME} - "No hash or hash URL found") - endif() - - if(DEFINED hash_url) - set(outfile ${CMAKE_CURRENT_BINARY_DIR}/${PKG_ARGS_NAME}.hash) - - # TODO(crueter): This is kind of a bad solution - # because "technically" the hash is invalidated each week - # but it works for now kjsdnfkjdnfjksdn - string(TOLOWER ${PKG_ARGS_NAME} lowername) - if(NOT EXISTS ${outfile} AND NOT EXISTS - ${CPM_SOURCE_CACHE}/${lowername}/${pkg_key}) - file(DOWNLOAD ${hash_url} ${outfile}) - endif() - - if(EXISTS ${outfile}) - file(READ ${outfile} pkg_hash_tmp) - endif() - - if(DEFINED ${pkg_hash_tmp}) - set(pkg_hash "${hash_algo}=${pkg_hash_tmp}") - endif() + cpm_utils_message(FATAL_ERROR ${PKG_ARGS_NAME} + "No hash defined") endif() macro(set_precedence local force) @@ -547,9 +491,33 @@ function(AddPackage) set_precedence(ON OFF) endif() - if(DEFINED PKG_ARGS_VERSION) + if(DEFINED PKG_ARGS_MIN_VERSION) list(APPEND EXTRA_ARGS - VERSION ${PKG_ARGS_VERSION}) + VERSION ${PKG_ARGS_MIN_VERSION}) + endif() + + if (PKG_ARGS_FIND_PACKAGE_ARGUMENTS) + list(APPEND EXTRA_ARGS + FIND_PACKAGE_ARGUMENTS "${PKG_ARGS_FIND_PACKAGE_ARGUMENTS}") + endif() + + if (PKG_ARGS_PATCHES) + list(APPEND EXTRA_ARGS + PATCHES "${PKG_ARGS_PATCHES}") + endif() + + if (PKG_ARGS_OPTIONS) + list(APPEND EXTRA_ARGS + OPTIONS "${PKG_ARGS_OPTIONS}") + endif() + + if (PKG_ARGS_SOURCE_SUBDIR) + list(APPEND EXTRA_ARGS + SOURCE_SUBDIR "${PKG_ARGS_SOURCE_SUBDIR}") + endif() + + if (PKG_ARGS_DOWNLOAD_ONLY OR PKG_ARGS_MODULE_PATH) + list(APPEND EXTRA_ARGS DOWNLOAD_ONLY ON) endif() CPMAddPackage( @@ -557,11 +525,7 @@ function(AddPackage) URL ${pkg_url} URL_HASH ${pkg_hash} CUSTOM_CACHE_KEY ${pkg_key} - DOWNLOAD_ONLY ${PKG_ARGS_DOWNLOAD_ONLY} - FIND_PACKAGE_ARGUMENTS ${PKG_ARGS_FIND_PACKAGE_ARGUMENTS} - OPTIONS ${PKG_ARGS_OPTIONS} - PATCHES ${PKG_ARGS_PATCHES} EXCLUDE_FROM_ALL ON ${EXTRA_ARGS} @@ -575,15 +539,15 @@ function(AddPackage) if(DEFINED PKG_ARGS_SHA) set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS ${PKG_ARGS_SHA}) - elseif(DEFINED PKG_ARGS_GIT_VERSION) - set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS - ${PKG_ARGS_GIT_VERSION}) - elseif(DEFINED PKG_ARGS_TAG) - set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS - ${PKG_ARGS_TAG}) elseif(DEFINED PKG_ARGS_VERSION) set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS ${PKG_ARGS_VERSION}) + elseif(DEFINED PKG_ARGS_TAG) + set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS + ${PKG_ARGS_TAG}) + elseif(DEFINED PKG_ARGS_MIN_VERSION) + set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS + ${PKG_ARGS_MIN_VERSION}) else() cpm_utils_message(WARNING ${PKG_ARGS_NAME} "Package has no specified sha, tag, or version") @@ -601,13 +565,14 @@ function(AddPackage) endif() # pass stuff to parent scope - set(${PKG_ARGS_NAME}_ADDED "${${PKG_ARGS_NAME}_ADDED}" - PARENT_SCOPE) - set(${PKG_ARGS_NAME}_SOURCE_DIR "${${PKG_ARGS_NAME}_SOURCE_DIR}" - PARENT_SCOPE) - set(${PKG_ARGS_NAME}_BINARY_DIR "${${PKG_ARGS_NAME}_BINARY_DIR}" - PARENT_SCOPE) + Propagate(${PKG_ARGS_NAME}_ADDED) + Propagate(${PKG_ARGS_NAME}_SOURCE_DIR) + Propagate(${PKG_ARGS_NAME}_BINARY_DIR) + if (PKG_ARGS_MODULE_PATH) + list(PREPEND CMAKE_PREFIX_PATH "${${ARTIFACT_PACKAGE}_SOURCE_DIR}") + Propagate(CMAKE_PREFIX_PATH) + endif() endfunction() # TODO(crueter): we could do an AddMultiArchPackage, multiplatformpackage? @@ -619,11 +584,12 @@ function(AddCIPackage) REPO PACKAGE EXTENSION - MIN_VERSION) + MIN_VERSION + GIT_HOST) set(multiValueArgs DISABLED_PLATFORMS) - set(optionArgs MODULE) + set(optionArgs MODULE_PATH) cmake_parse_arguments(PKG_ARGS "${optionArgs}" @@ -631,6 +597,7 @@ function(AddCIPackage) "${multiValueArgs}" ${ARGN}) + # TODO: use cpm_utils_message if(NOT DEFINED PKG_ARGS_VERSION) message(FATAL_ERROR "[CPMUtil] VERSION is required") endif() @@ -656,6 +623,12 @@ function(AddCIPackage) set(ARTIFACT_EXT ${PKG_ARGS_EXTENSION}) endif() + if (NOT DEFINED PKG_ARGS_GIT_HOST) + set(ARTIFACT_GIT_HOST "github.com") + else() + set(ARTIFACT_GIT_HOST "${PKG_ARGS_GIT_HOST}") + endif() + if(DEFINED PKG_ARGS_MIN_VERSION) set(ARTIFACT_MIN_VERSION ${PKG_ARGS_MIN_VERSION}) endif() @@ -670,75 +643,114 @@ function(AddCIPackage) set(ARTIFACT_REPO ${PKG_ARGS_REPO}) set(ARTIFACT_PACKAGE ${PKG_ARGS_PACKAGE}) - if(MSVC AND ARCHITECTURE_x86_64) - set(pkgname windows-amd64) - elseif(MSVC AND ARCHITECTURE_arm64) - set(pkgname windows-arm64) - elseif(MINGW AND ARCHITECTURE_x86_64) - set(pkgname mingw-amd64) - elseif(MINGW AND ARCHITECTURE_arm64) - set(pkgname mingw-arm64) - elseif(ANDROID AND ARCHITECTURE_x86_64) - set(pkgname android-x86_64) - elseif(ANDROID AND ARCHITECTURE_arm64) - set(pkgname android-aarch64) - elseif(PLATFORM_SUN) - set(pkgname solaris-amd64) - elseif(PLATFORM_FREEBSD) - set(pkgname freebsd-amd64) - elseif(PLATFORM_LINUX AND ARCHITECTURE_x86_64) - set(pkgname linux-amd64) - elseif(PLATFORM_LINUX AND ARCHITECTURE_arm64) - set(pkgname linux-aarch64) + # TODO: Use amd64/aarch64 naming for everything. + # Also drop macos universal + + if (MSVC) + set(platname windows) + elseif(MINGW) + set(platname mingw) + elseif(ANDROID) + set(platname android) + elseif(LINUX) + set(platname linux) + elseif(IOS) + set(platname ios) elseif(APPLE) - set(pkgname macos-universal) + set(platname macos) + else() + cpm_utils_message(WARNING ${PKG_ARGS_NAME} + "Unsupported platform ${CMAKE_SYSTEM_NAME} for CI packages") endif() - if (DEFINED pkgname AND NOT "${pkgname}" IN_LIST DISABLED_PLATFORMS) + if (APPLE AND NOT IOS) + set(archname universal) + elseif((WIN32 OR LINUX) AND CPMUTIL_AMD64) + set(archname amd64) + elseif(WIN32 AND CPMUTIL_ARM64) + set(archname arm64) + elseif((IOS OR LINUX OR ANDROID) AND CPMUTIL_ARM64) + set(archname aarch64) + elseif(ANDROID AND CPMUTIL_AMD64) + set(archname x86_64) + else() + cpm_utils_message(WARNING ${PKG_ARGS_NAME} + "Unsupported platform/arch combo for CI packages") + endif() + + if (DEFINED platname AND DEFINED archname) + set(pkgname ${platname}-${archname}) + endif() + + if (DEFINED pkgname + AND NOT "${pkgname}" IN_LIST DISABLED_PLATFORMS) set(ARTIFACT "${ARTIFACT_NAME}-${pkgname}-${ARTIFACT_VERSION}.${ARTIFACT_EXT}") + if (PKG_ARGS_MODULE_PATH) + set(EXTRA_ARGS MODULE_PATH) + endif() + + # download sha512sum file + # TODO: + set(sha512sum_url + "https://${ARTIFACT_GIT_HOST}/${ARTIFACT_REPO}/releases/download/v${ARTIFACT_VERSION}/${ARTIFACT}.sha512sum") + set(sha512sum_file + "${CMAKE_CURRENT_BINARY_DIR}/.cpmutil_${ARTIFACT}_sha512sum") + + file(DOWNLOAD "${sha512sum_url}" "${sha512sum_file}" + STATUS sha512sum_status) + list(GET sha512sum_status 0 sha512sum_error) + + if(sha512sum_error) + message(FATAL_ERROR "[CPMUtil] Failed to download sha512sum " + "for ${ARTIFACT_NAME} from ${sha512sum_url}") + endif() + + file(READ "${sha512sum_file}" sha512sum_hash) + string(STRIP "${sha512sum_hash}" sha512sum_hash) + file(REMOVE "${sha512sum_file}") + AddPackage( NAME ${ARTIFACT_PACKAGE} REPO ${ARTIFACT_REPO} TAG "v${ARTIFACT_VERSION}" - GIT_VERSION ${ARTIFACT_VERSION} + MIN_VERSION ${ARTIFACT_VERSION} ARTIFACT ${ARTIFACT} - - KEY "${pkgname}-${ARTIFACT_VERSION}" - HASH_SUFFIX sha512sum + HASH ${sha512sum_hash} FORCE_BUNDLED_PACKAGE ON - DOWNLOAD_ONLY ${PKG_ARGS_MODULE}) + ${EXTRA_ARGS}) set(${ARTIFACT_PACKAGE}_ADDED TRUE PARENT_SCOPE) - set(${ARTIFACT_PACKAGE}_SOURCE_DIR - "${${ARTIFACT_PACKAGE}_SOURCE_DIR}" PARENT_SCOPE) - - if (PKG_ARGS_MODULE) - list(APPEND CMAKE_PREFIX_PATH "${${ARTIFACT_PACKAGE}_SOURCE_DIR}") - set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} PARENT_SCOPE) - endif() + Propagate(${ARTIFACT_PACKAGE}_SOURCE_DIR) + Propagate(CMAKE_PREFIX_PATH) else() find_package(${ARTIFACT_PACKAGE} ${ARTIFACT_MIN_VERSION} REQUIRED) endif() endfunction() # Utility function for Qt -function(AddQt version) +function(AddQt repo version) + if (NOT DEFINED repo) + message(FATAL_ERROR "[CPMUtil] AddQt: repo is required") + endif() + if (NOT DEFINED version) message(FATAL_ERROR "[CPMUtil] AddQt: version is required") endif() AddCIPackage( - NAME Qt + NAME qt PACKAGE Qt6 VERSION ${version} MIN_VERSION 6 - REPO crueter-ci/Qt + REPO ${repo} DISABLED_PLATFORMS android-x86_64 android-aarch64 - freebsd-amd64 solaris-amd64 openbsd-amd64 - MODULE) + MODULE_PATH) - set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} PARENT_SCOPE) + find_package(Qt6 REQUIRED PATHS ${Qt6_SOURCE_DIR} NO_DEFAULT_PATH) + + Propagate(CMAKE_PREFIX_PATH) + Propagate(Qt6_SOURCE_DIR) endfunction() diff --git a/CMakeModules/WindowsCopyFiles.cmake b/CMakeModules/WindowsCopyFiles.cmake deleted file mode 100644 index a4afeb77bf..0000000000 --- a/CMakeModules/WindowsCopyFiles.cmake +++ /dev/null @@ -1,39 +0,0 @@ -# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project -# SPDX-License-Identifier: GPL-3.0-or-later - -# SPDX-FileCopyrightText: 2018 yuzu Emulator Project -# SPDX-License-Identifier: GPL-2.0-or-later - -# This file provides the function windows_copy_files. -# This is only valid on Windows. - -# Include guard -if(__windows_copy_files) - return() -endif() -set(__windows_copy_files YES) - -# Any number of files to copy from SOURCE_DIR to DEST_DIR can be specified after DEST_DIR. -# This copying happens post-build. -if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") - function(windows_copy_files TARGET SOURCE_DIR DEST_DIR) - # windows commandline expects the / to be \ so switch them - string(REPLACE "/" "\\\\" SOURCE_DIR ${SOURCE_DIR}) - string(REPLACE "/" "\\\\" DEST_DIR ${DEST_DIR}) - - # /NJH /NJS /NDL /NFL /NC /NS /NP - Silence any output - # cmake adds an extra check for command success which doesn't work too well with robocopy - # so trick it into thinking the command was successful with the || cmd /c "exit /b 0" - add_custom_command(TARGET ${TARGET} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E make_directory ${DEST_DIR} - COMMAND robocopy ${SOURCE_DIR} ${DEST_DIR} ${ARGN} /NJH /NJS /NDL /NFL /NC /NS /NP || cmd /c "exit /b 0" - ) - endfunction() -else() - function(windows_copy_files TARGET SOURCE_DIR DEST_DIR) - add_custom_command(TARGET ${TARGET} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E make_directory ${DEST_DIR} - COMMAND cp -ra ${SOURCE_DIR}/. ${DEST_DIR} - ) - endfunction() -endif() diff --git a/CMakeModules/aqt_config.ini b/CMakeModules/aqt_config.ini deleted file mode 100644 index c87caf4988..0000000000 --- a/CMakeModules/aqt_config.ini +++ /dev/null @@ -1,30 +0,0 @@ -# SPDX-FileCopyrightText: 2024 kleidis - -[aqt] -concurrency: 2 - -[mirrors] -trusted_mirrors: - https://download.qt.io -blacklist: - https://qt.mirror.constant.com - https://mirrors.ocf.berkeley.edu - https://mirrors.ustc.edu.cn - https://mirrors.tuna.tsinghua.edu.cn - https://mirrors.geekpie.club - https://mirrors-wan.geekpie.club - https://mirrors.sjtug.sjtu.edu.cn -fallbacks: - https://qtproject.mirror.liquidtelecom.com/ - https://mirrors.aliyun.com/qt/ - https://ftp.jaist.ac.jp/pub/qtproject/ - https://ftp.yz.yamagata-u.ac.jp/pub/qtproject/ - https://qt-mirror.dannhauer.de/ - https://ftp.fau.de/qtproject/ - https://mirror.netcologne.de/qtproject/ - https://mirrors.dotsrc.org/qtproject/ - https://www.nic.funet.fi/pub/mirrors/download.qt-project.org/ - https://master.qt.io/ - https://mirrors.ukfast.co.uk/sites/qt.io/ - https://ftp2.nluug.nl/languages/qt/ - https://ftp1.nluug.nl/languages/qt/ diff --git a/CMakeModules/FindDiscordRPC.cmake b/CMakeModules/find/FindDiscordRPC.cmake similarity index 93% rename from CMakeModules/FindDiscordRPC.cmake rename to CMakeModules/find/FindDiscordRPC.cmake index 5ae9f4f999..a498cb0f26 100644 --- a/CMakeModules/FindDiscordRPC.cmake +++ b/CMakeModules/find/FindDiscordRPC.cmake @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project # SPDX-License-Identifier: GPL-3.0-or-later # SPDX-FileCopyrightText: 2022 Alexandre Bouvier diff --git a/CMakeModules/FindFFmpeg.cmake b/CMakeModules/find/FindFFmpeg.cmake similarity index 98% rename from CMakeModules/FindFFmpeg.cmake rename to CMakeModules/find/FindFFmpeg.cmake index edd0e0a882..11dd08acd5 100644 --- a/CMakeModules/FindFFmpeg.cmake +++ b/CMakeModules/find/FindFFmpeg.cmake @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project # SPDX-License-Identifier: GPL-3.0-or-later # SPDX-FileCopyrightText: 2019 Citra Emulator Project diff --git a/CMakeModules/FindLLVM.cmake b/CMakeModules/find/FindLLVM.cmake similarity index 82% rename from CMakeModules/FindLLVM.cmake rename to CMakeModules/find/FindLLVM.cmake index 8dc064d5d1..f169f134d5 100644 --- a/CMakeModules/FindLLVM.cmake +++ b/CMakeModules/find/FindLLVM.cmake @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +# SPDX-License-Identifier: GPL-3.0-or-later + # SPDX-FileCopyrightText: 2023 Alexandre Bouvier # # SPDX-License-Identifier: GPL-3.0-or-later @@ -13,7 +16,8 @@ endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(LLVM HANDLE_COMPONENTS CONFIG_MODE) -if (LLVM_FOUND AND LLVM_Demangle_FOUND AND NOT TARGET LLVM::Demangle) +# Demangle only for Windows targets +if (WIN32 AND LLVM_FOUND AND LLVM_Demangle_FOUND AND NOT TARGET LLVM::Demangle) add_library(LLVM::Demangle INTERFACE IMPORTED) target_compile_definitions(LLVM::Demangle INTERFACE ${LLVM_DEFINITIONS}) target_include_directories(LLVM::Demangle INTERFACE ${LLVM_INCLUDE_DIRS}) diff --git a/CMakeModules/FindOpus.cmake b/CMakeModules/find/FindOpus.cmake similarity index 89% rename from CMakeModules/FindOpus.cmake rename to CMakeModules/find/FindOpus.cmake index 5557cd1baf..a4247f06db 100644 --- a/CMakeModules/FindOpus.cmake +++ b/CMakeModules/find/FindOpus.cmake @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project # SPDX-License-Identifier: GPL-3.0-or-later # SPDX-FileCopyrightText: 2022 yuzu Emulator Project diff --git a/CMakeModules/FindRenderDoc.cmake b/CMakeModules/find/FindRenderDoc.cmake similarity index 83% rename from CMakeModules/FindRenderDoc.cmake rename to CMakeModules/find/FindRenderDoc.cmake index 2678b936bc..8ac4271a36 100644 --- a/CMakeModules/FindRenderDoc.cmake +++ b/CMakeModules/find/FindRenderDoc.cmake @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +# SPDX-License-Identifier: GPL-3.0-or-later + # SPDX-FileCopyrightText: 2023 Alexandre Bouvier # # SPDX-License-Identifier: GPL-3.0-or-later diff --git a/CMakeModules/FindSimpleIni.cmake b/CMakeModules/find/FindSimpleIni.cmake similarity index 86% rename from CMakeModules/FindSimpleIni.cmake rename to CMakeModules/find/FindSimpleIni.cmake index 13426b25ba..1d2379fc82 100644 --- a/CMakeModules/FindSimpleIni.cmake +++ b/CMakeModules/find/FindSimpleIni.cmake @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +# SPDX-License-Identifier: GPL-3.0-or-later + # SPDX-FileCopyrightText: 2023 Alexandre Bouvier # # SPDX-License-Identifier: GPL-3.0-or-later diff --git a/CMakeModules/Findenet.cmake b/CMakeModules/find/Findenet.cmake similarity index 89% rename from CMakeModules/Findenet.cmake rename to CMakeModules/find/Findenet.cmake index d80e737ebb..2a2d1d807d 100644 --- a/CMakeModules/Findenet.cmake +++ b/CMakeModules/find/Findenet.cmake @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project # SPDX-License-Identifier: GPL-3.0-or-later # SPDX-FileCopyrightText: 2022 Alexandre Bouvier diff --git a/CMakeModules/Findgamemode.cmake b/CMakeModules/find/Findgamemode.cmake similarity index 81% rename from CMakeModules/Findgamemode.cmake rename to CMakeModules/find/Findgamemode.cmake index aa2f366832..3ec966c82d 100644 --- a/CMakeModules/Findgamemode.cmake +++ b/CMakeModules/find/Findgamemode.cmake @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +# SPDX-License-Identifier: GPL-3.0-or-later + # SPDX-FileCopyrightText: 2023 yuzu Emulator Project # SPDX-License-Identifier: GPL-2.0-or-later diff --git a/CMakeModules/Findhttplib.cmake b/CMakeModules/find/Findhttplib.cmake similarity index 90% rename from CMakeModules/Findhttplib.cmake rename to CMakeModules/find/Findhttplib.cmake index 48967add96..e92a9be63f 100644 --- a/CMakeModules/Findhttplib.cmake +++ b/CMakeModules/find/Findhttplib.cmake @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +# SPDX-License-Identifier: GPL-3.0-or-later + # SPDX-FileCopyrightText: 2022 Andrea Pappacoda # # SPDX-License-Identifier: GPL-2.0-or-later diff --git a/CMakeModules/Findlibiw.cmake b/CMakeModules/find/Findlibiw.cmake similarity index 92% rename from CMakeModules/Findlibiw.cmake rename to CMakeModules/find/Findlibiw.cmake index 1d13d7705a..3381db0f08 100644 --- a/CMakeModules/Findlibiw.cmake +++ b/CMakeModules/find/Findlibiw.cmake @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project # SPDX-License-Identifier: GPL-3.0-or-later find_package(PkgConfig QUIET) diff --git a/CMakeModules/Findlibusb.cmake b/CMakeModules/find/Findlibusb.cmake similarity index 90% rename from CMakeModules/Findlibusb.cmake rename to CMakeModules/find/Findlibusb.cmake index 87b174c5b4..dee8187d81 100644 --- a/CMakeModules/Findlibusb.cmake +++ b/CMakeModules/find/Findlibusb.cmake @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project # SPDX-License-Identifier: GPL-3.0-or-later # SPDX-FileCopyrightText: 2022 Alexandre Bouvier diff --git a/CMakeModules/Findlz4.cmake b/CMakeModules/find/Findlz4.cmake similarity index 93% rename from CMakeModules/Findlz4.cmake rename to CMakeModules/find/Findlz4.cmake index af230166eb..13235d7ac8 100644 --- a/CMakeModules/Findlz4.cmake +++ b/CMakeModules/find/Findlz4.cmake @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project # SPDX-License-Identifier: GPL-3.0-or-later # SPDX-FileCopyrightText: 2022 yuzu Emulator Project diff --git a/CMakeModules/Findstb.cmake b/CMakeModules/find/Findstb.cmake similarity index 89% rename from CMakeModules/Findstb.cmake rename to CMakeModules/find/Findstb.cmake index bff9985802..4808c45397 100644 --- a/CMakeModules/Findstb.cmake +++ b/CMakeModules/find/Findstb.cmake @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +# SPDX-License-Identifier: GPL-3.0-or-later + # SPDX-FileCopyrightText: 2023 Alexandre Bouvier # # SPDX-License-Identifier: GPL-3.0-or-later diff --git a/CMakeModules/Findzstd.cmake b/CMakeModules/find/Findzstd.cmake similarity index 95% rename from CMakeModules/Findzstd.cmake rename to CMakeModules/find/Findzstd.cmake index 8e44b7a892..58f0aa6117 100644 --- a/CMakeModules/Findzstd.cmake +++ b/CMakeModules/find/Findzstd.cmake @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project # SPDX-License-Identifier: GPL-3.0-or-later # SPDX-FileCopyrightText: 2022 yuzu Emulator Project diff --git a/CMakeModules/toolchains/GentooCross.cmake b/CMakeModules/toolchains/GentooCross.cmake new file mode 100644 index 0000000000..0bb78795fd --- /dev/null +++ b/CMakeModules/toolchains/GentooCross.cmake @@ -0,0 +1,39 @@ +# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +# SPDX-License-Identifier: GPL-3.0-or-later + +set(CROSS_TARGET "" CACHE STRING "Cross-compilation target (aarch64, etc)") +set(CROSS_PLATFORM "unknown-linux-gnu" CACHE STRING "Cross-compilation platform (e.g. unknown-linux-gnu)") +set(CROSS_COMPILER "gcc" CACHE STRING "Cross compiler type (gcc or clang)") + +if (NOT CROSS_TARGET) + message(FATAL_ERROR "GentooCross used without a valid CROSS_TARGET") +endif() + +set(prefix ${CROSS_TARGET}-${CROSS_PLATFORM}) + +set(CMAKE_SYSROOT /usr/${prefix}) + +if (CROSS_COMPILER STREQUAL "gcc") + set(CMAKE_C_COMPILER ${prefix}-gcc) + set(CMAKE_CXX_COMPILER ${prefix}-g++) +elseif (CROSS_COMPILER STREQUAL "clang") + set(CMAKE_C_COMPILER ${prefix}-clang) + set(CMAKE_CXX_COMPILER ${prefix}-clang++) +else() + message(FATAL_ERROR "Unsupported cross compiler type ${CROSS_COMPILER}") +endif() + +# search programs in the host environment +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + +# search headers and libraries in the target environment +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) + +# sanity checks +if (NOT IS_DIRECTORY ${CMAKE_SYSROOT}) + message(FATAL_ERROR "Invalid sysroot ${CMAKE_SYSROOT}." + "Double-check your CROSS_TARGET and CROSS_PLATFORM.") +endif() diff --git a/cpmfile.json b/cpmfile.json index 1bb29afae4..5bfb7ebb63 100644 --- a/cpmfile.json +++ b/cpmfile.json @@ -1,118 +1,404 @@ { - "openssl": { - "ci": true, - "package": "OpenSSL", - "name": "openssl", - "repo": "crueter-ci/OpenSSL", - "version": "3.6.0-1cb0d36b39", - "min_version": "3" + "biscuit": { + "hash": "1229f345b014f7ca544dedb4edb3311e41ba736f9aa9a67f88b5f26f3c983288c6bb6cdedcfb0b8a02c63088a37e6a0d7ba97d9c2a4d721b213916327cffe28a", + "min_version": "0.9.1", + "repo": "lioncash/biscuit", + "tag": "v%VERSION%", + "version": "0.19.0" }, "boost": { - "package": "Boost", - "repo": "boostorg/boost", - "tag": "boost-%VERSION%", "artifact": "%TAG%-cmake.tar.xz", - "hash": "6ae6e94664fe7f2fb01976b59b276ac5df8085c7503fa829d810fbfe495960cfec44fa2c36e2cb23480bc19c956ed199d4952b02639a00a6c07625d4e7130c2d", - "git_version": "1.90.0", - "version": "1.57", "find_args": "CONFIG OPTIONAL_COMPONENTS headers context system fiber filesystem", + "hash": "6ae6e94664fe7f2fb01976b59b276ac5df8085c7503fa829d810fbfe495960cfec44fa2c36e2cb23480bc19c956ed199d4952b02639a00a6c07625d4e7130c2d", + "min_version": "1.57", + "package": "Boost", "patches": [ "0001-clang-cl.patch" - ] + ], + "repo": "boostorg/boost", + "tag": "boost-%VERSION%", + "version": "1.90.0" + }, + "boost_headers": { + "bundled": true, + "hash": "4ef845775e2277a8104ded6ddf749aa262ce52cf8438042869a048f9a0156dd772fbbcfa74efa1378fecef339b7286f6fe4b4feb5c45d49966b35d08e3e83507", + "repo": "boostorg/headers", + "tag": "boost-%VERSION%", + "version": "1.90.0" + }, + "catch2": { + "hash": "7eea385d79d88a5690cde131fe7ccda97d5c54ea09d6f515000d7bf07c828809d61c1ac99912c1ee507cf933f61c1c47ecdcc45df7850ffa82714034b0fccf35", + "min_version": "3.0.1", + "package": "Catch2", + "patches": [ + "0001-solaris-isnan-fix.patch" + ], + "repo": "catchorg/Catch2", + "tag": "v%VERSION%", + "version": "3.13.0" + }, + "cpp-jwt": { + "find_args": "CONFIG", + "hash": "d11cbd5ddb3197b4c5ca15679bcd76a49963e7b530b7dd132db91e042925efa20dfb2c24ccfbe7ef82a7012af80deff0f72ee25851312ae80381a462df8534b8", + "min_version": "1.4", + "options": [ + "CPP_JWT_USE_VENDORED_NLOHMANN_JSON OFF" + ], + "patches": [ + "0001-fix-missing-decl.patch" + ], + "repo": "arun11299/cpp-jwt", + "sha": "7f24eb4c32", + "version": "1.5.1" + }, + "cubeb": { + "find_args": "CONFIG", + "hash": "8a4bcb2f83ba590f52c66626e895304a73eb61928dbc57777e1822e55378e3568366f17f9da4b80036cc2ef4ea9723c32abf6e7d9bbe00fb03654f0991596ab0", + "options": [ + "USE_SANITIZERS OFF", + "BUILD_TESTS OFF", + "BUILD_TOOLS OFF", + "BUNDLE_SPEEX ON" + ], + "repo": "mozilla/cubeb", + "sha": "fa02160712", + "version": "0.0.0" + }, + "discord-rpc": { + "find_args": "MODULE", + "hash": "8213c43dcb0f7d479f5861091d111ed12fbdec1e62e6d729d65a4bc181d82f48a35d5fd3cd5c291f2393ac7c9681eabc6b76609755f55376284c8a8d67e148f3", + "package": "DiscordRPC", + "repo": "eden-emulator/discord-rpc", + "sha": "0d8b2d6a37", + "version": "3.4.1" + }, + "enet": { + "find_args": "MODULE", + "hash": "a0d2fa8c957704dd49e00a726284ac5ca034b50b00d2b20a94fa1bbfbb80841467834bfdc84aa0ed0d6aab894608fd6c86c3b94eee46343f0e6d9c22e391dbf9", + "min_version": "1.3", + "repo": "lsalzman/enet", + "tag": "v%VERSION%", + "version": "1.3.18" + }, + "ffmpeg": { + "bundled": true, + "hash": "ed177621176b3961bdcaa339187d3a7688c1c8b060b79c4bb0257cbc67ad7021ae5d5adca5303b45625abbbe3d9aafdd87ce777b8690ac295290d744c875489a", + "repo": "FFmpeg/FFmpeg", + "sha": "c7b5f1537d", + "version": "8.0.1" + }, + "ffmpeg-ci": { + "ci": true, + "min_version": "4.1", + "name": "ffmpeg", + "package": "FFmpeg", + "repo": "crueter-ci/FFmpeg", + "version": "8.0.1-c7b5f1537d" }, "fmt": { + "hash": "f0da82c545b01692e9fd30fdfb613dbb8dd9716983dcd0ff19ac2a8d36f74beb5540ef38072fdecc1e34191b3682a8542ecbf3a61ef287dbba0a2679d4e023f2", + "min_version": "8", "repo": "fmtlib/fmt", "tag": "%VERSION%", - "hash": "f0da82c545b01692e9fd30fdfb613dbb8dd9716983dcd0ff19ac2a8d36f74beb5540ef38072fdecc1e34191b3682a8542ecbf3a61ef287dbba0a2679d4e023f2", - "version": "8", - "git_version": "12.1.0" + "version": "12.1.0" + }, + "frozen": { + "hash": "b8dfe741c82bc178dfc9749d4ab5a130cee718d9ee7b71d9b547cf5f7f23027ed0152ad250012a8546399fcc1e12187efc68d89d6731256c4d2df7d04eef8d5c", + "package": "frozen", + "repo": "serge-sans-paille/frozen", + "sha": "61dce5ae18", + "version": "1.2.0" + }, + "gamemode": { + "find_args": "MODULE", + "hash": "e87ec14ed3e826d578ebf095c41580069dda603792ba91efa84f45f4571a28f4d91889675055fd6f042d7dc25b0b9443daf70963ae463e38b11bcba95f4c65a9", + "min_version": "1.7", + "repo": "FeralInteractive/gamemode", + "sha": "ce6fe122f3", + "version": "1.8.2" + }, + "httplib": { + "find_args": "MODULE GLOBAL", + "hash": "159ed94965018f2a371d45a3bfc1961e5fb1549e501ded70a6b4532d7fe99d0579c18b5195aff6e35f96f399b426cea2650ec9fb75ef80d4c9edeccb51f2e6c9", + "options": [ + "HTTPLIB_REQUIRE_OPENSSL ON", + "HTTPLIB_DISABLE_MACOSX_AUTOMATIC_ROOT_CERTIFICATES ON" + ], + "patches": [ + "0001-mingw.patch" + ], + "repo": "yhirose/cpp-httplib", + "tag": "v%VERSION%", + "version": "0.46.0" + }, + "lagoon": { + "hash": "b9380f99c6effaeccc6d8f81d4942e852c11ad28613df637e155451556ae5826f93765bee57a5c87a9740d2bd1db463ad0f55a947772fe9d57eeabae3efa373e", + "repo": "loongson-community/lagoon", + "tag": "%VERSION%", + "version": "1.0.0" + }, + "libadrenotools": { + "hash": "f6526620cb752876edc5ed4c0925d57b873a8218ee09ad10859ee476e9333259784f61c1dcc55a2bcba597352d18aff22cd2e4c1925ec2ae94074e09d7da2265", + "patches": [ + "0001-linkerns-cpm.patch" + ], + "repo": "eden-emulator/libadrenotools", + "sha": "8ba23b42d7", + "version": "1.0.0" + }, + "libusb": { + "find_args": "MODULE", + "hash": "98c5f7940ff06b25c9aa65aa98e23de4c79a4c1067595f4c73cc145af23a1c286639e1ba11185cd91bab702081f307b973f08a4c9746576dc8d01b3620a3aeb5", + "patches": [ + "0001-netbsd-gettime.patch" + ], + "repo": "libusb/libusb", + "tag": "v%VERSION%", + "version": "1.0.29" + }, + "llvm-mingw": { + "artifact": "clang-rt-builtins.tar.zst", + "git_host": "git.eden-emu.dev", + "hash": "d902392caf94e84f223766e2cc51ca5fab6cae36ab8dc6ef9ef6a683ab1c483bfcfe291ef0bd38ab16a4ecc4078344fa8af72da2f225ab4c378dee23f6186181", + "repo": "eden-emu/llvm-mingw", + "tag": "%VERSION%", + "version": "20250828" }, "lz4": { + "hash": "35c21a5d9cfb5bbf314a5321d02b36819491d2ee3cf8007030ca09d13ca4dae672247b7aeab553e973093604fc48221cb03dc92197c6efe8fc3746891363fdab", "name": "lz4", "repo": "lz4/lz4", "sha": "ebb370ca83", - "hash": "35c21a5d9cfb5bbf314a5321d02b36819491d2ee3cf8007030ca09d13ca4dae672247b7aeab553e973093604fc48221cb03dc92197c6efe8fc3746891363fdab", - "source_subdir": "build/cmake" + "source_subdir": "build/cmake", + "version": "1.10.0" + }, + "moltenvk": { + "artifact": "MoltenVK-macOS.tar", + "bundled": true, + "hash": "5695b36ca5775819a71791557fcb40a4a5ee4495be6b8442e0b666d0c436bec02aae68cc6210183f7a5c986bdbec0e117aecfad5396e496e9c2fd5c89133a347", + "repo": "V380-Ori/Ryujinx.MoltenVK", + "tag": "v%VERSION%-ryujinx", + "version": "1.4.1" }, "nlohmann": { + "hash": "6cc1e86261f8fac21cc17a33da3b6b3c3cd5c116755651642af3c9e99bb3538fd42c1bd50397a77c8fb6821bc62d90e6b91bcdde77a78f58f2416c62fc53b97d", + "min_version": "3.8", "package": "nlohmann_json", "repo": "nlohmann/json", "tag": "v%VERSION%", - "hash": "6cc1e86261f8fac21cc17a33da3b6b3c3cd5c116755651642af3c9e99bb3538fd42c1bd50397a77c8fb6821bc62d90e6b91bcdde77a78f58f2416c62fc53b97d", - "version": "3.8", - "git_version": "3.12.0" + "version": "3.12.0" }, - "zlib": { - "package": "ZLIB", - "repo": "madler/zlib", + "oaknut": { + "hash": "9697e80a7d5d9bcb3ce51051a9a24962fb90ca79d215f1f03ae6b58da8ba13a63b5dda1b4dde3d26ac6445029696b8ef2883f4e5a777b342bba01283ed293856", + "min_version": "2.0.1", + "repo": "eden-emulator/oaknut", "tag": "v%VERSION%", - "hash": "16fea4df307a68cf0035858abe2fd550250618a97590e202037acd18a666f57afc10f8836cbbd472d54a0e76539d0e558cb26f059d53de52ff90634bbf4f47d4", - "version": "1.2", - "git_version": "1.3.2", - "options": [ - "ZLIB_BUILD_SHARED OFF", - "ZLIB_INSTALL OFF" - ] + "version": "2.0.3" }, - "zstd": { - "repo": "facebook/zstd", - "sha": "b8d6101fba", - "hash": "cc5ad4b119a9c2ea57f0b71eeff01113bb506e0d17000159c5409cb8236d22e38c52d5e9e97e7947a4bf1b2dfc44b6c503ab2d9aedbd59458435c6a2849cb029", - "version": "1.5", - "source_subdir": "build/cmake", - "find_args": "MODULE", + "oboe": { + "bundled": true, + "hash": "ce4011afe7345370d4ead3b891cd69a5ef224b129535783586c0ca75051d303ed446e6c7f10bde8da31fff58d6e307f1732a3ffd03b249f9ef1fd48fd4132715", + "repo": "google/oboe", + "tag": "%VERSION%", + "version": "1.10.0" + }, + "openssl": { + "hash": "29002ce50cb95a4f4f1d0e9d3f684401fbd4eac34203dc2eef3b6334af5d44aa46bf788b63a6f5c139c383eafb7269ae87a58a9a3ad5912903b9773e545ccc0a", + "min_version": "3.0.0", + "package": "OpenSSL", + "patches": [ + "0001-add-bundled-cert.patch" + ], + "repo": "openssl/openssl", + "tag": "openssl-%VERSION%", + "version": "3.6.2" + }, + "openssl-ci": { + "ci": true, + "min_version": "3.0.0", + "name": "openssl", + "package": "OpenSSL", + "repo": "crueter-ci/OpenSSL", + "version": "4.0.0-11b7b6ea3b" + }, + "openssl-cmake": { + "bundled": true, + "hash": "2cc185c924fd70e7d886257ca0caa42b3b8f7f712f2052b4f94dde74759e27022de76178460e18c9bdfc57c366583999e198fbb6052d4e7d91c099d15a0ca63e", "options": [ - "ZSTD_BUILD_SHARED OFF" - ] + "OPENSSL_CONFIGURE_OPTIONS threads" + ], + "patches": [ + "0001-cpmutil-compat.patch", + "0002-use-ccache.patch", + "0003-use-cmake-compiler-flags.patch", + "0004-use-shell-wrapper.patch" + ], + "repo": "jimmy-park/openssl-cmake", + "tag": "%VERSION%", + "version": "3.6.2" }, "opus": { - "package": "Opus", - "repo": "xiph/opus", - "sha": "a3f0ec02b3", - "hash": "9506147b0de35befda8633ff272981cc2575c860874791bd455b752f797fd7dbd1079f0ba42ccdd7bb1fe6773fa5e84b3d75667c2883dd1fb2d0e4a5fa4f8387", - "version": "1.3", "find_args": "MODULE", + "hash": "9506147b0de35befda8633ff272981cc2575c860874791bd455b752f797fd7dbd1079f0ba42ccdd7bb1fe6773fa5e84b3d75667c2883dd1fb2d0e4a5fa4f8387", + "min_version": "1.3", "options": [ "OPUS_PRESUME_NEON ON" ], + "package": "Opus", "patches": [ "0001-disable-clang-runtime-neon.patch", "0002-no-install.patch" - ] - }, - "boost_headers": { - "repo": "boostorg/headers", - "sha": "95930ca8f5", - "hash": "8a07d7a6f0065587d3005a83481a794704ae22e773b9f336fbd89ed230aaa7b4c86c03edcbae30bba8b3e20839c3131eaa2dceac037ef811533ef4eadc53b15b", - "bundled": true - }, - "llvm-mingw": { - "repo": "eden-emu/llvm-mingw", - "git_host": "git.eden-emu.dev", - "tag": "%VERSION%", - "version": "20250828", - "artifact": "clang-rt-builtins.tar.zst", - "hash": "d902392caf94e84f223766e2cc51ca5fab6cae36ab8dc6ef9ef6a683ab1c483bfcfe291ef0bd38ab16a4ecc4078344fa8af72da2f225ab4c378dee23f6186181" - }, - "vulkan-validation-layers": { - "package": "VVL", - "repo": "KhronosGroup/Vulkan-ValidationLayers", - "tag": "vulkan-sdk-%VERSION%", - "git_version": "1.4.341.0", - "artifact": "android-binaries-%VERSION%.zip", - "hash": "8812ae84cbe49e6a3418ade9c458d3be6d74a3dffd319d4502007b564d580998056e8190414368ec11b27bc83993c7a0dad713c31bcc3d9553b51243efee3753" + ], + "repo": "xiph/opus", + "sha": "a3f0ec02b3", + "version": "1.5.2" }, "quazip": { - "package": "QuaZip-Qt6", - "repo": "stachenov/quazip", - "sha": "2e95c9001b", "hash": "609c240c7f029ac26a37d8fbab51bc16284e05e128b78b9b9c0e95d083538c36047a67d682759ac990e4adb0eeb90f04f1ea7fe2253bbda7e7e3bcce32e53dd8", - "version": "1.3", - "git_version": "1.5", + "min_version": "1.3", "options": [ "QUAZIP_QT_MAJOR_VERSION 6", "QUAZIP_INSTALL OFF", - "QUAZIP_ENABLE_QTEXTCODEC OFF" - ] + "QUAZIP_ENABLE_QTEXTCODEC OFF", + "QUAZIP_BZIP2 OFF" + ], + "package": "QuaZip-Qt6", + "repo": "stachenov/quazip", + "sha": "2e95c9001b", + "version": "1.5" + }, + "sdl3": { + "hash": "df5a323af7ac366661a3c0e887969c72584d232f3cc211419d59b0487b620b6b2859d4549c9e8df002ee489290062e466fcfddf7edc0872a37b1f2845e81c0f3", + "min_version": "3.2.10", + "package": "SDL3", + "repo": "libsdl-org/SDL", + "tag": "release-%VERSION%", + "version": "3.4.8" + }, + "sdl3-ci": { + "ci": true, + "min_version": "3.2.10", + "name": "SDL3", + "package": "SDL3", + "repo": "crueter-ci/SDL3", + "version": "3.4.8-d57c3b685c" + }, + "simpleini": { + "find_args": "MODULE", + "hash": "b937c18a7b6277d77ca7ebfb216af4984810f77af4c32d101b7685369a4bd5eb61406223f82698e167e6311a728d07415ab59639fdf19eff71ad6dc2abfda989", + "package": "SimpleIni", + "repo": "brofield/simpleini", + "tag": "v%VERSION%", + "version": "4.25" + }, + "sirit": { + "artifact": "sirit-source-%VERSION%.tar.zst", + "find_args": "CONFIG", + "options": [ + "SIRIT_USE_SYSTEM_SPIRV_HEADERS ON" + ], + "repo": "eden-emulator/sirit", + "tag": "v%VERSION%", + "version": "1.0.5", + "hash": "10b3ff60bdcad428bb4f54360ff749212333a1d24c0b3ed99e466b1bfcf99d2db6cf596c0f965854a2095dfef9b7ce4e045edb070fa9f76eb3b295ab03a4a293" + }, + "sirit-ci": { + "ci": true, + "name": "sirit", + "package": "sirit", + "repo": "eden-emulator/sirit", + "version": "1.0.5" + }, + "spirv-headers": { + "hash": "cae8cd179c9013068876908fecc1d158168310ad6ac250398a41f0f5206ceff6469e2aaeab9c820bce9d1b08950c725c89c46e94b89a692be9805432cf749396", + "options": [ + "SPIRV_WERROR OFF" + ], + "package": "SPIRV-Headers", + "repo": "KhronosGroup/SPIRV-Headers", + "sha": "04f10f650d" + }, + "tzdb": { + "artifact": "%VERSION%.tar.gz", + "git_host": "git.eden-emu.dev", + "hash": "cce65a12bf90f4ead43b24a0b95dfad77ac3d9bfbaaf66c55e6701346e7a1e44ca5d2f23f47ee35ee02271eb1082bf1762af207aad9fb236f1c8476812d008ed", + "package": "nx_tzdb", + "repo": "eden-emu/tzdb_to_nx", + "tag": "%VERSION%", + "version": "230326" + }, + "unordered-dense": { + "bundled": true, + "find_args": "CONFIG", + "hash": "d2106f6640f6bfb81755e4b8bfb64982e46ec4a507cacdb38f940123212ccf35a20b43c70c6f01d7bfb8c246d1a16f7845d8052971949cea9def1475e3fa02c8", + "package": "unordered_dense", + "patches": [ + "0001-avoid-memset-when-clearing-an-empty-table.patch" + ], + "repo": "martinus/unordered_dense", + "sha": "7b55cab841", + "version": "4.8.1" + }, + "vulkan-headers": { + "hash": "d2846ea228415772645eea4b52a9efd33e6a563043dd3de059e798be6391a8f0ca089f455ae420ff22574939ed0f48ed7c6ff3d5a9987d5231dbf3b3f89b484b", + "min_version": "1.4.317", + "package": "VulkanHeaders", + "repo": "KhronosGroup/Vulkan-Headers", + "tag": "v%VERSION%", + "version": "1.4.345" + }, + "vulkan-memory-allocator": { + "find_args": "CONFIG", + "hash": "deb5902ef8db0e329fbd5f3f4385eb0e26bdd9f14f3a2334823fb3fe18f36bc5d235d620d6e5f6fe3551ec3ea7038638899db8778c09f6d5c278f5ff95c3344b", + "package": "VulkanMemoryAllocator", + "repo": "GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator", + "tag": "v%VERSION%", + "version": "3.3.0" + }, + "vulkan-utility-libraries": { + "hash": "114f6b237a6dcba923ccc576befb5dea3f1c9b3a30de7dc741f234a831d1c2d52d8a224afb37dd57dffca67ac0df461eaaab6a5ab5e503b393f91c166680c3e1", + "package": "VulkanUtilityLibraries", + "repo": "KhronosGroup/Vulkan-Utility-Libraries", + "tag": "v%VERSION%", + "version": "1.4.345" + }, + "vulkan-validation-layers": { + "artifact": "android-binaries-%VERSION%.zip", + "hash": "8812ae84cbe49e6a3418ade9c458d3be6d74a3dffd319d4502007b564d580998056e8190414368ec11b27bc83993c7a0dad713c31bcc3d9553b51243efee3753", + "package": "VVL", + "repo": "KhronosGroup/Vulkan-ValidationLayers", + "tag": "vulkan-sdk-%VERSION%", + "version": "1.4.341.0" + }, + "xbyak": { + "hash": "b6475276b2faaeb315734ea8f4f8bd87ededcee768961b39679bee547e7f3e98884d8b7851e176d861dab30a80a76e6ea302f8c111483607dde969b4797ea95a", + "package": "xbyak", + "repo": "herumi/xbyak", + "tag": "v%VERSION%", + "version": "7.35.2" + }, + "zlib": { + "hash": "16fea4df307a68cf0035858abe2fd550250618a97590e202037acd18a666f57afc10f8836cbbd472d54a0e76539d0e558cb26f059d53de52ff90634bbf4f47d4", + "min_version": "1.2", + "options": [ + "ZLIB_BUILD_SHARED OFF", + "ZLIB_INSTALL OFF" + ], + "package": "ZLIB", + "repo": "madler/zlib", + "tag": "v%VERSION%", + "version": "1.3.2" + }, + "zstd": { + "find_args": "MODULE", + "hash": "cc5ad4b119a9c2ea57f0b71eeff01113bb506e0d17000159c5409cb8236d22e38c52d5e9e97e7947a4bf1b2dfc44b6c503ab2d9aedbd59458435c6a2849cb029", + "min_version": "1.5", + "options": [ + "ZSTD_BUILD_SHARED OFF" + ], + "repo": "facebook/zstd", + "sha": "b8d6101fba", + "source_subdir": "build/cmake", + "version": "1.5.7" } } diff --git a/dist/72-eden-input.rules b/dist/72-eden-input.rules new file mode 100644 index 0000000000..b84ed200a3 --- /dev/null +++ b/dist/72-eden-input.rules @@ -0,0 +1,8 @@ +# SPDX-FileCopyrightText: 2023 yuzu Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later + +# Allow systemd-logind to manage user access to hidraw with this file +# On most systems, this file should be installed to /etc/udev/rules.d/72-eden-input.rules +# Consult your distro if this is not the case + +KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ENV{ID_INPUT_JOYSTICK}=="1", MODE="0660", TAG+="uaccess" diff --git a/dist/72-yuzu-input.rules b/dist/72-yuzu-input.rules deleted file mode 100644 index d64f8b28d7..0000000000 --- a/dist/72-yuzu-input.rules +++ /dev/null @@ -1,19 +0,0 @@ -# SPDX-FileCopyrightText: 2023 yuzu Emulator Project -# SPDX-License-Identifier: GPL-2.0-or-later - -# Allow systemd-logind to manage user access to hidraw with this file -# On most systems, this file should be installed to /etc/udev/rules.d/72-yuzu-input.rules -# Consult your distro if this is not the case - -# Switch Pro Controller (USB/Bluetooth) -KERNEL=="hidraw*", ATTRS{idVendor}=="057e", ATTRS{idProduct}=="2009", MODE="0660", TAG+="uaccess" -KERNEL=="hidraw*", KERNELS=="*057e:2009*", MODE="0660", TAG+="uaccess" - -# Joy-Con L (Bluetooth) -KERNEL=="hidraw*", KERNELS=="*057e:2006*", MODE="0660", TAG+="uaccess" - -# Joy-Con R (Bluetooth) -KERNEL=="hidraw*", KERNELS=="*057e:2007*", MODE="0660", TAG+="uaccess" - -# Joy-Con Charging Grip (USB) -KERNEL=="hidraw*", ATTRS{idVendor}=="057e", ATTRS{idProduct}=="200e", MODE="0660", TAG+="uaccess" diff --git a/dist/dev.eden_emu.eden.svg b/dist/dev.eden_emu.eden.svg index df206b43af..f88b52f625 100644 --- a/dist/dev.eden_emu.eden.svg +++ b/dist/dev.eden_emu.eden.svg @@ -6,8 +6,8 @@ viewBox="0 0 512 512" version="1.1" id="svg7" - sodipodi:docname="1stanni.svg" - inkscape:version="1.4.3 (0d15f75042, 2025-12-25)" + sodipodi:docname="base.svg.2026_01_12_14_43_47.0.svg" + inkscape:version="1.4.2 (ebf0e94, 2025-05-08)" inkscape:export-filename="base.svg.2026_01_12_14_43_47.0.svg" inkscape:export-xdpi="96" inkscape:export-ydpi="96" @@ -19,34 +19,36 @@ + id="stop3" /> + style="stop-color:#bf42f6;stop-opacity:0.5;" + offset="0.44631511" + id="stop4" /> + - + id="stop152" /> + style="stop-color:#bf42f6;stop-opacity:1;" + offset="0.44971901" + id="stop137" /> + + + + + + + @@ -136,6 +165,16 @@ inkscape:label="Circle" r="191.89999" /> + @@ -148,106 +187,14 @@ - - - - - - - - - - - - - - - - - + y2="448" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.3229974,0,0,1.3214002,-82.687336,-82.290326)" /> - - Determines how sharpened the image will look using FSR's dynamic contrast. - FSR يقوم بتحديد مدى وضوح الصورة باستخدام التباين الديناميكي لـ + Determines how sharpened the image will look using FSR's or SGSR's dynamic contrast. + يحدد مدى وضوح الصورة باستخدام التباين الديناميكي لـ FSR أو SGSR. @@ -782,36 +782,36 @@ Disabling it is only intended for debugging. لا يُقصد من تعطيله سوى تصحيح الأخطاء. - + Use asynchronous GPU emulation استخدم محاكاة وحدة معالجة الرسومات غير المتزامنة - + Uses an extra CPU thread for rendering. This option should always remain enabled. يستخدم خيط معالجة إضافي للمعالجة الرسومية. يجب أن يبقى هذا الخيار مُفعّلاً دائماً. - + NVDEC emulation: NVDEC محاكاة: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. يُحدد كيفية فك تشفير الفيديوهات.يمكن استخدام وحدة المعالجة المركزية CPU أو وحدة معالجة الرسومات GPU لفك التشفير، أو عدم فك التشفير إطلاقًا (شاشة سوداء على الفيديوهات).في معظم الحالات، يُوفر فك تشفير وحدة معالجة الرسومات GPU أفضل أداء. - + ASTC Decoding Method: ASTC طريقة فك تشفير: - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -823,12 +823,12 @@ stuttering but may present artifacts. وحدة المعالجة المركزية بشكل غير متزامن: استخدمها لفك تشفير قوام ASTC عند الطلب. يزيل هذا الخيار تقطع فك تشفير ASTC، ولكنه قد يُظهر بعض العيوب. - + ASTC Recompression Method: ASTC طريقة إعادة ضغط: - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. @@ -836,44 +836,56 @@ BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, BC1/BC3: سيتم إعادة ضغط الصيغة الوسيطة إلى صيغة BC1 أو BC3، مما يوفر مساحة على ذاكرة الوصول العشوائي للفيديو VRAM ولكنه يُضعف جودة الصورة. - + Frame Pacing Mode (Vulkan only) (Vulkan فقط) وضع توقيت الإطارات - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. يتحكم في كيفية إدارة المحاكي لسرعة الإطارات لتقليل التقطع وجعل معدل الإطارات أكثر سلاسة واتساقًا. - + VRAM Usage Mode: VRAM وضع استهلاك: - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. يُحدد ما إذا كان ينبغي على المُحاكي تفضيل توفير الذاكرة أو الاستفادة القصوى من ذاكرة الفيديو المُتاحة لتحسين الأداء. قد يؤثر الوضع المُكثّف على أداء تطبيقات أخرى، مثل برامج التسجيل. - + Skip CPU Inner Invalidation تخطي إبطال وحدة المعالجة المركزية الداخلية - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. يتخطى بعض عمليات إبطال ذاكرة التخزين المؤقتة أثناء تحديثات الذاكرة، مما يقلل من استخدام وحدة المعالجة المركزية ويحسّن زمن الوصول. قد يؤدي هذا إلى أعطال مؤقتة. - + + Anti-Flicker + مضاد الوميض + + + + Forces GPU fence callbacks to wait for submitted GPU work. +Use with Fast GPU mode, to avoid flicker with lower performance impact. + يُجبر هذا الوضع وظائف وحدة معالجة الرسومات على الانتظار حتى يتم إرسال البيانات إليها. +استخدمه مع وضع وحدة معالجة الرسومات السريع لتجنب الوميض مع تأثير أقل على الأداء. + + + VSync Mode: VSync وضع: - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -884,12 +896,12 @@ Immediate (no synchronization) presents whatever is available and can exhibit te يُظهر الوضع الفوري (بدون مزامنة) كل ما هو متاح، وقد يُظهر تمزقًا. - + Sync Memory Operations مزامنة عمليات الذاكرة - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. @@ -898,99 +910,99 @@ Unreal Engine 4 games often see the most significant changes thereof. التغييرات الأكثر أهمية Unreal Engine 4 غالبًا ما تشهد ألعاب. - + Enable asynchronous presentation (Vulkan only) (Vulkan فقط) تمكين العرض التقديمي غير المتزامن - + Slightly improves performance by moving presentation to a separate CPU thread. تحسين الأداء بشكل طفيف عن طريق نقل العرض التقديمي إلى مؤشر ترابط منفصل في وحدة المعالجة المركزية. - + Force maximum clocks (Vulkan only) (Vulkan فقط) فرض الحد الأقصى للساعات - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. يعمل في الخلفية أثناء انتظار أوامر الرسومات لمنع وحدة معالجة الرسومات من خفض سرعة الساعة. - + Anisotropic Filtering: الترشيح المتباين الخواص: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. يتحكم بجودة عرض الملمس بزوايا مائلة. من الآمن ضبطه على 16x على معظم وحدات معالجة الرسومات. - + GPU Mode: وضع وحدة معالجة الرسومات: - + Controls the GPU emulation mode. Most games render fine with Fast or Balanced modes, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. يتحكم في وضع محاكاة وحدة معالجة الرسومات.تُعرض معظم الألعاب بشكل جيد في الوضعين السريع أو المتوازن، ولكن الوضع الدقيق لا يزال مطلوبًا لبعض الألعاب.تميل الجسيمات إلى العرض بشكل صحيح فقط في الوضع الدقيق. - + DMA Accuracy: DMA دقة: - + Controls the DMA precision accuracy. Safe precision fixes issues in some games but may degrade performance. الدقة الآمنة تعمل على إصلاح المشكلات في بعض الألعاب ولكنها قد تؤدي إلى انخفاض الأداء DMA يتحكم في دقة - + Enable asynchronous shader compilation تفعيل تجميع التظليل غير المتزامن - + May reduce shader stutter. قد يقلل من تقطع التظليل. - + Fast GPU Time وقت معالجة الرسومات السريع - + Overclocks the emulated GPU to increase dynamic resolution and render distance. Use 256 for maximal performance and 512 for maximal graphics fidelity. يزيد من سرعة وحدة معالجة الرسومات المحاكية لزيادة الدقة الديناميكية ومسافة العرض.استخدم 256 للحصول على أقصى أداء و 512 للحصول على أقصى دقة للرسومات. - + GPU Unswizzle إلغاء ترتيب بيانات وحدة معالجة الرسومات - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. باستخدام حوسبة وحدة معالجة الرسومات BCn 3D يسرع فك تشفير نسيج. قم بتعطيله في حالة حدوث أعطال أو مشاكل في الرسومات. - + GPU Unswizzle Max Texture Size الحد الأقصى لحجم النسيج في وحدة معالجة الرسومات بعد إعادة ترتيب البيانات - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. @@ -999,48 +1011,48 @@ Adjust this to find the balance between GPU acceleration and CPU overhead. - + GPU Unswizzle Stream Size حجم تدفق إلغاء ترتيب بيانات وحدة معالجة الرسومات - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. يحدد الحد الأقصى لكمية بيانات النسيج (بالميغابايت) التي تتم معالجتها لكل إطار. يمكن أن تقلل القيم الأعلى من التقطع أثناء تحميل النسيج، ولكنها قد تؤثر على اتساق الإطارات. - + GPU Unswizzle Chunk Size حجم كتلة إلغاء ترتيب بيانات وحدة معالجة الرسومات - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. يُحدد هذا الخيار عدد شرائح العمق التي تتم معالجتها في عملية إرسال واحدة. زيادة هذا الخيار قد تُحسّن الإنتاجية على وحدات معالجة الرسومات المتطورة، ولكنها قد تُسبب أخطاء في إعادة تعيين وقت الاستجابة أو انقطاع الاتصال ببرنامج التشغيل على الأجهزة ذات المواصفات الأقل. - + Use Vulkan pipeline cache Vulkan استخدام ذاكرة التخزين المؤقتة لخط أنابيب - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. يُفعّل ذاكرة التخزين المؤقت لخطوط الأنابيب الخاصة ببائع وحدة معالجة الرسومات. بتخزين ملفات ذاكرة التخزين المؤقتة للخطوط الداخلية Vulkan يمكن أن يؤدي هذا الخيار إلى تحسين وقت تحميل التظليل بشكل كبير في الحالات التي لا يقوم فيها برنامج تشغيل. - + Enable Compute Pipelines (Intel Vulkan Only) (Intel Vulkan فقط) تمكين خطوط أنابيب الحوسبة - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. @@ -1049,182 +1061,192 @@ Compute pipelines are always enabled on all other drivers. خطوط أنابيب الحوسبة مفعلة دائمًا على جميع برامج التشغيل الأخرى. - + Enable Reactive Flushing تمكين التنظيف التفاعلي - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. يستخدم التنظيف التفاعلي بدلاً من التنظيف التنبئي، مما يسمح بمزامنة الذاكرة بشكل أكثر دقة. - + Sync to framerate of video playback المزامنة مع معدل الإطارات لتشغيل الفيديو - + Run the game at normal speed during video playback, even when the framerate is unlocked. قم بتشغيل اللعبة بالسرعة العادية أثناء تشغيل الفيديو، حتى عندما يكون معدل الإطارات مفتوحًا. - + Barrier feedback loops حلقات التغذية الراجعة للحواجز - + Improves rendering of transparency effects in specific games. يحسن عرض تأثيرات الشفافية في بعض الألعاب المحددة. - + Enable buffer history تمكين سجل التخزين المؤقت - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. يُتيح هذا الخيار الوصول إلى حالات التخزين المؤقت السابقة. وقد يُحسّن جودة العرض وثبات الأداء في بعض الألعاب. - + Fix bloom effects إصلاح تأثيرات التوهج - + Removes bloom in Burnout. يزيل التوهج في وضع الاحتراق. - + Enable Legacy Rescale Pass تفعيل ميزة إعادة التحجيم القديمة - + May fix rescale issues in some games by relying on behavior from the previous implementation. Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. قد يصلح مشاكل إعادة القياس في بعض الألعاب بالاعتماد على سلوك من التنفيذ السابق. حل لسلوك قديم يصلح تشوهات الخطوط على بطاقات AMD وIntel الرسومية، ووميض النسيج الرمادي على بطاقات Nvidia الرسومية في لعبة Luigi's Mansion 3. - + Extended Dynamic State الحالة الديناميكية الموسعة - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. يتحكم في عدد الميزات التي يمكن استخدامها في الحالة الديناميكية الموسعة.تسمح الحالات الأعلى بمزيد من الميزات ويمكن أن تزيد من الأداء، ولكنها قد تسبب مشكلات رسومية إضافية. - + Vertex Input Dynamic State حالة ديناميكية لإدخال الرأس - + Enables vertex input dynamic state feature for better quality and performance. يتيح ميزة الحالة الديناميكية لإدخال الرأس لتحسين الجودة والأداء. - + Sample Shading تظليل العينة - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. يسمح لمظلل الأجزاء بالتنفيذ لكل عينة في جزء متعدد العينات بدلاً من مرة واحدة لكل جزء. يحسن جودة الرسومات على حساب الأداء. القيم الأعلى تحسن الجودة ولكنها تقلل من الأداء. - + RNG Seed مولد الأرقام العشوائية - + Controls the seed of the random number generator. Mainly used for speedrunning. يتحكم في بذرة مولد الأرقام العشوائية. يُستخدم بشكل رئيسي في سباقات السرعة. - + Device Name اسم الجهاز - + The name of the console. اسم وحدة التحكم. - + + Homebrew Args + Homebrew وسائط + + + + Command-line arguments passed to homebrew at launch (e.g. -noglsl). + (noglsl -مثل) عند التشغيل Homebrew سطر الأوامر الوسائط المُمررة إلى + + + Custom RTC Date: المخصص RTC تاريخ: - + This option allows to change the clock of the console. Can be used to manipulate time in games. يتيح لك هذا الخيار تغيير ساعة وحدة التحكم. يمكن استخدامه للتحكم بالوقت في الألعاب. - + The number of seconds from the current unix time عدد الثواني من وقت يونكس الحالي - + Language: اللغة: - + This option can be overridden when region setting is auto-select يمكن تجاوز هذا الخيار عند تحديد إعداد المنطقة تلقائيًا - + Region: المنطقة: - + The region of the console. منطقة وحدة التحكم. - + Time Zone: المنطقة الزمنية: - + The time zone of the console. المنطقة الزمنية لوحدة التحكم. - + Sound Output Mode: وضع إخراج الصوت: - + Console Mode: وضع وحدة التحكم: - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. @@ -1233,1031 +1255,1041 @@ Setting to Handheld can help improve performance for low end systems. يُمكن أن يُساعد الضبط على الوضع المحمول على تحسين أداء الأجهزة منخفضة المواصفات. - + Prompt for user profile on boot المطالبة بملف تعريف المستخدم عند التشغيل - + Useful if multiple people use the same PC. مفيد إذا كان هناك عدة أشخاص يستخدمون نفس الكمبيوتر. - + Pause when not in focus توقف عند عدم التركيز - + Pauses emulation when focusing on other windows. يوقف المحاكاة عند التركيز على نوافذ أخرى. - + Confirm before stopping emulation تأكيد قبل إيقاف المحاكاة - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. يتجاوز المطالبات التي تطلب تأكيد إيقاف المحاكاة. يؤدي تمكينه إلى تجاوز هذه المطالبات والخروج مباشرة من المحاكاة. - + Hide mouse on inactivity إخفاء الماوس عند عدم النشاط - + Hides the mouse after 2.5s of inactivity. يخفي الماوس بعد 2.5 ثانية من عدم النشاط. - + Disable controller applet تعطيل تطبيق التحكم - + Forcibly disables the use of the controller applet in emulated programs. When a program attempts to open the controller applet, it is immediately closed. يُعطّل استخدام أداة التحكم في البرامج المُحاكاة قسرًا. عند محاولة أي برنامج فتح أداة التحكم، تُغلق فورًا. - + Check for updates التحقق من توفر التحديثات - + Whether or not to check for updates upon startup. ما إذا كان يجب التحقق من التحديثات عند بدء التشغيل أم لا. - + Enable Gamemode تمكين وضع اللعبة - + Force X11 as Graphics Backend كخلفية رسومات X11 فرض - + Custom frontend الواجهة الأمامية المخصصة - + Real applet تطبيق صغير حقيقي - + Never أبداً - + On Load عند التحميل - + Always دائماً - + CPU وحدة المعالجة المركزية - + GPU وحدة معالجة الرسومات - + CPU Asynchronous وحدة المعالجة المركزية غير المتزامنة - + Uncompressed (Best quality) Uncompressed (أفضل جودة) - + BC1 (Low quality) BC1 (جودة منخفضة) - + BC3 (Medium quality) BC3 (جودة متوسطة) - - + + Auto تلقائي - + 30 FPS 30 إطارًا في الثانية - + 60 FPS 60 إطارًا في الثانية - + 90 FPS 90 إطارًا في الثانية - + 120 FPS 120 إطارًا في الثانية - + Conservative محافظ - + Aggressive عدواني - + Vulkan Vulkan - + OpenGL GLSL OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) OpenGL GLASM (Assembly Shaders, NVIDIA Only) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) OpenGL SPIR-V (Experimental, AMD/Mesa Only) - + Null لا شيء - + Fast سريع - + Balanced متوازن - - + + Accurate دقه - - + + Default افتراضي - + Unsafe (fast) غير آمن (سريع) - + Safe (stable) آمن (مستقر) - + Unsafe غير آمن - + Paranoid (disables most optimizations) جنون العظمة (يعطل معظم التحسينات) - + Debugging تصحيح الأخطاء - + Dynarmic Dynarmic - + NCE NCE - + Borderless Windowed نافذة بلا حدود - + Exclusive Fullscreen شاشة كاملة حصرية - + No Video Output لا يوجد إخراج فيديو - + CPU Video Decoding فك تشفير فيديو وحدة المعالجة المركزية - + GPU Video Decoding (Default) فك تشفير فيديو وحدة معالجة الرسومات (افتراضي) - + 0.25X (180p/270p) [EXPERIMENTAL] 0.25X (180p/270p) [تجريبي] - + 0.5X (360p/540p) [EXPERIMENTAL] 0.5X (360p/540p) [تجريبي] - + 0.75X (540p/810p) [EXPERIMENTAL] 0.75X (540p/810p) [تجريبي] - + 1X (720p/1080p) 1X (720p/1080p) - + 1.25X (900p/1350p) [EXPERIMENTAL] 1.25X (900p/1350p) [تجريبي] - + 1.5X (1080p/1620p) [EXPERIMENTAL] 1.5X (1080p/1620p) [تجريبي] - + 2X (1440p/2160p) 2X (1440p/2160p) - + 3X (2160p/3240p) 3X (2160p/3240p) - + 4X (2880p/4320p) 4X (2880p/4320p) - + 5X (3600p/5400p) 5X (3600p/5400p) - + 6X (4320p/6480p) 6X (4320p/6480p) - + 7X (5040p/7560p) 7X (5040p/7560p) - + 8X (5760p/8640p) 8X (5760p/8640p) - + Nearest Neighbor Nearest Neighbor - + Bilinear Bilinear - + Bicubic Bicubic - + Gaussian Gaussian - + Lanczos Lanczos - + ScaleForce ScaleForce - + AMD FidelityFX Super Resolution AMD FidelityFX Super Resolution - + Area Area - + MMPX MMPX - + Zero-Tangent Zero-Tangent - + B-Spline B-Spline - + Mitchell Mitchell - + Spline-1 Spline-1 - - + + Snapdragon Game Super Resolution + دقة فائقة للألعاب من سنابدراغون + + + + Snapdragon Game Super Resolution EdgeDir + دقة فائقة للألعاب من سنابدراغون EdgeDir + + + + None لا شيء - + FXAA FXAA - + SMAA SMAA - + Default (16:9) (16:9) افتراضي - + Force 4:3 4:3 فرض - + Force 21:9 21:9 فرض - + Force 16:10 16:10 فرض - + Stretch to Window تمديد إلى النافذة - + Automatic تلقائي - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x - + 32x 32x - + 64x 64x - + Japanese (日本語) اليابانية (日本語) - + American English الإنجليزية الأمريكية - + French (français) الفرنسية (français) - + German (Deutsch) الألمانية (Deutsch) - + Italian (italiano) الإيطالية (Italiano) - + Spanish (español) الإسبانية (español) - + Chinese الصينية - + Korean (한국어) الكورية (한국어) - + Dutch (Nederlands) الهولندية (Nederlands) - + Portuguese (português) البرتغالية (Português) - + Russian (Русский) الروسية (Русский) - + Taiwanese تايواني - + British English الإنكليزية البريطانية - + Canadian French الكندية الفرنسية - + Latin American Spanish أمريكا اللاتينية الإسبانية - + Simplified Chinese الصينية المبسطة - + Traditional Chinese (正體中文) الصينية التقليدية (正體中文) - + Brazilian Portuguese (português do Brasil) البرتغالية البرازيلية (português do Brasil) - + Polish (polska) البولندية (polska) - + Thai (แบบไทย) التايلاندية (แบบไทย) - - + + Japan اليابان - + USA الولايات المتحدة الأمريكية - + Europe أوروبا - + Australia أستراليا - + China الصين - + Korea كوريا - + Taiwan تايوان - + Auto (%1) Auto select time zone تلقائي (%1) - + Default (%1) Default time zone افتراضي (%1) - + CET CET - + CST6CDT CST6CDT - + Cuba Cuba - + EET EET - + Egypt Egypt - + Eire Eire - + EST EST - + EST5EDT EST5EDT - + GB GB - + GB-Eire GB-Eire - + GMT GMT - + GMT+0 GMT+0 - + GMT-0 GMT-0 - + GMT0 GMT0 - + Greenwich Greenwich - + Hongkong Hongkong - + HST HST - + Iceland Iceland - + Iran Iran - + Israel Israel - + Jamaica Jamaica - + Kwajalein Kwajalein - + Libya Libya - + MET MET - + MST MST - + MST7MDT MST7MDT - + Navajo Navajo - + NZ NZ - + NZ-CHAT NZ-CHAT - + Poland Poland - + Portugal Portugal - + PRC PRC - + PST8PDT PST8PDT - + ROC ROC - + ROK ROK - + Singapore Singapore - + Turkey Turkey - + UCT UCT - + Universal عالمي - + UTC UTC - + W-SU W-SU - + WET WET - + Zulu Zulu - + Mono أحادي - + Stereo مجسم - + Surround محيطي - + 4GB DRAM (Default) 4GB DRAM (افتراضي) - + 6GB DRAM (Unsafe) 6GB DRAM (غير آمنة) - + 8GB DRAM 8GB DRAM - + 10GB DRAM (Unsafe) 10GB DRAM (غير آمنة) - + 12GB DRAM (Unsafe) 12GB DRAM (غير آمنة) - + Docked الإرساء - + Handheld محمول - - + + Off تعطيل - + Boost (1700MHz) تعزيز (1700 ميجا هرتز) - + Fast (2000MHz) سريع (2000 ميجاهرتز) - + Always ask (Default) اسأل دائمًا (افتراضي) - + Only if game specifies not to stop فقط إذا حددت اللعبة عدم التوقف - + Never ask لا تسأل أبدا - - + + Medium (256) متوسط (256) - - + + High (512) عالي (512) - + Very Small (16 MB) صغير جدًا (16 ميغابايت) - + Small (32 MB) صغير (32 ميغابايت) - + Normal (128 MB) قياسي (128 ميغابايت) - + Large (256 MB) كبير (256 ميغابايت) - + Very Large (512 MB) كبير جدًا (512 ميغابايت) - + Very Low (4 MB) منخفض جدًا (4 ميغابايت) - + Low (8 MB) منخفض (8 ميغابايت) - + Normal (16 MB) قياسي (16 ميغابايت) - + Medium (32 MB) متوسط (32 ميغابايت) - + High (64 MB) عالي (64 ميغابايت) - + Very Low (32) منخفض جدًا (32) - + Low (64) منخفض (64) - + Normal (128) قياسي (128) - + Disabled معطل - + ExtendedDynamicState 1 الحالة الديناميكية الممتدة 1 - + ExtendedDynamicState 2 الحالة الديناميكية الممتدة 2 - + ExtendedDynamicState 3 الحالة الديناميكية الممتدة 3 - + Tree View عرض قائمة - + Grid View عرض شبكة @@ -3312,33 +3344,33 @@ Would you like to delete the old save data? :لون الخلفية - + % - FSR sharpening percentage (e.g. 50%) + FSR/SGSR sharpening percentage (e.g. 50%) % - + Off معطل - + VSync Off VSync معطل - + Recommended مستحسن - + On مفعل - + VSync On VSync مفعل @@ -4467,7 +4499,7 @@ To invert the axes, first move your joystick vertically, and then horizontally.< - + Configure الإعدادات @@ -4498,7 +4530,7 @@ To invert the axes, first move your joystick vertically, and then horizontally.< - + Test إختبار @@ -4513,77 +4545,77 @@ To invert the axes, first move your joystick vertically, and then horizontally.< إزالة الخادم - + %1:%2 %1:%2 - - - - - - + + + + + + Eden إيدن - + Port number has invalid characters يحتوي رقم المنفذ على أحرف غير صالحة - + Port has to be in range 0 and 65353 يجب أن يكون المنفذ في النطاق 0 و 65353 - + IP address is not valid غير صالح IP عنوان - + This UDP server already exists هذا موجود بالفعل UDP خادم - + Unable to add more than 8 servers غير قادر على إضافة أكثر من 8 خوادم - + Testing اختبار - + Configuring الإعدادات - + Test Successful تم الاختبار بنجاح - + Successfully received data from the server. تم استلام البيانات من الخادم بنجاح. - + Test Failed فشل الاختبار - + Could not receive valid data from the server.<br>Please verify that the server is set up correctly and the address and port are correct. تعذر تلقي بيانات صالحة من الخادم.<br> يرجى التحقق من إعداد الخادم بشكل صحيح ومن صحة العنوان والمنفذ. - + UDP Test or calibration configuration is in progress.<br>Please wait for them to finish. أو ضبط المعايرة قيد التقدم. <br>يرجى الانتظار حتى الانتهاء UDP اختبار @@ -4768,57 +4800,57 @@ Current values are %1% and %2% respectively. بعض الإعدادات متوفرة فقط عندما لا تكون اللعبة قيد التشغيل. - + Add-Ons الإضافات - + System النظام - + CPU المعالج - + Graphics الرسومات - + Adv. Graphics الرسومات المتقدمة - + Ext. Graphics الرسومات الخارجية - + Audio الصوت - + Input Profiles ملفات تعريف الإدخال - + Network الشبكة - + Applets التطبيقات الصغيرة - + Properties خصائص @@ -5834,7 +5866,7 @@ Drag points to change position, or double-click table cells to edit values.استيراد البيانات لهذا المجلد. قد يستغرق هذا بعض الوقت، وسيؤدي إلى حذف جميع البيانات الموجودة! - + Calculating... يتم الحساب... @@ -6038,50 +6070,50 @@ Please go to Configure -> System -> Network and make a selection. GRenderWindow - - + + OpenGL not available! OpenGL غير متوفر! - + OpenGL shared contexts are not supported. OpenGL لا يتم دعم السياقات المشتركة - + Eden has not been compiled with OpenGL support. OpenGL لم يتم تجميع إيدن بدعم - - - + + + Error while initializing OpenGL! OpenGL حدث خطأ أثناء تهيئة - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. أو قد لا يكون لديك أحدث برنامج تشغيل للرسومات OpenGL قد لا تدعم بطاقة الرسومات الخاصة بك - + Error while initializing OpenGL 4.6! OpenGL 4.6 حدث خطأ أثناء تهيئة - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 أو قد لا يكون لديك أحدث برنامج تشغيل للرسومات OpenGL 4.6 قد لا تدعم بطاقة الرسومات الخاصة بك.<br><br>GL Renderer:<br>%1 - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 قد لا تدعم وحدة معالجة الرسومات لديك ملحقًا واحدًا أو أكثر من ملحقات OpenGL المطلوبة. يُرجى التأكد من تثبيت أحدث برنامج تشغيل للرسومات.<br><br>GL Renderer:<br>1%<br><br>إضافات غير مدعومة: <br>2% - + This build doesn't have OpenGL support. هذا الإصدار لا يدعم OpenGL. @@ -6089,279 +6121,282 @@ Please go to Configure -> System -> Network and make a selection. GameList - + &Add New Game Directory &إضافة مجلد ألعاب جديد - + Favorite مفضلة - + Start Game بدء اللعبة - + Start Game without Custom Configuration بدء اللعبة بدون الإعدادات المخصصة - + Open Save Data Location فتح موقع بيانات الحفظ - + Open Mod Data Location فتح موقع بيانات التعديلات - + Open Transferable Pipeline Cache ذاكرة التخزين المؤقتة المفتوحة القابلة للتحويل - + Link to Ryujinx Ryujinx ربط بـ - + Remove إزالة - + Remove Installed Update إزالة التحديث المثبت - + Remove All Installed DLC إزالة جميع المحتويات القابلة للتنزيل المثبتة - + Remove Custom Configuration إزالة الإعدادات المخصصة - + Remove Cache Storage إزالة تخزين ذاكرة التخزين المؤقتة - + Remove OpenGL Pipeline Cache OpenGL إزالة ذاكرة التخزين المؤقتة لخط أنابيب - + Remove Vulkan Pipeline Cache Vulkan إزالة ذاكرة التخزين المؤقتة لخط أنابيب - + Remove All Pipeline Caches إزالة جميع ذاكرات التخزين المؤقتة لخط الأنابيب - + Remove All Installed Contents إزالة كافة المحتويات المثبتة - + Manage Play Time إدارة زمن اللعب - + Edit Play Time Data تعديل بيانات زمن التشغيل - + Remove Play Time Data إزالة بيانات زمن اللعب - - + + Dump RomFS RomFS تفريغ - + Dump RomFS to SDMC SDMC إلى RomFS تفريغ - + Verify Integrity التحقق من السلامة - + Copy Title ID to Clipboard نسخ معرف العنوان إلى الحافظة - + Navigate to GameDB entry انتقل إلى إدخال قاعدة بيانات الألعاب - + Create Shortcut إنشاء إختصار - + Add to Desktop إضافة إلى سطح المكتب - + Add to Applications Menu إضافة إلى قائمة التطبيقات - + Configure Game إعدادات اللعبة - + Scan Subfolders مسح الملفات الداخلية - + Remove Game Directory إزالة مجلد اللعبة - + ▲ Move Up ▲ نقل للأعلى - + ▼ Move Down ▼ نقل للأسفل - + Open Directory Location فتح موقع المجلد - + Clear مسح - - - Name - الاسم - - - - Compatibility - التوافق - - - - Add-ons - الإضافات - - - - File type - نوع الملف - - - - Size - الحجم - - - - Play time - زمن اللعب - GameListItemCompat - + Ingame في اللعبة - + Game starts, but crashes or major glitches prevent it from being completed. تبدأ اللعبة، لكن الأعطال أو الأخطاء الرئيسية تمنعها من الاكتمال. - + Perfect مثالي - + Game can be played without issues. يمكن لعب اللعبة بدون مشاكل. - + Playable قابل للعب - + Game functions with minor graphical or audio glitches and is playable from start to finish. تحتوي وظائف اللعبة على بعض الأخطاء الرسومية أو الصوتية البسيطة ويمكن تشغيلها من البداية إلى النهاية. - + Intro/Menu مقدمة/القائمة - + Game loads, but is unable to progress past the Start Screen. يتم تحميل اللعبة، ولكنها غير قادرة على التقدم بعد شاشة البدء. - + Won't Boot لا تشتغل - + The game crashes when attempting to startup. تعطل اللعبة عند محاولة بدء التشغيل. - + Not Tested لم تختبر - + The game has not yet been tested. لم يتم اختبار اللعبة بعد. + + GameListModel + + + Name + الاسم + + + + Compatibility + التوافق + + + + Add-ons + الإضافات + + + + File type + نوع الملف + + + + Size + الحجم + + + + Play time + زمن اللعب + + GameListPlaceholder - + Double-click to add a new folder to the game list انقر نقرًا مزدوجًا لإضافة مجلد جديد إلى قائمة الألعاب @@ -6369,17 +6404,17 @@ Please go to Configure -> System -> Network and make a selection. GameListSearchField - + %1 of %n result(s) %1 من %n نتيجة (نتائج)%1 من %n نتيجة (نتائج)%1 من %n نتيجة (نتائج)%1 من %n نتيجة (نتائج)%1 من %n نتيجة (نتائج)%1 من %n نتيجة (نتائج) - + Filter: :تصفية - + Enter pattern to filter أدخل النمط المطلوب لتصفية النتائج @@ -6875,772 +6910,777 @@ Debug Message: &وضع قائمة الألعاب - + Game &Icon Size حجم &أيقونة اللعبة - + Reset Window Size to &720p 720p إعادة تعيين حجم النافذة إلى - + Reset Window Size to 720p 720p إعادة تعيين حجم النافذة إلى - + Reset Window Size to &900p 900p إعادة تعيين حجم النافذة إلى - + Reset Window Size to 900p 900p إعادة تعيين حجم النافذة إلى - + Reset Window Size to &1080p 1080p إعادة تعيين حجم النافذة إلى - + Reset Window Size to 1080p 1080p إعادة تعيين حجم النافذة إلى - + &Multiplayer &متعدد اللاعبين - + &Tools &أدوات - + Am&iibo أم&يبو - + Launch &Applet تشغيل &التطبيق المصغر - + &TAS &TAS - + &Create Home Menu Shortcut &إنشاء اختصار لقائمة الشاشة الرئيسية - + Install &Firmware تثبيت &الفيرموير - + &Help &مساعدة - + &Install Files to NAND... &تثبيت الملفات في الذاكرة الداخلية - + L&oad File... ت&حميل ملف - + Load &Folder... تحميل &مجلد - + E&xit خ&روج - - + + &Pause &إيقاف مؤقت - + &Stop &إيقاف - + &Verify Installed Contents &التحقق من المحتويات المثبتة - + &About Eden &حول إيدن - + Single &Window Mode وضع &النافذة الواحدة - + Con&figure... الإع&دادات - + Ctrl+, Ctrl+, - + Enable Overlay Display Applet تمكين التطبيق المصغر لعرض الطبقة - + Show &Filter Bar عرض &شريط التصفية - + Show &Status Bar عرض &شريط الحالة - + Show Status Bar عرض شريط الحالة - + &Browse Public Game Lobby &تصفح قائمة الألعاب العامة - + &Create Room &إنشاء غرفة - + &Leave Room &مغادرة الغرفة - + &Direct Connect to Room &الاتصال المباشر بالغرفة - + &Show Current Room &عرض الغرفة الحالية - + F&ullscreen م&لء الشاشة - + &Restart &إعادة التشغيل - + Load/Remove &Amiibo... تحميل/إزالة &أميبو - + &Report Compatibility &تقرير التوافق - + Open &Mods Page صفحة &التعديلات - + Open &Quickstart Guide دليل &البدء السريع - + &FAQ &الأسئلة الشائعة - + &Capture Screenshot &التقاط لقطة للشاشة - + &Album &الألبوم - + &Set Nickname and Owner &تعيين الاسم المستعار والمالك - + &Delete Game Data حذف بيانات اللعبة - + &Restore Amiibo &استعادة أميبو - + &Format Amiibo &تنسيق أميبو - + &Mii Editor &Mii محرر - + &Configure TAS... &TAS إعدادات - + Configure C&urrent Game... إعدادات اللعبة ال&حالية - - + + &Start &بدء - + &Reset &إعادة تعيين - - + + R&ecord ت&سجيل - + Open &Controller Menu &قائمة ذراع التحكم - + Install Decryption &Keys تثبيت &مفاتيح فك التشفير - + &Home Menu &القائمة الرئيسية - + &Desktop &سطح المكتب - + &Application Menu &قائمة التطبيقات - + &Root Data Folder &مجلد البيانات الرئيسي - + &NAND Folder &مجلد الذاكرة الداخلية - + &SDMC Folder &مجلد بطاقة الذاكرة - + &Mod Folder &مجلد التعديلات - + &Log Folder &مجلد السجلات - + From Folder من مجلد - + From ZIP من ملف مضغوط - + &Eden Dependencies &تبعيات إيدن - + &Data Manager &إدارة البيانات - + &Tree View &عرض قائمة - + &Grid View &عرض شبكة - + Game Icon Size حجم أيقونة اللعبة - - + + None لا شيء - + Show Game &Name عرض اسم &اللعبة - + Show &Performance Overlay عرض &طبقة الأداء - + + &Carousel View + + + + Small (32x32) صغير (32x32) - + Standard (64x64) قياسي (64x64) - + Large (128x128) كبير (128x128) - + Full Size (256x256) حجم كامل (256x256) - + Broken Vulkan Installation Detected Vulkan تم الكشف عن تلف في تثبيت - + Vulkan initialization failed during boot. أثناء التشغيل Vulkan فشل تهيئة. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping تشغيل لعبة - + Loading Web Applet... جارٍ تحميل تطبيق الويب... - - + + Disable Web Applet تعطيل تطبيق الويب - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) قد يؤدي تعطيل أداة الويب إلى سلوك غير محدد، ويجب استخدامه فقط مع لعبة Super Mario 3D All-Stars. هل أنت متأكد من رغبتك في تعطيل أداة الويب؟ (يمكن إعادة تفعيلها في إعدادات التصحيح.) - + The amount of shaders currently being built كمية التظليلات التي يتم بناؤها حاليًا - + The current selected resolution scaling multiplier. مضاعف قياس الدقة المحددة حالياً. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. Switch سرعة المحاكاة الحالية. تشير القيم الأعلى أو الأقل من 100٪ إلى أن المحاكاة تعمل بشكل أسرع أو أبطأ من. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. عدد الإطارات في الثانية التي تعرضها اللعبة حاليًا. يختلف هذا العدد من لعبة إلى أخرى ومن مشهد إلى آخر. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. الوقت المستغرق لمحاكاة إطار على جهاز سويتش، بدون احتساب تحديد الإطارات أو المزامنة العمودية. لمحاكاة بسرعة كاملة، يجب أن يكون هذا في حدود 16.67 مللي ثانية كحد أقصى. - + Unmute إلغاء كتم الصوت - + Mute كتم الصوت - + Reset Volume إعادة تعيين مستوى الصوت - + &Clear Recent Files &مسح الملفات الحديثة - + &Continue &متابعة - + Warning: Outdated Game Format تحذير: تنسيق اللعبة قديم - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. أنت تستخدم تنسيق مجلد ROM المُفكك لهذه اللعبة، وهو تنسيق قديم استُبدل بآخر مثل NCA وNAX وXCI وNSP. تفتقر مجلدات ROM المُفككة إلى الأيقونات والبيانات الوصفية ودعم التحديثات. <br>لتوضيح تنسيقات Switch المختلفة التي يدعمها Eden، يُرجى مراجعة دليل المستخدم. لن تظهر هذه الرسالة مرة أخرى. - - + + Error while loading ROM! ROM خطأ أثناء تحميل - + The ROM format is not supported. غير مدعوم ROM تنسيق. - + An error occurred initializing the video core. حدث خطأ أثناء تهيئة نواة الفيديو. - + Eden has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-mirror.github.io/help/reference/log-files/'>How to Upload the Log File</a>. واجه إيدن خطأً أثناء تشغيل نواة الفيديو. عادةً ما يكون السبب هو برامج تشغيل وحدة معالجة الرسومات القديمة، بما في ذلك المدمجة منها. يُرجى مراجعة السجل لمزيد من التفاصيل. لمزيد من المعلومات حول الوصول إلى السجل، يُرجى زيارة الصفحة التالية: <a href='https://yuzu-mirror.github.io/help/reference/log-files/'>كيفيه رفع سجلات الإخطاء</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. حدث خطأ أثناء تحميل ROM! %1 - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. %1<br>Discord/Stoat يرجى إعادة تحميل ملفاتك أو طلب المساعدة على - + An unknown error occurred. Please see the log for more details. حدث خطأ غير معروف. يرجى الاطلاع على السجل لمزيد من التفاصيل. - + (64-bit) (64-bit) - + (32-bit) (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Closing software... جارٍ إغلاق البرنامج... - + Save Data بيانات الحفظ - + Mod Data بيانات التعديل - + Error Opening %1 Folder خطأ في فتح المجلد %1 - - + + Folder does not exist! المجلد غير موجود! - + Remove Installed Game Contents? إزالة محتويات اللعبة المثبتة؟ - + Remove Installed Game Update? إزالة تحديث اللعبة المثبت؟ - + Remove Installed Game DLC? إزالة المحتوى القابل للتنزيل المثبت للعبة؟ - + Remove Entry إزالة الإدخال - + Delete OpenGL Transferable Shader Cache? OpenGL Shader حذف ذاكرة التخزين المؤقتة القابلة للنقل لـ - + Delete Vulkan Transferable Shader Cache? Vulkan Shader حذف ذاكرة التخزين المؤقتة القابلة للنقل لـ - + Delete All Transferable Shader Caches? حذف جميع مخازن ذاكرة التظليل القابلة للنقل؟ - + Remove Custom Game Configuration? إزالة إعدادات اللعبة المخصص؟ - + Remove Cache Storage? إزالة ذاكرة التخزين المؤقتة؟ - + Remove File إزالة الملف - + Remove Play Time Data إزالة بيانات زمن اللعب - + Reset play time? إعادة تعيين زمن اللعب؟ - - + + RomFS Extraction Failed! RomFS فشل استخراج - + There was an error copying the RomFS files or the user cancelled the operation. أو قام المستخدم بإلغاء العملية RomFS حدث خطأ أثناء نسخ ملفات - + Full كامل - + Skeleton Skeleton - + Select RomFS Dump Mode RomFS حدد وضع تفريغ - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. الرجاء تحديد الطريقة التي تريد بها تفريغ RomFS. <br>سيتم نسخ جميع الملفات إلى المجلد الجديد في أثناء <br>قيام الهيكل العظمي بإنشاء بنية المجلد فقط. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root لا توجد مساحة فارغة كافية في %1 لاستخراج نظام الملفات RomFS. يُرجى تحرير مساحة أو اختيار مجلد تفريغ آخر من: المحاكاة > التكوين > النظام > نظام الملفات > تفريغ الجذر. - + Extracting RomFS... RomFS استخراج... - - + + Cancel إلغاء - + RomFS Extraction Succeeded! بنجاح RomFS تم استخراج! - + The operation completed successfully. تمت العملية بنجاح. - + Error Opening %1 خطأ في فتح %1 - + Select Directory حدد المجلد - + Properties خصائص - + The game properties could not be loaded. تعذر تحميل خصائص اللعبة. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. تشغيل الملف القابل للتنفيذ (%1)؛؛جميع الملفات (*.*) - + Load File تحميل الملف - + Open Extracted ROM Directory المستخرج ROM فتح ملف - + Invalid Directory Selected تم تحديد مجلد غير صالح - + The directory you have selected does not contain a 'main' file. لا يحتوي المجلد الذي حددته على ملف رئيسي - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files تثبيت الملفات - + %n file(s) remaining %n ملف (ملفات) متبقية%n ملف (ملفات) متبقية%n ملف (ملفات) متبقية%n ملف (ملفات) متبقية%n ملف (ملفات) متبقية%n ملف (ملفات) متبقية - + Installing file "%1"... تثبيت الملف ”%1“... - - + + Install Results نتائج التثبيت - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. لتجنب أي تعارضات محتملة، ننصح المستخدمين بعدم تثبيت الألعاب الأساسية على الذاكرة الداخلية. يرجى استخدام هذه الميزة فقط لتثبيت التحديثات والمحتوى القابل للتنزيل. - + %n file(s) were newly installed %n تم تثبيت ملف (ملفات) جديدة @@ -7652,7 +7692,7 @@ Please, only use this feature to install updates and DLC. - + %n file(s) were overwritten %n تم استبدال ملف (ملفات) @@ -7664,7 +7704,7 @@ Please, only use this feature to install updates and DLC. - + %n file(s) failed to install %n فشل تثبيت ملف (ملفات) @@ -7676,361 +7716,314 @@ Please, only use this feature to install updates and DLC. - + System Application تطبيق النظام - + System Archive أرشيف النظام - + System Application Update تحديث تطبيق النظام - + Firmware Package (Type A) حزمة الفيرموير (النوع أ) - + Firmware Package (Type B) حزمة الفيرموير (النوع ب) - + Game لعبة - + Game Update تحديث اللعبة - + Game DLC الخاص باللعبة DLC الـ - + Delta Title Delta عنوان - + Select NCA Install Type... NCA حدد نوع تثبيت... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) يرجى تحديد نوع اللعبة التي ترغب في تثبيت NCA عليها: (في معظم الحالات، يكون الإعداد الافتراضي "لعبة" مناسبًا.) - + Failed to Install فشل التثبيت - + The title type you selected for the NCA is invalid. غير صالح NCA نوع العنوان الذي حددته لـ. - + File not found لم يتم العثور على الملف - + File "%1" not found لم يتم العثور على الملف "%1" - + OK موافق - + Function Disabled الوظيفة معطلة - + Compatibility list reporting is currently disabled. Check back later! تقارير قائمة التوافق معطلة حاليًا. يرجى التحقق لاحقًا! - + Error opening URL خطأ في فتح الرابط - + Unable to open the URL "%1". تعذر فتح الرابط ”%1“ - + TAS Recording TAS تسجيل - + Overwrite file of player 1? الكتابة فوق ملف اللاعب 1؟ - + Invalid config detected تم اكتشاف إعدادات غير صالح - + Handheld controller can't be used on docked mode. Pro controller will be selected. لا يمكن استخدام وحدة التحكم المحمولة في وضع الإرساء. سيتم اختيار وحدة تحكم احترافية. - - + + Amiibo أميبو - - + + The current amiibo has been removed تمت إزالة أميبو الحالي. - + Error خطأ - - + + The current game is not looking for amiibos اللعبة الحالية لا تبحث عن أميبو - + Amiibo File (%1);; All Files (*.*) جميع الملفات (%1)؛؛ ملف أميبو (*.*) - + Load Amiibo تحميل أميبو - + Error loading Amiibo data خطأ أثناء تحميل بيانات أميبو - + The selected file is not a valid amiibo الملف المحدد ليس أميبو صالح - + The selected file is already on use الملف المحدد قيد الاستخدام بالفعل - + An unknown error occurred حدث خطأ غير معروف - - - Keys not installed - المفاتيح غير مثبتة - - - - - Install decryption keys and restart Eden before attempting to install firmware. - قم بتثبيت مفاتيح فك التشفير وأعد تشغيل إيدن قبل محاولة تثبيت الفيرموير. - - - - Select Dumped Firmware Source Location - حدد موقع مصدر الفيرموير المفرغة - - - - Select Dumped Firmware ZIP - حدد ملف الفيرموير المضغوط الذي تم تفريغه - - - - Zipped Archives (*.zip) - Zipped Archives (*.zip) - - - - Firmware cleanup failed - فشل مسح الفيرموير - - - - Failed to clean up extracted firmware cache. -Check write permissions in the system temp directory and try again. -OS reported error: %1 - فشل تنظيف ذاكرة التخزين المؤقتة للفيرموير المستخرج. -تحقق من أذونات الكتابة في دليل الملفات المؤقتة للنظام وحاول مرة أخرى. -أبلغ نظام التشغيل عن خطأ: %1 - - - + No firmware available لا يوجد فيرموير متوفر - + Firmware Corrupted الفيرموير تالف - + Unknown applet تطبيق غير معروف - + Applet doesn't map to a known value. لا يرتبط التطبيق المصغر بقيمة معروفة. - + Record not found لم يتم العثور على السجل - + Applet not found. Please reinstall firmware. لم يتم العثور على التطبيق المصغر. يرجى إعادة تثبيت الفيرموير. - + Capture Screenshot التقاط لقطة شاشة - + PNG Image (*.png) PNG Image (*.png) - + TAS state: Running %1/%2 حالة TAS: تشغيل %1/%2 - + TAS state: Recording %1 حالة TAS: تسجيل %1 - + TAS state: Idle %1/%2 حالة TAS: خامل %1/%2 - + TAS State: Invalid حالة TAS: غير صالحة - + &Stop Running &إيقاف التشغيل - + Stop R&ecording إيقاف ال&تسجيل - + Building: %n shader(s) بناء: %n تظليل(ات)بناء: %n تظليل(ات)بناء: %n تظليل(ات)بناء: %n تظليل(ات)بناء: %n تظليل(ات)بناء: %n تظليل(ات) - + Scale: %1x %1 is the resolution scaling factor الدقة: %1x - + Speed: %1% / %2% السرعة: %1% / %2% - + Speed: %1% السرعة: %1% - + Game: %1 FPS اللعبة: %1 FPS - + Frame: %1 ms الإطار: %1 ms - - - FSR - FSR - - - + NO AA NO AA - + VOLUME: MUTE الصوت: كتم الصوت - + VOLUME: %1% Volume percentage (e.g. 50%) %1% :الصوت - + Derivation Components Missing مكونات الاشتقاق مفقودة - + Decryption keys are missing. Install them now? مفاتيح فك التشفير مفقودة. هل تريد تثبيتها الآن؟ - + Wayland Detected! Wayland تم الكشف عن - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -8041,74 +8034,74 @@ Would you like to force it for future launches? هل ترغب في فرض استخدامه في عمليات التشغيل المستقبلية؟ - + Use X11 X11 استخدم - + Continue with Wayland Wayland متابعة مع - + Don't show again لا تعرض مرة أخرى - + Restart Required إعادة التشغيل مطلوبة - + Restart Eden to apply the X11 backend. X11 أعد تشغيل إيدن لتطبيق الخلفية - + Slow بطيء - + Turbo تيربو - + Unlocked إلغاء القفل - + Select RomFS Dump Target RomFS حدد هدف تفريغ - + Please select which RomFS you would like to dump. الذي تريد تفريغه RomFS الرجاء تحديد - + Are you sure you want to close Eden? هل أنت متأكد من أنك تريد إغلاق إيدن؟ - - - + + + Eden إيدن - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. هل أنت متأكد من أنك تريد إيقاف المحاكاة؟ سيتم فقدان أي تقدم لم يتم حفظه. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? @@ -8176,6 +8169,11 @@ Would you like to bypass this and exit anyway? ScaleForce ScaleForce + + + FSR + FSR + Area @@ -8187,52 +8185,62 @@ Would you like to bypass this and exit anyway? MMPX - + + SGSR + SGSR + + + + SGSR EdgeDir + SGSR EdgeDir + + + Docked الإرساء - + Handheld محمول - + Fast سريع - + Balanced متوازن - + Accurate دقه - + Vulkan Vulkan - + OpenGL GLSL OpenGL GLSL - + OpenGL SPIRV OpenGL SPIRV - + OpenGL GLASM OpenGL GLASM - + Null لا شيء @@ -8275,7 +8283,7 @@ If you wish to clean up the files which were left in the old data location, you %1 - + Data was migrated successfully. تم نقل البيانات بنجاح. @@ -9133,47 +9141,47 @@ p, li { white-space: pre-wrap; } %1 يلعب %2 - + Play Time: %1 زمن اللعب: %1 - + Never Played لم تُلعب قط - + Version: %1 الإصدار: %1 - + Version: 1.0.0 الإصدار: 1.0.0 - + Installed SD Titles عناوين المثبتة على بطاقة الذاكرة - + Installed NAND Titles عناوين المثبتة على الذاكرة الداخلية - + System Titles عناوين النظام - + Add New Game Directory إضافة مجلد ألعاب جديد - + Favorites المفضلة @@ -9294,47 +9302,47 @@ p, li { white-space: pre-wrap; } QtCommon::Content - + Game Requires Firmware تتطلب اللعبة الفيرموير - + The game you are trying to launch requires firmware to boot or to get past the opening menu. Please <a href='https://yuzu-mirror.github.io/help/quickstart'>dump and install firmware</a>, or press "OK" to launch anyways. اللعبة التي تحاول تشغيلها تتطلب فيرموير للتشغيل أو لتجاوز قائمة الفتح. يُرجى <a href='https://yuzu-mirror.github.io/help/quickstart'>تفريغ فيرموير وتثبيتها</a>، أو اضغط "حسنًا" للعب على أي حال - + Installing Firmware... تثبيت الفيرموير... - - - - - + + + + + Cancel إلغاء - + Firmware Install Failed فشل تثبيت الفيرموير - + Firmware Install Succeeded تم تثبيت الفيرموير بنجاح - + Firmware integrity verification failed! فشل التحقق من سلامة الفيرموير! - - + + Verification failed for the following files: %1 @@ -9343,207 +9351,242 @@ p, li { white-space: pre-wrap; } %1 - - + + Verifying integrity... جارٍ التحقق من السلامة... - - + + Integrity verification succeeded! تم التحقق من السلامة بنجاح! - - + + The operation completed successfully. اكتملت العملية بنجاح. - - + + Integrity verification failed! فشل التحقق من السلامة! - + File contents may be corrupt or missing. قد تكون محتويات الملف تالفة أو مفقودة. - + Integrity verification couldn't be performed تعذر إجراء التحقق من السلامة - + Firmware installation cancelled, firmware may be in a bad state or corrupted. File contents could not be checked for validity. تم إلغاء تثبيت الفيرموير، قد يكون الفيرموير في حالة سيئة أو تآلف. تعذر التحقق من صحة محتويات الملف. - + Select Dumped Keys Location حدد موقع المفاتيح المفرغة - + Decryption Keys install succeeded تم تثبيت مفاتيح فك التشفير بنجاح - + Decryption Keys install failed فشل تثبيت مفاتيح فك التشفير - + Orphaned Profiles Detected! تم الكشف عن ملفات تعريف مهملة! - + UNEXPECTED BAD THINGS MAY HAPPEN IF YOU DON'T READ THIS!<br>Eden has detected the following save directories with no attached profile:<br>%1<br><br>The following profiles are valid:<br>%2<br><br>Click "OK" to open your save folder and fix up your profiles.<br>Hint: copy the contents of the largest or last-modified folder elsewhere, delete all orphaned profiles, and move your copied contents to the good profile.<br><br>Still confused? See the <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/Orphaned.md'>help page</a>.<br> قد تحدث أمور سيئة غير متوقعة إذا لم تقرأ هذا<br>!لقد اكتشف إيدن مجلدات الحفظ التالية بدون ملف تعريف مرفق:<br>%1<br><br>ملفات التعريف التالية صالحة:<br>%2<br><br>انقر على ”موافق“ لفتح مجلد الحفظ وإصلاح ملفات التعريف الخاصة بك.<br>تلميح: انسخ محتويات المجلد الأكبر أو آخر مجلد تم تعديله إلى مكان آخر، واحذف جميع ملفات التعريف اليتيمة، وانقل المحتويات المنسوخة إلى ملف التعريف الصحيح.<br><br>هل ما زلت تشعر بالارتباك؟ انظر إلى <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/Orphaned.md'>صفحة المساعدة</a>.<br> - + Really clear data? مسح البيانات بالفعل؟ - + Important data may be lost! قد يتم فقدان بيانات مهمة! - + Are you REALLY sure? هل أنت متأكد حقًا؟ - + Once deleted, your data will NOT come back! Only do this if you're 100% sure you want to delete this data. بمجرد حذفها، لن تتمكن من استعادة بياناتك! قم بذلك فقط إذا كنت متأكدًا بنسبة 100٪ أنك تريد حذف هذه البيانات. - + Clearing... إزالة... - + Select Export Location حدد موقع التصدير - + %1.zip %1.zip - - + + + Zipped Archives (*.zip) Zipped Archives (*.zip) - + Exporting data. This may take a while... تصدير البيانات. قد يستغرق هذا بعض الوقت... - + Exporting التصدير - + Exported Successfully تم التصدير بنجاح - + Data was exported successfully. تم تصدير البيانات بنجاح. - + Export Cancelled تم إلغاء التصدير - + Export was cancelled by the user. تم إلغاء التصدير من قبل المستخدم. - + Export Failed فشل التصدير - + Ensure you have write permissions on the targeted directory and try again. تأكد من أن لديك أذونات الكتابة على المجلد المحدد وحاول مرة أخرى. - + Select Import Location حدد موقع الاستيراد - + Import Warning تحذير الاستيراد - + All previous data in this directory will be deleted. Are you sure you wish to proceed? سيتم حذف جميع البيانات السابقة في هذا المجلد. هل أنت متأكد من رغبتك في المتابعة؟ - + Importing data. This may take a while... استيراد البيانات. قد يستغرق هذا بعض الوقت... - + Importing استيراد - + Imported Successfully تم الاستيراد بنجاح - + Data was imported successfully. تم استيراد البيانات بنجاح. - + Import Cancelled تم إلغاء الاستيراد - + Import was cancelled by the user. تم إلغاء الاستيراد من قبل المستخدم. - + Import Failed فشل الاستيراد - + Ensure you have read permissions on the targeted directory and try again. تأكد من أن لديك أذونات قراءة على المجلد المحدد وحاول مرة أخرى. + + + Keys not installed + المفاتيح غير مثبتة + + + + Install decryption keys and restart Eden before attempting to install firmware. + قم بتثبيت مفاتيح فك التشفير وأعد تشغيل إيدن قبل محاولة تثبيت الفيرموير. + + + + Select Dumped Firmware Source Location + حدد موقع مصدر الفيرموير المفرغة + + + + Select Dumped Firmware ZIP + حدد ملف الفيرموير المضغوط الذي تم تفريغه + + + + Firmware cleanup failed + فشل مسح الفيرموير + + + + Failed to clean up extracted firmware cache. +Check write permissions in the system temp directory and try again. +OS reported error: %1 + فشل مسح ذاكرة التخزين المؤقتة للفيرموير المستخرج. +تحقق من أذونات الكتابة في دليل النظام المؤقت وحاول مرة أخرى. +أبلغ نظام التشغيل عن خطأ: %1 + QtCommon::FS @@ -9790,72 +9833,72 @@ Would you like to manually select a portable folder to use? تعذر حذف ذاكرة التخزين المؤقتة للبيانات الوصفية. قد تكون قيد الاستخدام أو غير موجودة. - + Create Shortcut إنشاء اختصار - + Do you want to launch the game in fullscreen? هل تريد تشغيل اللعبة في وضع ملء الشاشة؟ - + Shortcut Created تم إنشاء الاختصار - + Successfully created a shortcut to %1 تم إنشاء اختصار بنجاح إلى %1 - + Shortcut may be Volatile! قد يكون الاختصار متقلبًا! - + This will create a shortcut to the current AppImage. This may not work well if you update. Continue? سيؤدي هذا إلى إنشاء اختصار لصورة التطبيق الحالية. قد لا يعمل هذا بشكل جيد إذا قمت بالتحديث. هل تريد المتابعة؟ - + Failed to Create Shortcut فشل في إنشاء اختصار - + Failed to create a shortcut to %1 فشل إنشاء اختصار إلى %1 - + Create Icon إنشاء أيقونة - + Cannot create icon file. Path "%1" does not exist and cannot be created. لا يمكن إنشاء ملف الرمز. المسار ”%1“ غير موجود ولا يمكن إنشاؤه. - + No firmware available لا يوجد فيرموير متوفر - + Please install firmware to use the home menu. يرجى تثبيت الفيرموير لاستخدام القائمة الرئيسية. - + Home Menu Applet القائمة الرئيسية - + Home Menu is not available. Please reinstall firmware. القائمة الرئيسية غير متوفرة. يرجى إعادة تثبيت الفيرموير. @@ -9863,37 +9906,37 @@ Would you like to manually select a portable folder to use? QtCommon::Mod - + Mod Name اسم التعديل - + What should this mod be called? ما الاسم المناسب لهذا التعديل؟ - + RomFS RomFS - + ExeFS/Patch ExeFS/التصحيح - + Cheat الغش - + Mod Type نوع التعديل - + Could not detect mod type automatically. Please manually specify the type of mod you downloaded. Most mods are RomFS mods, but patches (.pchtxt) are typically ExeFS mods. @@ -9902,18 +9945,18 @@ Most mods are RomFS mods, but patches (.pchtxt) are typically ExeFS mods. - - + + Mod Extract Failed فشل استخراج التعديل - + Failed to create temporary directory %1 %1 فشل إنشاء مجلد المؤقت - + Zip file %1 is empty الملف المضغوط 1% فارغ @@ -10615,66 +10658,66 @@ By selecting "From Eden", previous save data stored in Ryujinx will be %1 متاح للتنزيل - + New Version Location موقع الإصدار الجديد - + All Files (*.*) جميع الملفات (*.*) - - - + + + Failed to save file فشل حفظ الملف - + Could not open file %1 for writing. تعذر فتح الملف 1% للكتابة - + Downloading... جارٍ التنزيل... - + Cancel إلغاء - + Could not write to file %1. تعذر الكتابة إلى الملف %1 - + Could not commit to file %1. تعذر حفظ التغييرات في الملف %1 - + Failed to download file فشل تنزيل الملف - + Could not download from %1%2 Error code: %3 تعذر التنزيل من %1%2 رمز الخطأ: %3 - + Download Complete اكتمل التنزيل - + Successfully downloaded %1. Would you like to open it? تم تنزيل 1% بنجاح. هل تريد فتحه؟ diff --git a/dist/languages/ca.ts b/dist/languages/ca.ts index 10db8a5e3a..2e15435d35 100644 --- a/dist/languages/ca.ts +++ b/dist/languages/ca.ts @@ -702,7 +702,7 @@ Options lower than 1X can cause artifacts. - Determines how sharpened the image will look using FSR's dynamic contrast. + Determines how sharpened the image will look using FSR's or SGSR's dynamic contrast. @@ -753,35 +753,35 @@ Disabling it is only intended for debugging. - + Use asynchronous GPU emulation - + Uses an extra CPU thread for rendering. This option should always remain enabled. - + NVDEC emulation: Emulació NVDEC: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. - + ASTC Decoding Method: - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -790,55 +790,66 @@ stuttering but may present artifacts. - + ASTC Recompression Method: - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. - + Frame Pacing Mode (Vulkan only) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. - + VRAM Usage Mode: - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. - + Skip CPU Inner Invalidation - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. - + + Anti-Flicker + + + + + Forces GPU fence callbacks to wait for submitted GPU work. +Use with Fast GPU mode, to avoid flicker with lower performance impact. + + + + VSync Mode: Mode de sincronització vertical: - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -846,1361 +857,1381 @@ Immediate (no synchronization) presents whatever is available and can exhibit te - + Sync Memory Operations - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. - + Enable asynchronous presentation (Vulkan only) - + Slightly improves performance by moving presentation to a separate CPU thread. - + Force maximum clocks (Vulkan only) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. - + Anisotropic Filtering: Filtrat anisotròpic: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. - + GPU Mode: Mode de la GPU: - + Controls the GPU emulation mode. Most games render fine with Fast or Balanced modes, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. - + DMA Accuracy: Precisió DMA: - + Controls the DMA precision accuracy. Safe precision fixes issues in some games but may degrade performance. - + Enable asynchronous shader compilation - + May reduce shader stutter. - + Fast GPU Time - + Overclocks the emulated GPU to increase dynamic resolution and render distance. Use 256 for maximal performance and 512 for maximal graphics fidelity. - + GPU Unswizzle - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. - + GPU Unswizzle Max Texture Size - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. - + GPU Unswizzle Stream Size - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. - + GPU Unswizzle Chunk Size - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. - + Use Vulkan pipeline cache - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. - + Enable Compute Pipelines (Intel Vulkan Only) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. - + Enable Reactive Flushing - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. - + Sync to framerate of video playback - + Run the game at normal speed during video playback, even when the framerate is unlocked. - + Barrier feedback loops - + Improves rendering of transparency effects in specific games. - + Enable buffer history - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. - + Fix bloom effects - + Removes bloom in Burnout. - + Enable Legacy Rescale Pass - + May fix rescale issues in some games by relying on behavior from the previous implementation. Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. - + Extended Dynamic State - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. - + Vertex Input Dynamic State - + Enables vertex input dynamic state feature for better quality and performance. - + Sample Shading - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. - + RNG Seed Llavor de GNA - + Controls the seed of the random number generator. Mainly used for speedrunning. - + Device Name Nom del dispositiu - + The name of the console. - + + Homebrew Args + + + + + Command-line arguments passed to homebrew at launch (e.g. -noglsl). + + + + Custom RTC Date: - + This option allows to change the clock of the console. Can be used to manipulate time in games. - + The number of seconds from the current unix time - + Language: Idioma: - + This option can be overridden when region setting is auto-select - + Region: Regió: - + The region of the console. - + Time Zone: Zona horària: - + The time zone of the console. - + Sound Output Mode: - + Console Mode: Mode de la consola - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. - + Prompt for user profile on boot - + Useful if multiple people use the same PC. - + Pause when not in focus - + Pauses emulation when focusing on other windows. - + Confirm before stopping emulation - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. - + Hide mouse on inactivity Ocultar el cursor del ratolí en cas d'inactivitat - + Hides the mouse after 2.5s of inactivity. - + Disable controller applet - + Forcibly disables the use of the controller applet in emulated programs. When a program attempts to open the controller applet, it is immediately closed. - + Check for updates - + Whether or not to check for updates upon startup. - + Enable Gamemode Activa el mode joc - + Force X11 as Graphics Backend - + Custom frontend - + Real applet - + Never Mai - + On Load - + Always Sempre - + CPU CPU - + GPU GPU - + CPU Asynchronous - + Uncompressed (Best quality) - + BC1 (Low quality) - + BC3 (Medium quality) - - + + Auto Auto - + 30 FPS 30 FPS - + 60 FPS 60 FPS - + 90 FPS 90 FPS - + 120 FPS 120 FPS - + Conservative Conservador - + Aggressive Agressiu - + Vulkan Vulkan - + OpenGL GLSL OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) OpenGL GLASM (Ensamblat d'ombrejadors, només NVIDIA) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) OpenGL SPIR-V (Experimental, només AMD/Mesa) - + Null Nul - + Fast Ràpid - + Balanced Equilibrat - - + + Accurate Precís - - + + Default Valor predeterminat - + Unsafe (fast) Insegur (ràpid) - + Safe (stable) Segur (estable) - + Unsafe Insegur - + Paranoid (disables most optimizations) Paranoic (desactiva la majoria d'optimitzacions) - + Debugging Depuració - + Dynarmic Dynarmic - + NCE NCE - + Borderless Windowed Finestra sense vores - + Exclusive Fullscreen Pantalla completa exclusiva - + No Video Output Sense sortida de vídeo - + CPU Video Decoding Descodificació de vídeo a la CPU - + GPU Video Decoding (Default) Descodificació de vídeo a la GPU (Valor predeterminat) - + 0.25X (180p/270p) [EXPERIMENTAL] 0.25X (180p/270p) [EXPERIMENTAL] - + 0.5X (360p/540p) [EXPERIMENTAL] 0.5X (360p/540p) [EXPERIMENTAL] - + 0.75X (540p/810p) [EXPERIMENTAL] 0.75X (540p/810p) [EXPERIMENTAL] - + 1X (720p/1080p) 1X (720p/1080p) - + 1.25X (900p/1350p) [EXPERIMENTAL] 1.25X (900p/1350p) [EXPERIMENTAL] - + 1.5X (1080p/1620p) [EXPERIMENTAL] 1.5X (1080p/1620p) [EXPERIMENTAL] - + 2X (1440p/2160p) 2X (1440p/2160p) - + 3X (2160p/3240p) 3X (2160p/3240p) - + 4X (2880p/4320p) 4X (2880p/4320p) - + 5X (3600p/5400p) 5X (3600p/5400p) - + 6X (4320p/6480p) 6X (4320p/6480p) - + 7X (5040p/7560p) 7X (5040p/7560p) - + 8X (5760p/8640p) 8X (5760p/8640p) - + Nearest Neighbor Veí més proper - + Bilinear Bilineal - + Bicubic Bicúbic - + Gaussian Gaussià - + Lanczos Lanczos - + ScaleForce ScaleForce - + AMD FidelityFX Super Resolution Super resolució AMD FidelityFX - + Area Àrea - + MMPX MMPX - + Zero-Tangent Tangència zero - + B-Spline B-Spline - + Mitchell Mitchell - + Spline-1 Spline-1 - - + + Snapdragon Game Super Resolution + + + + + Snapdragon Game Super Resolution EdgeDir + + + + + None Cap - + FXAA FXAA - + SMAA SMAA - + Default (16:9) Valor predeterminat (16:9) - + Force 4:3 Forçar 4:3 - + Force 21:9 Forçar 21:9 - + Force 16:10 - + Stretch to Window Estirar a la finestra - + Automatic Automàtic - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x - + 32x 32x - + 64x 64x - + Japanese (日本語) Japonès (日本語) - + American English Anglès americà - + French (français) Francès (français) - + German (Deutsch) Alemany (Deutsch) - + Italian (italiano) Italià (italiano) - + Spanish (español) Castellà (español) - + Chinese Xinès - + Korean (한국어) Coreà (한국어) - + Dutch (Nederlands) Holandès (Nederlands) - + Portuguese (português) Portuguès (português) - + Russian (Русский) Rus (Русский) - + Taiwanese Taiwanès - + British English Anglès britànic - + Canadian French Francès canadenc - + Latin American Spanish Espanyol llatinoamericà - + Simplified Chinese Xinès simplificat - + Traditional Chinese (正體中文) Xinès tradicional (正體中文) - + Brazilian Portuguese (português do Brasil) Portuguès brasiler (português do Brasil) - + Polish (polska) Polonès (polska) - + Thai (แบบไทย) Tailandès (แบบไทย) - - + + Japan Japó - + USA EUA - + Europe Europa - + Australia Austràlia - + China Xina - + Korea Corea - + Taiwan Taiwan - + Auto (%1) Auto select time zone Auto (%1) - + Default (%1) Default time zone Per defecte (%1) - + CET CET - + CST6CDT CST6CDT - + Cuba Cuba - + EET EET - + Egypt Egipte - + Eire Eire - + EST EST - + EST5EDT EST5EDT - + GB GB - + GB-Eire GB-Eire - + GMT GMT - + GMT+0 GMT+0 - + GMT-0 GMT-0 - + GMT0 GMT0 - + Greenwich Greenwich - + Hongkong Hong Kong - + HST HST - + Iceland Islàndia - + Iran Iran - + Israel Isreal - + Jamaica Jamaica - + Kwajalein Kwajalein - + Libya Líbia - + MET MET - + MST MST - + MST7MDT MST7MDT - + Navajo Navajo - + NZ NZ - + NZ-CHAT NZ-CHAT - + Poland Polònia - + Portugal Portugal - + PRC PRC - + PST8PDT PST8PDT - + ROC ROC - + ROK ROK - + Singapore Singapur - + Turkey Turquia - + UCT UCT - + Universal Universal - + UTC UTC - + W-SU W-SU - + WET WET - + Zulu Zulu - + Mono Mono - + Stereo Estèreo - + Surround Envoltant - + 4GB DRAM (Default) 4GB DRAM (Insegur) - + 6GB DRAM (Unsafe) 6GB DRAM (Insegur) - + 8GB DRAM 8GB DRAM - + 10GB DRAM (Unsafe) 10GB DRAM (Insegur) - + 12GB DRAM (Unsafe) 12GB DRAM (Insegur) - + Docked Sobretaula - + Handheld Portàtil - - + + Off - + Boost (1700MHz) - + Fast (2000MHz) - + Always ask (Default) - + Only if game specifies not to stop Tan sols si el joc especifica no parar - + Never ask No preguntar mai - - + + Medium (256) - - + + High (512) - + Very Small (16 MB) - + Small (32 MB) - + Normal (128 MB) - + Large (256 MB) - + Very Large (512 MB) - + Very Low (4 MB) - + Low (8 MB) - + Normal (16 MB) - + Medium (32 MB) - + High (64 MB) - + Very Low (32) - + Low (64) - + Normal (128) - + Disabled - + ExtendedDynamicState 1 - + ExtendedDynamicState 2 - + ExtendedDynamicState 3 - + Tree View - + Grid View @@ -3254,33 +3285,33 @@ Would you like to delete the old save data? Color de fons: - + % - FSR sharpening percentage (e.g. 50%) - % + FSR/SGSR sharpening percentage (e.g. 50%) + - + Off Apagat - + VSync Off Vsync Apagat - + Recommended Recomanat - + On Encés - + VSync On VSync Encés @@ -4409,7 +4440,7 @@ Per invertir els eixos, primer moveu el joystick verticalment i després horitzo - + Configure Configuració @@ -4440,7 +4471,7 @@ Per invertir els eixos, primer moveu el joystick verticalment i després horitzo - + Test Provar @@ -4455,77 +4486,77 @@ Per invertir els eixos, primer moveu el joystick verticalment i després horitzo Eliminar servidor - + %1:%2 %1:%2 - - - - - - + + + + + + Eden Eden - + Port number has invalid characters El número de port té caràcters invàlids - + Port has to be in range 0 and 65353 El port ha d'estar entre el rang 0 i 65353 - + IP address is not valid l'Adreça IP no és vàlida - + This UDP server already exists Aquest servidor UDP ja existeix - + Unable to add more than 8 servers No és possible afegir més de 8 servidors - + Testing Provant - + Configuring Configurant - + Test Successful Prova exitosa - + Successfully received data from the server. S'han rebut dades des del servidor correctament. - + Test Failed Prova fallida - + Could not receive valid data from the server.<br>Please verify that the server is set up correctly and the address and port are correct. No s'han pogut rebre dades vàlides des del servidor.<br>Si us plau, verifiqui que el servidor està configurat correctament i que la direcció i el port són correctes.  - + UDP Test or calibration configuration is in progress.<br>Please wait for them to finish. La prova del UDP o la configuració de la calibració està en curs.<br>Si us plau, esperi a que acabi el procés. @@ -4709,57 +4740,57 @@ Current values are %1% and %2% respectively. Algunes configuracions són disponibles només quan el joc no està corrent. - + Add-Ons Complements - + System Sistema - + CPU CPU - + Graphics Gràfics - + Adv. Graphics Gràfics avanç. - + Ext. Graphics - + Audio Àudio - + Input Profiles Perfils d'entrada - + Network - + Applets Applets - + Properties Propietats @@ -5770,7 +5801,7 @@ Arrossegui els punts per a canviar la posició, o faci doble clic a les cel·les - + Calculating... @@ -5972,50 +6003,50 @@ Please go to Configure -> System -> Network and make a selection. GRenderWindow - - + + OpenGL not available! OpenGL no disponible! - + OpenGL shared contexts are not supported. - + Eden has not been compiled with OpenGL support. - - - + + + Error while initializing OpenGL! Error al inicialitzar OpenGL! - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. La seva GPU no suporta OpenGL, o no té instal·lat els últims controladors gràfics. - + Error while initializing OpenGL 4.6! Error inicialitzant OpenGL 4.6! - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 La seva GPU no suporta OpenGL 4.6, o no té instal·lats els últims controladors gràfics.<br><br>GL Renderer:<br>%1 - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 És possible que la seva GPU no suporti una o més extensions necessàries d'OpenGL. Si us plau, asseguris de tenir els últims controladors de la tarjeta gràfica.<br><br>GL Renderer:<br>%1<br><br>Extensions no suportades:<br>%2 - + This build doesn't have OpenGL support. Aquesta compilació no té suport per a OpenGL. @@ -6023,279 +6054,282 @@ Please go to Configure -> System -> Network and make a selection. GameList - + &Add New Game Directory - + Favorite Preferit - + Start Game Iniciar el joc - + Start Game without Custom Configuration Iniciar el joc sense la configuració personalitzada - + Open Save Data Location Obrir la ubicació dels arxius de partides guardades - + Open Mod Data Location Obrir la ubicació dels mods - + Open Transferable Pipeline Cache Obrir cache transferible de shaders de canonada - + Link to Ryujinx - + Remove Eliminar - + Remove Installed Update Eliminar actualització instal·lada - + Remove All Installed DLC Eliminar tots els DLC instal·lats - + Remove Custom Configuration Eliminar configuració personalitzada - + Remove Cache Storage - + Remove OpenGL Pipeline Cache Eliminar cache de canonada d'OpenGL - + Remove Vulkan Pipeline Cache Eliminar cache de canonada de Vulkan - + Remove All Pipeline Caches Eliminar totes les caches de canonada - + Remove All Installed Contents Eliminar tots els continguts instal·lats - + Manage Play Time - + Edit Play Time Data - + Remove Play Time Data - - + + Dump RomFS Bolcar RomFS - + Dump RomFS to SDMC Bolcar RomFS a SDMC - + Verify Integrity - + Copy Title ID to Clipboard Copiar la ID del títol al porta-retalls - + Navigate to GameDB entry Navegar a l'entrada de GameDB - + Create Shortcut - + Add to Desktop - + Add to Applications Menu - + Configure Game - + Scan Subfolders Escanejar subdirectoris - + Remove Game Directory Eliminar directori de jocs - + ▲ Move Up ▲ Moure amunt - + ▼ Move Down ▼ Move avall - + Open Directory Location Obre ubicació del directori - + Clear Esborrar - - - Name - Nom - - - - Compatibility - Compatibilitat - - - - Add-ons - Complements - - - - File type - Tipus d'arxiu - - - - Size - Mida - - - - Play time - - GameListItemCompat - + Ingame - + Game starts, but crashes or major glitches prevent it from being completed. - + Perfect Perfecte - + Game can be played without issues. - + Playable - + Game functions with minor graphical or audio glitches and is playable from start to finish. - + Intro/Menu Intro / Menú - + Game loads, but is unable to progress past the Start Screen. - + Won't Boot No engega - + The game crashes when attempting to startup. El joc es bloqueja al intentar iniciar. - + Not Tested No provat - + The game has not yet been tested. Aquest joc encara no ha estat provat. + + GameListModel + + + Name + + + + + Compatibility + + + + + Add-ons + + + + + File type + + + + + Size + + + + + Play time + + + GameListPlaceholder - + Double-click to add a new folder to the game list Faci doble clic per afegir un nou directori a la llista de jocs @@ -6303,17 +6337,17 @@ Please go to Configure -> System -> Network and make a selection. GameListSearchField - + %1 of %n result(s) %1 de %n resultat(s)%1 de %n resultat(s) - + Filter: Filtre: - + Enter pattern to filter Introdueixi patró per a filtrar @@ -6808,1139 +6842,1099 @@ Debug Message: - + Game &Icon Size - + Reset Window Size to &720p Reiniciar el tamany de la finestra a &720p - + Reset Window Size to 720p Reiniciar el tamany de la finestra a 720p - + Reset Window Size to &900p Reiniciar el tamany de la finestra a &900p - + Reset Window Size to 900p Reiniciar el tamany de la finestra a 900p - + Reset Window Size to &1080p Reiniciar el tamany de la finestra a &1080p - + Reset Window Size to 1080p Reiniciar el tamany de la finestra a 1080p - + &Multiplayer - + &Tools &Eines - + Am&iibo - + Launch &Applet - + &TAS &TAS - + &Create Home Menu Shortcut - + Install &Firmware - + &Help &Ajuda - + &Install Files to NAND... &instal·lar arxius a la NAND... - + L&oad File... C&arregar arxiu... - + Load &Folder... Carregar &carpeta... - + E&xit S&ortir - - + + &Pause &Pausar - + &Stop &Aturar - + &Verify Installed Contents - + &About Eden - + Single &Window Mode Mode una sola &finestra - + Con&figure... Con&figurar... - + Ctrl+, - + Enable Overlay Display Applet - + Show &Filter Bar Mostrar la barra de &filtre - + Show &Status Bar Mostrar la barra d'&estat - + Show Status Bar Mostrar barra d'estat - + &Browse Public Game Lobby - + &Create Room - + &Leave Room - + &Direct Connect to Room - + &Show Current Room - + F&ullscreen P&antalla completa - + &Restart &Reiniciar - + Load/Remove &Amiibo... Carregar/Eliminar &Amiibo... - + &Report Compatibility &Informar de compatibilitat - + Open &Mods Page Obrir la pàgina de &mods - + Open &Quickstart Guide Obre la guia d'&inici ràpid - + &FAQ &Preguntes freqüents - + &Capture Screenshot &Captura de pantalla - + &Album - + &Set Nickname and Owner - + &Delete Game Data - + &Restore Amiibo - + &Format Amiibo - + &Mii Editor - + &Configure TAS... &Configurar TAS... - + Configure C&urrent Game... Configurar joc a&ctual... - - + + &Start &Iniciar - + &Reset &Reiniciar - - + + R&ecord E&nregistrar - + Open &Controller Menu - + Install Decryption &Keys - + &Home Menu - + &Desktop - + &Application Menu - + &Root Data Folder - + &NAND Folder - + &SDMC Folder - + &Mod Folder - + &Log Folder - + From Folder - + From ZIP - + &Eden Dependencies - + &Data Manager - + &Tree View - + &Grid View - + Game Icon Size - - + + None Cap - + Show Game &Name - + Show &Performance Overlay - + + &Carousel View + + + + Small (32x32) Petit (32x32) - + Standard (64x64) - + Large (128x128) - + Full Size (256x256) - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping - + Loading Web Applet... - - + + Disable Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. - + Unmute - + Mute - + Reset Volume - + &Clear Recent Files - + &Continue - + Warning: Outdated Game Format - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. - - + + Error while loading ROM! - + The ROM format is not supported. - + An error occurred initializing the video core. - + Eden has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-mirror.github.io/help/reference/log-files/'>How to Upload the Log File</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. - + (64-bit) (64-bit) - + (32-bit) (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit - + Closing software... - + Save Data - + Mod Data - + Error Opening %1 Folder - - + + Folder does not exist! - + Remove Installed Game Contents? - + Remove Installed Game Update? - + Remove Installed Game DLC? - + Remove Entry - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? - + Remove Cache Storage? - + Remove File - + Remove Play Time Data - + Reset play time? - - + + RomFS Extraction Failed! - + There was an error copying the RomFS files or the user cancelled the operation. - + Full - + Skeleton - + Select RomFS Dump Mode - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... - - + + Cancel Cancel·lar - + RomFS Extraction Succeeded! - + The operation completed successfully. - + Error Opening %1 - + Select Directory - + Properties - + The game properties could not be loaded. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. - + Load File - + Open Extracted ROM Directory - + Invalid Directory Selected - + The directory you have selected does not contain a 'main' file. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files - + %n file(s) remaining - + Installing file "%1"... - - + + Install Results - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application - + System Archive - + System Application Update - + Firmware Package (Type A) - + Firmware Package (Type B) - + Game - + Game Update - + Game DLC - + Delta Title - + Select NCA Install Type... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) - + Failed to Install - + The title type you selected for the NCA is invalid. - + File not found - + File "%1" not found - + OK - + Function Disabled - + Compatibility list reporting is currently disabled. Check back later! - + Error opening URL - + Unable to open the URL "%1". - + TAS Recording - + Overwrite file of player 1? - + Invalid config detected - + Handheld controller can't be used on docked mode. Pro controller will be selected. - - + + Amiibo - - + + The current amiibo has been removed - + Error - - + + The current game is not looking for amiibos - + Amiibo File (%1);; All Files (*.*) - + Load Amiibo - + Error loading Amiibo data - + The selected file is not a valid amiibo - + The selected file is already on use - + An unknown error occurred - - - Keys not installed - - - - - - Install decryption keys and restart Eden before attempting to install firmware. - - - - - Select Dumped Firmware Source Location - - - - - Select Dumped Firmware ZIP - - - - - Zipped Archives (*.zip) - - - - - Firmware cleanup failed - - - - - Failed to clean up extracted firmware cache. -Check write permissions in the system temp directory and try again. -OS reported error: %1 - - - - + No firmware available - + Firmware Corrupted - + Unknown applet - + Applet doesn't map to a known value. - + Record not found - + Applet not found. Please reinstall firmware. - + Capture Screenshot - + PNG Image (*.png) - + TAS state: Running %1/%2 - + TAS state: Recording %1 - + TAS state: Idle %1/%2 - + TAS State: Invalid - + &Stop Running - + Stop R&ecording - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor Escala: %1x - + Speed: %1% / %2% Velocitat: %1% / %2% - + Speed: %1% Velocitat: %1% - + Game: %1 FPS Joc: %1 FPS - + Frame: %1 ms Fotograma: %1 ms - - - FSR - FSR - - - + NO AA SENSE AA - + VOLUME: MUTE - + VOLUME: %1% Volume percentage (e.g. 50%) VOLUMEN: %1% - + Derivation Components Missing - + Decryption keys are missing. Install them now? - + Wayland Detected! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -7948,74 +7942,74 @@ Would you like to force it for future launches? - + Use X11 - + Continue with Wayland - + Don't show again - + Restart Required - + Restart Eden to apply the X11 backend. - + Slow Lent - + Turbo Turbo - + Unlocked - + Select RomFS Dump Target - + Please select which RomFS you would like to dump. - + Are you sure you want to close Eden? - - - + + + Eden Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? @@ -8081,6 +8075,11 @@ Would you like to bypass this and exit anyway? ScaleForce + + + FSR + FSR + Area @@ -8092,52 +8091,62 @@ Would you like to bypass this and exit anyway? - + + SGSR + + + + + SGSR EdgeDir + + + + Docked Sobretaula - + Handheld Portàtil - + Fast - + Balanced - + Accurate - + Vulkan Vulkan - + OpenGL GLSL OpenGL GLSL - + OpenGL SPIRV OpenGL SPIRV - + OpenGL GLASM OpenGL GLASM - + Null Nul @@ -8170,7 +8179,7 @@ If you wish to clean up the files which were left in the old data location, you - + Data was migrated successfully. @@ -9025,47 +9034,47 @@ p, li { white-space: pre-wrap; } - + Play Time: %1 - + Never Played Mai jugat - + Version: %1 Versió: %1 - + Version: 1.0.0 Versió: 1.0.0 - + Installed SD Titles Títols instal·lats a la SD - + Installed NAND Titles Títols instal·lats a la NAND - + System Titles Títols del sistema - + Add New Game Directory Afegir un nou directori de jocs - + Favorites Preferits @@ -9186,253 +9195,286 @@ p, li { white-space: pre-wrap; } QtCommon::Content - + Game Requires Firmware - + The game you are trying to launch requires firmware to boot or to get past the opening menu. Please <a href='https://yuzu-mirror.github.io/help/quickstart'>dump and install firmware</a>, or press "OK" to launch anyways. - + Installing Firmware... - - - - - + + + + + Cancel Cancel·lar - + Firmware Install Failed - + Firmware Install Succeeded - + Firmware integrity verification failed! - - + + Verification failed for the following files: %1 - - + + Verifying integrity... + + + + Integrity verification succeeded! + + - Integrity verification succeeded! - - - - - The operation completed successfully. - - + + Integrity verification failed! - + File contents may be corrupt or missing. - + Integrity verification couldn't be performed - + Firmware installation cancelled, firmware may be in a bad state or corrupted. File contents could not be checked for validity. - + Select Dumped Keys Location - + Decryption Keys install succeeded - + Decryption Keys install failed - + Orphaned Profiles Detected! - + UNEXPECTED BAD THINGS MAY HAPPEN IF YOU DON'T READ THIS!<br>Eden has detected the following save directories with no attached profile:<br>%1<br><br>The following profiles are valid:<br>%2<br><br>Click "OK" to open your save folder and fix up your profiles.<br>Hint: copy the contents of the largest or last-modified folder elsewhere, delete all orphaned profiles, and move your copied contents to the good profile.<br><br>Still confused? See the <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/Orphaned.md'>help page</a>.<br> - + Really clear data? - + Important data may be lost! - + Are you REALLY sure? - + Once deleted, your data will NOT come back! Only do this if you're 100% sure you want to delete this data. - + Clearing... - + Select Export Location - + %1.zip %1.zip - - + + + Zipped Archives (*.zip) - + Exporting data. This may take a while... - + Exporting Exportant - + Exported Successfully - + Data was exported successfully. - + Export Cancelled - + Export was cancelled by the user. - + Export Failed - + Ensure you have write permissions on the targeted directory and try again. - + Select Import Location - + Import Warning - + All previous data in this directory will be deleted. Are you sure you wish to proceed? - + Importing data. This may take a while... - + Importing - + Imported Successfully - + Data was imported successfully. - + Import Cancelled - + Import was cancelled by the user. - + Import Failed - + Ensure you have read permissions on the targeted directory and try again. + + + Keys not installed + + + + + Install decryption keys and restart Eden before attempting to install firmware. + + + + + Select Dumped Firmware Source Location + + + + + Select Dumped Firmware ZIP + + + + + Firmware cleanup failed + + + + + Failed to clean up extracted firmware cache. +Check write permissions in the system temp directory and try again. +OS reported error: %1 + + QtCommon::FS @@ -9676,72 +9718,72 @@ Would you like to manually select a portable folder to use? - + Create Shortcut - + Do you want to launch the game in fullscreen? - + Shortcut Created - + Successfully created a shortcut to %1 - + Shortcut may be Volatile! - + This will create a shortcut to the current AppImage. This may not work well if you update. Continue? - + Failed to Create Shortcut - + Failed to create a shortcut to %1 - + Create Icon - + Cannot create icon file. Path "%1" does not exist and cannot be created. - + No firmware available - + Please install firmware to use the home menu. - + Home Menu Applet Applet del menú d'inici - + Home Menu is not available. Please reinstall firmware. @@ -9749,55 +9791,55 @@ Would you like to manually select a portable folder to use? QtCommon::Mod - + Mod Name - + What should this mod be called? - + RomFS - + ExeFS/Patch - + Cheat - + Mod Type - + Could not detect mod type automatically. Please manually specify the type of mod you downloaded. Most mods are RomFS mods, but patches (.pchtxt) are typically ExeFS mods. - - + + Mod Extract Failed - + Failed to create temporary directory %1 - + Zip file %1 is empty @@ -10491,65 +10533,65 @@ By selecting "From Eden", previous save data stored in Ryujinx will be - + New Version Location - + All Files (*.*) - - - + + + Failed to save file - + Could not open file %1 for writing. - + Downloading... - + Cancel - + Could not write to file %1. - + Could not commit to file %1. - + Failed to download file - + Could not download from %1%2 Error code: %3 - + Download Complete - + Successfully downloaded %1. Would you like to open it? diff --git a/dist/languages/cs.ts b/dist/languages/cs.ts index b7469b5d5c..cec3f83b94 100644 --- a/dist/languages/cs.ts +++ b/dist/languages/cs.ts @@ -702,7 +702,7 @@ Options lower than 1X can cause artifacts. - Determines how sharpened the image will look using FSR's dynamic contrast. + Determines how sharpened the image will look using FSR's or SGSR's dynamic contrast. @@ -753,35 +753,35 @@ Disabling it is only intended for debugging. - + Use asynchronous GPU emulation - + Uses an extra CPU thread for rendering. This option should always remain enabled. - + NVDEC emulation: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. - + ASTC Decoding Method: - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -790,55 +790,66 @@ stuttering but may present artifacts. - + ASTC Recompression Method: - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. - + Frame Pacing Mode (Vulkan only) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. - + VRAM Usage Mode: - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. - + Skip CPU Inner Invalidation - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. - + + Anti-Flicker + + + + + Forces GPU fence callbacks to wait for submitted GPU work. +Use with Fast GPU mode, to avoid flicker with lower performance impact. + + + + VSync Mode: - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -846,1361 +857,1381 @@ Immediate (no synchronization) presents whatever is available and can exhibit te - + Sync Memory Operations - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. - + Enable asynchronous presentation (Vulkan only) - + Slightly improves performance by moving presentation to a separate CPU thread. - + Force maximum clocks (Vulkan only) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. - + Anisotropic Filtering: Anizotropní filtrování: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. - + GPU Mode: - + Controls the GPU emulation mode. Most games render fine with Fast or Balanced modes, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. - + DMA Accuracy: - + Controls the DMA precision accuracy. Safe precision fixes issues in some games but may degrade performance. - + Enable asynchronous shader compilation - + May reduce shader stutter. - + Fast GPU Time - + Overclocks the emulated GPU to increase dynamic resolution and render distance. Use 256 for maximal performance and 512 for maximal graphics fidelity. - + GPU Unswizzle - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. - + GPU Unswizzle Max Texture Size - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. - + GPU Unswizzle Stream Size - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. - + GPU Unswizzle Chunk Size - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. - + Use Vulkan pipeline cache - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. - + Enable Compute Pipelines (Intel Vulkan Only) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. - + Enable Reactive Flushing - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. - + Sync to framerate of video playback - + Run the game at normal speed during video playback, even when the framerate is unlocked. - + Barrier feedback loops - + Improves rendering of transparency effects in specific games. - + Enable buffer history - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. - + Fix bloom effects - + Removes bloom in Burnout. - + Enable Legacy Rescale Pass - + May fix rescale issues in some games by relying on behavior from the previous implementation. Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. - + Extended Dynamic State - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. - + Vertex Input Dynamic State - + Enables vertex input dynamic state feature for better quality and performance. - + Sample Shading - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. - + RNG Seed RNG Seed - + Controls the seed of the random number generator. Mainly used for speedrunning. - + Device Name Název Zařízení - + The name of the console. - + + Homebrew Args + + + + + Command-line arguments passed to homebrew at launch (e.g. -noglsl). + + + + Custom RTC Date: - + This option allows to change the clock of the console. Can be used to manipulate time in games. - + The number of seconds from the current unix time - + Language: Jazyk: - + This option can be overridden when region setting is auto-select - + Region: Region: - + The region of the console. - + Time Zone: Časové Pásmo: - + The time zone of the console. - + Sound Output Mode: - + Console Mode: - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. - + Prompt for user profile on boot - + Useful if multiple people use the same PC. - + Pause when not in focus - + Pauses emulation when focusing on other windows. - + Confirm before stopping emulation Potvrzení před zastavením emulace - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. - + Hide mouse on inactivity Skrýt myš při neaktivitě - + Hides the mouse after 2.5s of inactivity. - + Disable controller applet - + Forcibly disables the use of the controller applet in emulated programs. When a program attempts to open the controller applet, it is immediately closed. - + Check for updates - + Whether or not to check for updates upon startup. - + Enable Gamemode - + Force X11 as Graphics Backend - + Custom frontend - + Real applet - + Never - + On Load - + Always - + CPU CPU - + GPU - + CPU Asynchronous - + Uncompressed (Best quality) - + BC1 (Low quality) - + BC3 (Medium quality) - - + + Auto Automatické - + 30 FPS - + 60 FPS - + 90 FPS - + 120 FPS - + Conservative - + Aggressive - + Vulkan Vulkan - + OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) - + Null - + Fast - + Balanced - - + + Accurate Přesné - - + + Default Výchozí - + Unsafe (fast) - + Safe (stable) - + Unsafe Nebezpečné - + Paranoid (disables most optimizations) Paranoidní (zakáže většinu optimizací) - + Debugging - + Dynarmic - + NCE - + Borderless Windowed Okno bez okrajů - + Exclusive Fullscreen Exkluzivní - + No Video Output - + CPU Video Decoding - + GPU Video Decoding (Default) - + 0.25X (180p/270p) [EXPERIMENTAL] - + 0.5X (360p/540p) [EXPERIMENTAL] - + 0.75X (540p/810p) [EXPERIMENTAL] - + 1X (720p/1080p) 1X (720p/1080p) - + 1.25X (900p/1350p) [EXPERIMENTAL] - + 1.5X (1080p/1620p) [EXPERIMENTAL] - + 2X (1440p/2160p) - + 3X (2160p/3240p) - + 4X (2880p/4320p) - + 5X (3600p/5400p) - + 6X (4320p/6480p) - + 7X (5040p/7560p) - + 8X (5760p/8640p) - + Nearest Neighbor - + Bilinear Bilineární - + Bicubic - + Gaussian - + Lanczos - + ScaleForce - + AMD FidelityFX Super Resolution - + Area - + MMPX - + Zero-Tangent - + B-Spline - + Mitchell - + Spline-1 - - + + Snapdragon Game Super Resolution + + + + + Snapdragon Game Super Resolution EdgeDir + + + + + None Žádné - + FXAA FXAA - + SMAA SMAA - + Default (16:9) Výchozí (16:9) - + Force 4:3 Vynutit 4:3 - + Force 21:9 Vynutit 21:9 - + Force 16:10 - + Stretch to Window Roztáhnout podle okna - + Automatic - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x - + 32x - + 64x - + Japanese (日本語) Japonština (日本語) - + American English - + French (français) Francouzština (français) - + German (Deutsch) Nemčina (Deutsch) - + Italian (italiano) Italština (Italiano) - + Spanish (español) Španělština (español) - + Chinese Čínština - + Korean (한국어) Korejština (한국어) - + Dutch (Nederlands) Holandština (Nederlands) - + Portuguese (português) Portugalština (português) - + Russian (Русский) Ruština (Русский) - + Taiwanese Tajwanština - + British English Britská Angličtina - + Canadian French Kanadská Francouzština - + Latin American Spanish Latinsko Americká Španělština - + Simplified Chinese Zjednodušená Čínština - + Traditional Chinese (正體中文) Tradiční Čínština (正體中文) - + Brazilian Portuguese (português do Brasil) Brazilská Portugalština (português do Brasil) - + Polish (polska) - + Thai (แบบไทย) - - + + Japan Japonsko - + USA USA - + Europe Evropa - + Australia Austrálie - + China Čína - + Korea Korea - + Taiwan Taiwan - + Auto (%1) Auto select time zone - + Default (%1) Default time zone - + CET CET - + CST6CDT CST6CDT - + Cuba Cuba - + EET EET - + Egypt Egypt - + Eire Eire - + EST EST - + EST5EDT EST5EDT - + GB GB - + GB-Eire GB-Eire - + GMT GMT - + GMT+0 GMT+0 - + GMT-0 GMT-0 - + GMT0 GMT0 - + Greenwich Greenwich - + Hongkong Hongkong - + HST HST - + Iceland Island - + Iran Iran - + Israel Israel - + Jamaica Jamajka - + Kwajalein Kwajalein - + Libya Lybie - + MET MET - + MST MST - + MST7MDT MST7MDT - + Navajo Navajo - + NZ NZ - + NZ-CHAT NZ-CHAT - + Poland Polsko - + Portugal Portugalsko - + PRC PRC - + PST8PDT PST8PDT - + ROC ROC - + ROK ROK - + Singapore Singapur - + Turkey Turecko - + UCT UCT - + Universal Univerzální - + UTC UTC - + W-SU W-SU - + WET WET - + Zulu Zulu - + Mono Mono - + Stereo Stereo - + Surround Surround - + 4GB DRAM (Default) - + 6GB DRAM (Unsafe) - + 8GB DRAM - + 10GB DRAM (Unsafe) - + 12GB DRAM (Unsafe) - + Docked Zadokovaná - + Handheld Příruční - - + + Off - + Boost (1700MHz) - + Fast (2000MHz) - + Always ask (Default) Vždy se zeptat (Výchozí) - + Only if game specifies not to stop - + Never ask - - + + Medium (256) - - + + High (512) - + Very Small (16 MB) - + Small (32 MB) - + Normal (128 MB) - + Large (256 MB) - + Very Large (512 MB) - + Very Low (4 MB) - + Low (8 MB) - + Normal (16 MB) - + Medium (32 MB) - + High (64 MB) - + Very Low (32) - + Low (64) - + Normal (128) - + Disabled - + ExtendedDynamicState 1 - + ExtendedDynamicState 2 - + ExtendedDynamicState 3 - + Tree View - + Grid View @@ -3246,33 +3277,33 @@ Would you like to delete the old save data? Barva Pozadí: - + % - FSR sharpening percentage (e.g. 50%) - % + FSR/SGSR sharpening percentage (e.g. 50%) + - + Off - + VSync Off - + Recommended - + On - + VSync On @@ -4401,7 +4432,7 @@ Pro převrácení os nejprve posuňte joystick vertikálně, poté horizontáln - + Configure Konfigurovat @@ -4432,7 +4463,7 @@ Pro převrácení os nejprve posuňte joystick vertikálně, poté horizontáln - + Test Test @@ -4447,77 +4478,77 @@ Pro převrácení os nejprve posuňte joystick vertikálně, poté horizontáln Odstranit server - + %1:%2 %1:%2 - - - - - - + + + + + + Eden - + Port number has invalid characters Číslo portu obsahuje neplatné znaky - + Port has to be in range 0 and 65353 Port musí být v rozsahu 0 až 65353 - + IP address is not valid IP adresa není platná - + This UDP server already exists UDP server již existuje - + Unable to add more than 8 servers Není možné přidat více než 8 serverů - + Testing Testování - + Configuring Nastavování - + Test Successful Test byl úspěšný - + Successfully received data from the server. Úspěšně jsme získali data ze serveru. - + Test Failed Test byl neúspěšný - + Could not receive valid data from the server.<br>Please verify that the server is set up correctly and the address and port are correct. Nedostali jsme platná data ze serveru.<br>Prosím zkontrolujte, že váš server je nastaven správně a že adresa a port jsou zadány správně. - + UDP Test or calibration configuration is in progress.<br>Please wait for them to finish. Probíhá test UDP nebo konfigurace kalibrace.<br>Prosím vyčkejte na dokončení. @@ -4701,57 +4732,57 @@ Current values are %1% and %2% respectively. Některá nastavení jsou dostupná pouze, pokud hra neběží. - + Add-Ons Doplňky - + System Systém - + CPU CPU - + Graphics Grafika - + Adv. Graphics Pokroč. grafika - + Ext. Graphics - + Audio Zvuk - + Input Profiles Profily Vstupu - + Network - + Applets - + Properties Vlastnosti @@ -5761,7 +5792,7 @@ Táhněte body pro změnu pozice nebo dvojitě klikněte na buňky tabulky pro z - + Calculating... @@ -5963,50 +5994,50 @@ Please go to Configure -> System -> Network and make a selection. GRenderWindow - - + + OpenGL not available! OpenGL není k dispozici! - + OpenGL shared contexts are not supported. - + Eden has not been compiled with OpenGL support. - - - + + + Error while initializing OpenGL! Chyba při inicializaci OpenGL! - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. Vaše grafická karta pravděpodobně nepodporuje OpenGL nebo nejsou nainstalovány nejnovější ovladače. - + Error while initializing OpenGL 4.6! Chyba při inicializaci OpenGL 4.6! - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 Vaše grafická karta pravděpodobně nepodporuje OpenGL 4.6 nebo nejsou nainstalovány nejnovější ovladače.<br><br>GL Renderer:<br>%1 - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 Vaše grafická karta pravděpodobně nepodporuje jedno nebo více rozšíření OpenGL. Ujistěte se prosím, že jsou nainstalovány nejnovější ovladače.<br><br>GL Renderer:<br>%1<br><br>Nepodporované rozšíření:<br>%2 - + This build doesn't have OpenGL support. @@ -6014,279 +6045,282 @@ Please go to Configure -> System -> Network and make a selection. GameList - + &Add New Game Directory - + Favorite Oblíbené - + Start Game Spustit hru - + Start Game without Custom Configuration Spustit hru bez vlastní konfigurace - + Open Save Data Location Otevřít Lokaci Savů - + Open Mod Data Location Otevřít Lokaci Modifikací - + Open Transferable Pipeline Cache - + Link to Ryujinx - + Remove Odstranit - + Remove Installed Update Odstranit nainstalovanou aktualizaci - + Remove All Installed DLC Odstranit všechny nainstalované DLC - + Remove Custom Configuration Odstranit vlastní konfiguraci hry - + Remove Cache Storage - + Remove OpenGL Pipeline Cache - + Remove Vulkan Pipeline Cache - + Remove All Pipeline Caches - + Remove All Installed Contents Odstranit všechen nainstalovaný obsah - + Manage Play Time - + Edit Play Time Data - + Remove Play Time Data Odstranit data o době hraní - - + + Dump RomFS Vypsat RomFS - + Dump RomFS to SDMC - + Verify Integrity Ověřit Integritu - + Copy Title ID to Clipboard Zkopírovat ID Titulu do schránky - + Navigate to GameDB entry Navigovat do GameDB - + Create Shortcut Vytvořit Zástupce - + Add to Desktop - + Add to Applications Menu - + Configure Game - + Scan Subfolders Prohledat podsložky - + Remove Game Directory Odstranit složku se hrou - + ▲ Move Up ▲ Posunout nahoru - + ▼ Move Down ▼ Posunout dolů - + Open Directory Location Otevřít umístění složky - + Clear Vymazat - - - Name - Název - - - - Compatibility - Kompatibilita - - - - Add-ons - Modifkace - - - - File type - Typ-Souboru - - - - Size - Velikost - - - - Play time - Doba hraní - GameListItemCompat - + Ingame - + Game starts, but crashes or major glitches prevent it from being completed. - + Perfect Perfektní - + Game can be played without issues. Hra může být hrána bez problémů. - + Playable Hratelné - + Game functions with minor graphical or audio glitches and is playable from start to finish. Hra funguje s drobnými grafickými nebo zvukovými chybami a je hratelná od začátku do konce. - + Intro/Menu Intro/Menu - + Game loads, but is unable to progress past the Start Screen. - + Won't Boot Nebootuje - + The game crashes when attempting to startup. Hra crashuje při startu. - + Not Tested Netestováno - + The game has not yet been tested. Hra ještě nebyla testována + + GameListModel + + + Name + + + + + Compatibility + + + + + Add-ons + + + + + File type + + + + + Size + + + + + Play time + + + GameListPlaceholder - + Double-click to add a new folder to the game list Dvojitým kliknutím přidáte novou složku do seznamu her @@ -6294,17 +6328,17 @@ Please go to Configure -> System -> Network and make a selection. GameListSearchField - + %1 of %n result(s) - + Filter: Filtr: - + Enter pattern to filter Zadejte filtr @@ -6798,1139 +6832,1099 @@ Debug Message: - + Game &Icon Size - + Reset Window Size to &720p Nastavit velikost okna na &720p - + Reset Window Size to 720p Nastavit velikost okna na 720p - + Reset Window Size to &900p Resetovat Velikost Okna na &900p - + Reset Window Size to 900p Resetovat Velikost Okna na 900p - + Reset Window Size to &1080p Nastavit velikost okna na &1080p - + Reset Window Size to 1080p Nastavit velikost okna na 1080p - + &Multiplayer - + &Tools &Nástroje - + Am&iibo - + Launch &Applet - + &TAS - + &Create Home Menu Shortcut - + Install &Firmware - + &Help &Pomoc - + &Install Files to NAND... &Instalovat soubory na NAND... - + L&oad File... Načís&t soubor... - + Load &Folder... Načíst sl&ožku... - + E&xit E&xit - - + + &Pause &Pauza - + &Stop &Stop - + &Verify Installed Contents &Ověřit Nainstalovaný Obsah - + &About Eden - + Single &Window Mode &Režim jednoho okna - + Con&figure... &Nastavení - + Ctrl+, - + Enable Overlay Display Applet - + Show &Filter Bar Zobrazit &filtrovací panel - + Show &Status Bar Zobrazit &stavový řádek - + Show Status Bar Zobrazit Staus Bar - + &Browse Public Game Lobby - + &Create Room &Vytvořit Místnost - + &Leave Room &Opustit Místnost - + &Direct Connect to Room - + &Show Current Room - + F&ullscreen &Celá obrazovka - + &Restart &Restartovat - + Load/Remove &Amiibo... - + &Report Compatibility &Nahlásit kompatibilitu - + Open &Mods Page Otevřít stránku s &modifikacemi - + Open &Quickstart Guide Otevřít &rychlého průvodce - + &FAQ Často &kladené otázky - + &Capture Screenshot Za&chytit snímek obrazovky - + &Album - + &Set Nickname and Owner &Nastavit Přezdívku a Vlastníka - + &Delete Game Data &Odstranit Herní Data - + &Restore Amiibo &Obnovit Amiibo - + &Format Amiibo - + &Mii Editor - + &Configure TAS... - + Configure C&urrent Game... Nastavení současné hry - - + + &Start &Start - + &Reset &Resetovat - - + + R&ecord - + Open &Controller Menu Otevřít &Menu Ovladače - + Install Decryption &Keys - + &Home Menu - + &Desktop - + &Application Menu - + &Root Data Folder - + &NAND Folder - + &SDMC Folder - + &Mod Folder - + &Log Folder - + From Folder - + From ZIP - + &Eden Dependencies - + &Data Manager - + &Tree View - + &Grid View - + Game Icon Size - - + + None - + Show Game &Name - + Show &Performance Overlay - + + &Carousel View + + + + Small (32x32) - + Standard (64x64) - + Large (128x128) - + Full Size (256x256) - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping - + Loading Web Applet... - - + + Disable Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. - + Unmute - + Mute - + Reset Volume - + &Clear Recent Files - + &Continue - + Warning: Outdated Game Format - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. - - + + Error while loading ROM! - + The ROM format is not supported. - + An error occurred initializing the video core. - + Eden has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-mirror.github.io/help/reference/log-files/'>How to Upload the Log File</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. - + (64-bit) - + (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit - + Closing software... - + Save Data - + Mod Data - + Error Opening %1 Folder - - + + Folder does not exist! - + Remove Installed Game Contents? - + Remove Installed Game Update? - + Remove Installed Game DLC? - + Remove Entry - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? - + Remove Cache Storage? - + Remove File - + Remove Play Time Data - + Reset play time? - - + + RomFS Extraction Failed! - + There was an error copying the RomFS files or the user cancelled the operation. - + Full - + Skeleton - + Select RomFS Dump Mode - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... - - + + Cancel - + RomFS Extraction Succeeded! - + The operation completed successfully. - + Error Opening %1 - + Select Directory - + Properties - + The game properties could not be loaded. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. - + Load File - + Open Extracted ROM Directory - + Invalid Directory Selected - + The directory you have selected does not contain a 'main' file. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files - + %n file(s) remaining - + Installing file "%1"... - - + + Install Results - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application - + System Archive - + System Application Update - + Firmware Package (Type A) - + Firmware Package (Type B) - + Game - + Game Update - + Game DLC - + Delta Title - + Select NCA Install Type... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) - + Failed to Install - + The title type you selected for the NCA is invalid. - + File not found - + File "%1" not found - + OK - + Function Disabled - + Compatibility list reporting is currently disabled. Check back later! - + Error opening URL - + Unable to open the URL "%1". - + TAS Recording - + Overwrite file of player 1? - + Invalid config detected - + Handheld controller can't be used on docked mode. Pro controller will be selected. - - + + Amiibo - - + + The current amiibo has been removed - + Error - - + + The current game is not looking for amiibos - + Amiibo File (%1);; All Files (*.*) - + Load Amiibo - + Error loading Amiibo data - + The selected file is not a valid amiibo - + The selected file is already on use - + An unknown error occurred - - - Keys not installed - - - - - - Install decryption keys and restart Eden before attempting to install firmware. - - - - - Select Dumped Firmware Source Location - - - - - Select Dumped Firmware ZIP - - - - - Zipped Archives (*.zip) - - - - - Firmware cleanup failed - - - - - Failed to clean up extracted firmware cache. -Check write permissions in the system temp directory and try again. -OS reported error: %1 - - - - + No firmware available - + Firmware Corrupted - + Unknown applet - + Applet doesn't map to a known value. - + Record not found - + Applet not found. Please reinstall firmware. - + Capture Screenshot - + PNG Image (*.png) - + TAS state: Running %1/%2 - + TAS state: Recording %1 - + TAS state: Idle %1/%2 - + TAS State: Invalid - + &Stop Running - + Stop R&ecording - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor - + Speed: %1% / %2% - + Speed: %1% - + Game: %1 FPS - + Frame: %1 ms - - - FSR - - - - + NO AA - + VOLUME: MUTE - + VOLUME: %1% Volume percentage (e.g. 50%) - + Derivation Components Missing - + Decryption keys are missing. Install them now? - + Wayland Detected! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -7938,74 +7932,74 @@ Would you like to force it for future launches? - + Use X11 - + Continue with Wayland - + Don't show again - + Restart Required - + Restart Eden to apply the X11 backend. - + Slow - + Turbo - + Unlocked - + Select RomFS Dump Target - + Please select which RomFS you would like to dump. - + Are you sure you want to close Eden? - - - + + + Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? @@ -8071,6 +8065,11 @@ Would you like to bypass this and exit anyway? ScaleForce + + + FSR + + Area @@ -8082,52 +8081,62 @@ Would you like to bypass this and exit anyway? - + + SGSR + + + + + SGSR EdgeDir + + + + Docked - + Handheld - + Fast - + Balanced - + Accurate - + Vulkan - + OpenGL GLSL - + OpenGL SPIRV - + OpenGL GLASM - + Null @@ -8160,7 +8169,7 @@ If you wish to clean up the files which were left in the old data location, you - + Data was migrated successfully. @@ -9013,47 +9022,47 @@ p, li { white-space: pre-wrap; } %1 hraje %2 - + Play Time: %1 - + Never Played - + Version: %1 - + Version: 1.0.0 - + Installed SD Titles Nainstalované SD tituly - + Installed NAND Titles Nainstalované NAND tituly - + System Titles Systémové tituly - + Add New Game Directory Přidat novou složku s hrami - + Favorites Oblíbené @@ -9174,253 +9183,286 @@ p, li { white-space: pre-wrap; } QtCommon::Content - + Game Requires Firmware - + The game you are trying to launch requires firmware to boot or to get past the opening menu. Please <a href='https://yuzu-mirror.github.io/help/quickstart'>dump and install firmware</a>, or press "OK" to launch anyways. - + Installing Firmware... - - - - - + + + + + Cancel - + Firmware Install Failed - + Firmware Install Succeeded - + Firmware integrity verification failed! - - + + Verification failed for the following files: %1 - - + + Verifying integrity... + + + + Integrity verification succeeded! + + - Integrity verification succeeded! - - - - - The operation completed successfully. - - + + Integrity verification failed! - + File contents may be corrupt or missing. - + Integrity verification couldn't be performed - + Firmware installation cancelled, firmware may be in a bad state or corrupted. File contents could not be checked for validity. - + Select Dumped Keys Location - + Decryption Keys install succeeded - + Decryption Keys install failed - + Orphaned Profiles Detected! - + UNEXPECTED BAD THINGS MAY HAPPEN IF YOU DON'T READ THIS!<br>Eden has detected the following save directories with no attached profile:<br>%1<br><br>The following profiles are valid:<br>%2<br><br>Click "OK" to open your save folder and fix up your profiles.<br>Hint: copy the contents of the largest or last-modified folder elsewhere, delete all orphaned profiles, and move your copied contents to the good profile.<br><br>Still confused? See the <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/Orphaned.md'>help page</a>.<br> - + Really clear data? - + Important data may be lost! - + Are you REALLY sure? - + Once deleted, your data will NOT come back! Only do this if you're 100% sure you want to delete this data. - + Clearing... - + Select Export Location - + %1.zip - - + + + Zipped Archives (*.zip) - + Exporting data. This may take a while... - + Exporting - + Exported Successfully - + Data was exported successfully. - + Export Cancelled - + Export was cancelled by the user. - + Export Failed - + Ensure you have write permissions on the targeted directory and try again. - + Select Import Location - + Import Warning - + All previous data in this directory will be deleted. Are you sure you wish to proceed? - + Importing data. This may take a while... - + Importing - + Imported Successfully - + Data was imported successfully. - + Import Cancelled - + Import was cancelled by the user. - + Import Failed - + Ensure you have read permissions on the targeted directory and try again. + + + Keys not installed + + + + + Install decryption keys and restart Eden before attempting to install firmware. + + + + + Select Dumped Firmware Source Location + + + + + Select Dumped Firmware ZIP + + + + + Firmware cleanup failed + + + + + Failed to clean up extracted firmware cache. +Check write permissions in the system temp directory and try again. +OS reported error: %1 + + QtCommon::FS @@ -9664,72 +9706,72 @@ Would you like to manually select a portable folder to use? - + Create Shortcut - + Do you want to launch the game in fullscreen? - + Shortcut Created - + Successfully created a shortcut to %1 - + Shortcut may be Volatile! - + This will create a shortcut to the current AppImage. This may not work well if you update. Continue? - + Failed to Create Shortcut - + Failed to create a shortcut to %1 - + Create Icon - + Cannot create icon file. Path "%1" does not exist and cannot be created. - + No firmware available - + Please install firmware to use the home menu. - + Home Menu Applet - + Home Menu is not available. Please reinstall firmware. @@ -9737,55 +9779,55 @@ Would you like to manually select a portable folder to use? QtCommon::Mod - + Mod Name - + What should this mod be called? - + RomFS - + ExeFS/Patch - + Cheat - + Mod Type - + Could not detect mod type automatically. Please manually specify the type of mod you downloaded. Most mods are RomFS mods, but patches (.pchtxt) are typically ExeFS mods. - - + + Mod Extract Failed - + Failed to create temporary directory %1 - + Zip file %1 is empty @@ -10479,65 +10521,65 @@ By selecting "From Eden", previous save data stored in Ryujinx will be - + New Version Location - + All Files (*.*) - - - + + + Failed to save file - + Could not open file %1 for writing. - + Downloading... - + Cancel - + Could not write to file %1. - + Could not commit to file %1. - + Failed to download file - + Could not download from %1%2 Error code: %3 - + Download Complete - + Successfully downloaded %1. Would you like to open it? diff --git a/dist/languages/da.ts b/dist/languages/da.ts index 91380077a2..bb69f633ea 100644 --- a/dist/languages/da.ts +++ b/dist/languages/da.ts @@ -704,7 +704,7 @@ Options lower than 1X can cause artifacts. - Determines how sharpened the image will look using FSR's dynamic contrast. + Determines how sharpened the image will look using FSR's or SGSR's dynamic contrast. @@ -755,35 +755,35 @@ Disabling it is only intended for debugging. - + Use asynchronous GPU emulation - + Uses an extra CPU thread for rendering. This option should always remain enabled. - + NVDEC emulation: NVDEC-emulering: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. - + ASTC Decoding Method: - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -792,55 +792,66 @@ stuttering but may present artifacts. - + ASTC Recompression Method: - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. - + Frame Pacing Mode (Vulkan only) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. - + VRAM Usage Mode: - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. - + Skip CPU Inner Invalidation - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. - + + Anti-Flicker + + + + + Forces GPU fence callbacks to wait for submitted GPU work. +Use with Fast GPU mode, to avoid flicker with lower performance impact. + + + + VSync Mode: - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -848,1361 +859,1381 @@ Immediate (no synchronization) presents whatever is available and can exhibit te - + Sync Memory Operations - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. - + Enable asynchronous presentation (Vulkan only) - + Slightly improves performance by moving presentation to a separate CPU thread. - + Force maximum clocks (Vulkan only) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. - + Anisotropic Filtering: Anisotropisk Filtrering: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. - + GPU Mode: - + Controls the GPU emulation mode. Most games render fine with Fast or Balanced modes, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. - + DMA Accuracy: - + Controls the DMA precision accuracy. Safe precision fixes issues in some games but may degrade performance. - + Enable asynchronous shader compilation - + May reduce shader stutter. - + Fast GPU Time - + Overclocks the emulated GPU to increase dynamic resolution and render distance. Use 256 for maximal performance and 512 for maximal graphics fidelity. - + GPU Unswizzle - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. - + GPU Unswizzle Max Texture Size - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. - + GPU Unswizzle Stream Size - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. - + GPU Unswizzle Chunk Size - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. - + Use Vulkan pipeline cache - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. - + Enable Compute Pipelines (Intel Vulkan Only) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. - + Enable Reactive Flushing - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. - + Sync to framerate of video playback - + Run the game at normal speed during video playback, even when the framerate is unlocked. - + Barrier feedback loops - + Improves rendering of transparency effects in specific games. - + Enable buffer history - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. - + Fix bloom effects - + Removes bloom in Burnout. - + Enable Legacy Rescale Pass - + May fix rescale issues in some games by relying on behavior from the previous implementation. Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. - + Extended Dynamic State - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. - + Vertex Input Dynamic State - + Enables vertex input dynamic state feature for better quality and performance. - + Sample Shading - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. - + RNG Seed RNG-Seed - + Controls the seed of the random number generator. Mainly used for speedrunning. - + Device Name - + The name of the console. - + + Homebrew Args + + + + + Command-line arguments passed to homebrew at launch (e.g. -noglsl). + + + + Custom RTC Date: - + This option allows to change the clock of the console. Can be used to manipulate time in games. - + The number of seconds from the current unix time - + Language: - + This option can be overridden when region setting is auto-select - + Region: Region - + The region of the console. - + Time Zone: Tidszone - + The time zone of the console. - + Sound Output Mode: - + Console Mode: - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. - + Prompt for user profile on boot - + Useful if multiple people use the same PC. - + Pause when not in focus - + Pauses emulation when focusing on other windows. - + Confirm before stopping emulation - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. - + Hide mouse on inactivity Skjul mus ved inaktivitet - + Hides the mouse after 2.5s of inactivity. - + Disable controller applet - + Forcibly disables the use of the controller applet in emulated programs. When a program attempts to open the controller applet, it is immediately closed. - + Check for updates - + Whether or not to check for updates upon startup. - + Enable Gamemode - + Force X11 as Graphics Backend - + Custom frontend - + Real applet - + Never - + On Load - + Always - + CPU CPU - + GPU - + CPU Asynchronous - + Uncompressed (Best quality) - + BC1 (Low quality) - + BC3 (Medium quality) - - + + Auto Automatisk - + 30 FPS - + 60 FPS - + 90 FPS - + 120 FPS - + Conservative - + Aggressive - + Vulkan - + OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) - + Null - + Fast - + Balanced - - + + Accurate Nøjagtig - - + + Default Standard - + Unsafe (fast) - + Safe (stable) - + Unsafe Usikker - + Paranoid (disables most optimizations) Paranoid (deaktiverer de fleste optimeringer) - + Debugging - + Dynarmic - + NCE - + Borderless Windowed Uindrammet Vindue - + Exclusive Fullscreen Eksklusiv Fuld Skærm - + No Video Output Ingen Video-Output - + CPU Video Decoding CPU-Video Afkodning - + GPU Video Decoding (Default) GPU-Video Afkodning (Standard) - + 0.25X (180p/270p) [EXPERIMENTAL] - + 0.5X (360p/540p) [EXPERIMENTAL] - + 0.75X (540p/810p) [EXPERIMENTAL] 0,75X (540p/810p) [EKSPERIMENTEL] - + 1X (720p/1080p) 1X (720p/1080p) - + 1.25X (900p/1350p) [EXPERIMENTAL] - + 1.5X (1080p/1620p) [EXPERIMENTAL] - + 2X (1440p/2160p) 2X (1440p/2160p) - + 3X (2160p/3240p) 3X (2160p/3240p) - + 4X (2880p/4320p) 4X (2880p/4320p) - + 5X (3600p/5400p) 5X (3600p/5400p) - + 6X (4320p/6480p) 6X (4320p/6480p) - + 7X (5040p/7560p) - + 8X (5760p/8640p) - + Nearest Neighbor Nærmeste Nabo - + Bilinear Bilineær - + Bicubic Bikubisk - + Gaussian Gausisk - + Lanczos - + ScaleForce ScaleForce - + AMD FidelityFX Super Resolution - + Area - + MMPX - + Zero-Tangent - + B-Spline - + Mitchell - + Spline-1 - - + + Snapdragon Game Super Resolution + + + + + Snapdragon Game Super Resolution EdgeDir + + + + + None Ingen - + FXAA FXAA - + SMAA - + Default (16:9) Standard (16:9) - + Force 4:3 Tving 4:3 - + Force 21:9 Tving 21:9 - + Force 16:10 - + Stretch to Window Stræk til Vindue - + Automatic - + 2x - + 4x - + 8x - + 16x - + 32x - + 64x - + Japanese (日本語) Japansk (日本語) - + American English - + French (français) Fransk (français) - + German (Deutsch) Tysk (Deutsch) - + Italian (italiano) Italiensk (italiano) - + Spanish (español) Spansk (español) - + Chinese Kinesisk - + Korean (한국어) Koreansk (한국어) - + Dutch (Nederlands) Hollandsk (Nederlands) - + Portuguese (português) Portugisisk (português) - + Russian (Русский) Russisk (Русский) - + Taiwanese Taiwanesisk - + British English Britisk Engelsk - + Canadian French Candadisk Fransk - + Latin American Spanish Latinamerikansk Spansk - + Simplified Chinese Forenklet Kinesisk - + Traditional Chinese (正體中文) Traditionelt Kinesisk (正體中文) - + Brazilian Portuguese (português do Brasil) Braziliansk Portugisisk (português do Brasil) - + Polish (polska) - + Thai (แบบไทย) - - + + Japan Japan - + USA USA - + Europe Europa - + Australia Australien - + China Kina - + Korea Korea - + Taiwan Taiwan - + Auto (%1) Auto select time zone - + Default (%1) Default time zone - + CET CET - + CST6CDT CST6CDT - + Cuba Cuba - + EET EET - + Egypt Ægypten - + Eire Eire - + EST EST - + EST5EDT EST5EDT - + GB GB - + GB-Eire GB-Eire - + GMT GMT - + GMT+0 GMT+0 - + GMT-0 GMT-0 - + GMT0 GMT0 - + Greenwich Greenwich - + Hongkong Hongkong - + HST HST - + Iceland Island - + Iran Iran - + Israel Israel - + Jamaica Jamaica - + Kwajalein Kwajalein - + Libya Libyen - + MET MET - + MST MST - + MST7MDT MST7MDT - + Navajo Navajo - + NZ NZ - + NZ-CHAT NZ-CHAT - + Poland Polen - + Portugal Portugal - + PRC PRC - + PST8PDT PST8PDT - + ROC ROC - + ROK ROK - + Singapore Singapore - + Turkey Tyrkiet - + UCT UCT - + Universal Universel - + UTC UTC - + W-SU W-SU - + WET WET - + Zulu Zulu - + Mono Mono - + Stereo Stereo - + Surround Surround - + 4GB DRAM (Default) - + 6GB DRAM (Unsafe) - + 8GB DRAM - + 10GB DRAM (Unsafe) - + 12GB DRAM (Unsafe) - + Docked Dokket - + Handheld Håndholdt - - + + Off - + Boost (1700MHz) - + Fast (2000MHz) - + Always ask (Default) - + Only if game specifies not to stop - + Never ask - - + + Medium (256) - - + + High (512) - + Very Small (16 MB) - + Small (32 MB) - + Normal (128 MB) - + Large (256 MB) - + Very Large (512 MB) - + Very Low (4 MB) - + Low (8 MB) - + Normal (16 MB) - + Medium (32 MB) - + High (64 MB) - + Very Low (32) - + Low (64) - + Normal (128) - + Disabled - + ExtendedDynamicState 1 - + ExtendedDynamicState 2 - + ExtendedDynamicState 3 - + Tree View - + Grid View @@ -3254,33 +3285,33 @@ Would you like to delete the old save data? Baggrundsfarve: - + % - FSR sharpening percentage (e.g. 50%) - % + FSR/SGSR sharpening percentage (e.g. 50%) + - + Off - + VSync Off - + Recommended - + On - + VSync On @@ -4409,7 +4440,7 @@ Bevæg, for at omvende akserne, først din styrepind lodret og så vandret. - + Configure Konfigurér @@ -4440,7 +4471,7 @@ Bevæg, for at omvende akserne, først din styrepind lodret og så vandret. - + Test Afprøv @@ -4455,77 +4486,77 @@ Bevæg, for at omvende akserne, først din styrepind lodret og så vandret.Fjernserver - + %1:%2 %1:%2 - - - - - - + + + + + + Eden - + Port number has invalid characters Portnummer indeholder ugyldige tegn - + Port has to be in range 0 and 65353 Port skal være imellem 0 and 65353 - + IP address is not valid IP-adresse er ikke gyldig - + This UDP server already exists Denne UDP-server eksisterer allerede - + Unable to add more than 8 servers Ude af stand til, at tilføje mere end 8 servere - + Testing Afprøvning - + Configuring Konfigurér - + Test Successful Afprøvning Lykkedes - + Successfully received data from the server. Modtagelse af data fra serveren lykkedes. - + Test Failed Afprøvning Mislykkedes - + Could not receive valid data from the server.<br>Please verify that the server is set up correctly and the address and port are correct. Kunne ikke modtage gyldig data fra serveren.<br>Bekræft venligst, at serveren er opsat korrekt, og at adressen og porten er korrekte. - + UDP Test or calibration configuration is in progress.<br>Please wait for them to finish. UDP-Afprøvnings- eller -kalibreringskonfiguration er i gang.<br>vent venligst på, at de bliver færdige. @@ -4709,57 +4740,57 @@ Current values are %1% and %2% respectively. - + Add-Ons Tilføjelser - + System System - + CPU CPU - + Graphics Grafik - + Adv. Graphics - + Ext. Graphics - + Audio Lyd - + Input Profiles - + Network - + Applets - + Properties Egenskaber @@ -5769,7 +5800,7 @@ Træk punkter, for at skifte position, eller dobbeltklik i tabelceller, for at r - + Calculating... @@ -5971,50 +6002,50 @@ Please go to Configure -> System -> Network and make a selection. GRenderWindow - - + + OpenGL not available! - + OpenGL shared contexts are not supported. - + Eden has not been compiled with OpenGL support. - - - + + + Error while initializing OpenGL! - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. - + Error while initializing OpenGL 4.6! - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 - + This build doesn't have OpenGL support. @@ -6022,279 +6053,282 @@ Please go to Configure -> System -> Network and make a selection. GameList - + &Add New Game Directory - + Favorite - + Start Game - + Start Game without Custom Configuration - + Open Save Data Location Åbn Gemt Data-Placering - + Open Mod Data Location Åbn Mod-Data-Placering - + Open Transferable Pipeline Cache - + Link to Ryujinx - + Remove Fjern - + Remove Installed Update - + Remove All Installed DLC - + Remove Custom Configuration - + Remove Cache Storage - + Remove OpenGL Pipeline Cache - + Remove Vulkan Pipeline Cache - + Remove All Pipeline Caches - + Remove All Installed Contents - + Manage Play Time - + Edit Play Time Data - + Remove Play Time Data - - + + Dump RomFS - + Dump RomFS to SDMC - + Verify Integrity - + Copy Title ID to Clipboard Kopiér Titel-ID til Udklipsholder - + Navigate to GameDB entry - + Create Shortcut - + Add to Desktop - + Add to Applications Menu - + Configure Game - + Scan Subfolders - + Remove Game Directory - + ▲ Move Up - + ▼ Move Down - + Open Directory Location - + Clear Ryd - - - Name - Navn - - - - Compatibility - Kompatibilitet - - - - Add-ons - Tilføjelser - - - - File type - Filtype - - - - Size - Størrelse - - - - Play time - - GameListItemCompat - + Ingame - + Game starts, but crashes or major glitches prevent it from being completed. - + Perfect Perfekt - + Game can be played without issues. - + Playable - + Game functions with minor graphical or audio glitches and is playable from start to finish. - + Intro/Menu Intro/Menu - + Game loads, but is unable to progress past the Start Screen. - + Won't Boot Starter Ikke Op - + The game crashes when attempting to startup. - + Not Tested Ikke Afprøvet - + The game has not yet been tested. Spillet er endnu ikke blevet afprøvet. + + GameListModel + + + Name + + + + + Compatibility + + + + + Add-ons + + + + + File type + + + + + Size + + + + + Play time + + + GameListPlaceholder - + Double-click to add a new folder to the game list @@ -6302,17 +6336,17 @@ Please go to Configure -> System -> Network and make a selection. GameListSearchField - + %1 of %n result(s) - + Filter: Filter: - + Enter pattern to filter @@ -6806,1139 +6840,1099 @@ Debug Message: - + Game &Icon Size - + Reset Window Size to &720p - + Reset Window Size to 720p - + Reset Window Size to &900p - + Reset Window Size to 900p - + Reset Window Size to &1080p - + Reset Window Size to 1080p - + &Multiplayer - + &Tools - + Am&iibo - + Launch &Applet - + &TAS - + &Create Home Menu Shortcut - + Install &Firmware - + &Help &Hjælp - + &Install Files to NAND... - + L&oad File... - + Load &Folder... - + E&xit - - + + &Pause - + &Stop - + &Verify Installed Contents - + &About Eden - + Single &Window Mode - + Con&figure... - + Ctrl+, - + Enable Overlay Display Applet - + Show &Filter Bar - + Show &Status Bar - + Show Status Bar Vis Statuslinje - + &Browse Public Game Lobby - + &Create Room - + &Leave Room - + &Direct Connect to Room - + &Show Current Room - + F&ullscreen - + &Restart - + Load/Remove &Amiibo... - + &Report Compatibility - + Open &Mods Page - + Open &Quickstart Guide - + &FAQ - + &Capture Screenshot - + &Album - + &Set Nickname and Owner - + &Delete Game Data - + &Restore Amiibo - + &Format Amiibo - + &Mii Editor - + &Configure TAS... - + Configure C&urrent Game... - - + + &Start - + &Reset - - + + R&ecord - + Open &Controller Menu - + Install Decryption &Keys - + &Home Menu - + &Desktop - + &Application Menu - + &Root Data Folder - + &NAND Folder - + &SDMC Folder - + &Mod Folder - + &Log Folder - + From Folder - + From ZIP - + &Eden Dependencies - + &Data Manager - + &Tree View - + &Grid View - + Game Icon Size - - + + None - + Show Game &Name - + Show &Performance Overlay - + + &Carousel View + + + + Small (32x32) - + Standard (64x64) - + Large (128x128) - + Full Size (256x256) - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping - + Loading Web Applet... - - + + Disable Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. - + Unmute - + Mute - + Reset Volume - + &Clear Recent Files - + &Continue - + Warning: Outdated Game Format - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. - - + + Error while loading ROM! - + The ROM format is not supported. - + An error occurred initializing the video core. - + Eden has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-mirror.github.io/help/reference/log-files/'>How to Upload the Log File</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. - + (64-bit) - + (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit - + Closing software... - + Save Data - + Mod Data - + Error Opening %1 Folder - - + + Folder does not exist! - + Remove Installed Game Contents? - + Remove Installed Game Update? - + Remove Installed Game DLC? - + Remove Entry - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? - + Remove Cache Storage? - + Remove File - + Remove Play Time Data - + Reset play time? - - + + RomFS Extraction Failed! - + There was an error copying the RomFS files or the user cancelled the operation. - + Full - + Skeleton - + Select RomFS Dump Mode - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... - - + + Cancel - + RomFS Extraction Succeeded! - + The operation completed successfully. - + Error Opening %1 - + Select Directory - + Properties - + The game properties could not be loaded. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. - + Load File - + Open Extracted ROM Directory - + Invalid Directory Selected - + The directory you have selected does not contain a 'main' file. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files - + %n file(s) remaining - + Installing file "%1"... - - + + Install Results - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application - + System Archive - + System Application Update - + Firmware Package (Type A) - + Firmware Package (Type B) - + Game - + Game Update - + Game DLC - + Delta Title - + Select NCA Install Type... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) - + Failed to Install - + The title type you selected for the NCA is invalid. - + File not found - + File "%1" not found - + OK - + Function Disabled - + Compatibility list reporting is currently disabled. Check back later! - + Error opening URL - + Unable to open the URL "%1". - + TAS Recording - + Overwrite file of player 1? - + Invalid config detected - + Handheld controller can't be used on docked mode. Pro controller will be selected. - - + + Amiibo - - + + The current amiibo has been removed - + Error - - + + The current game is not looking for amiibos - + Amiibo File (%1);; All Files (*.*) - + Load Amiibo - + Error loading Amiibo data - + The selected file is not a valid amiibo - + The selected file is already on use - + An unknown error occurred - - - Keys not installed - - - - - - Install decryption keys and restart Eden before attempting to install firmware. - - - - - Select Dumped Firmware Source Location - - - - - Select Dumped Firmware ZIP - - - - - Zipped Archives (*.zip) - - - - - Firmware cleanup failed - - - - - Failed to clean up extracted firmware cache. -Check write permissions in the system temp directory and try again. -OS reported error: %1 - - - - + No firmware available - + Firmware Corrupted - + Unknown applet - + Applet doesn't map to a known value. - + Record not found - + Applet not found. Please reinstall firmware. - + Capture Screenshot - + PNG Image (*.png) - + TAS state: Running %1/%2 - + TAS state: Recording %1 - + TAS state: Idle %1/%2 - + TAS State: Invalid - + &Stop Running - + Stop R&ecording - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor - + Speed: %1% / %2% - + Speed: %1% - + Game: %1 FPS - + Frame: %1 ms - - - FSR - - - - + NO AA - + VOLUME: MUTE - + VOLUME: %1% Volume percentage (e.g. 50%) - + Derivation Components Missing - + Decryption keys are missing. Install them now? - + Wayland Detected! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -7946,74 +7940,74 @@ Would you like to force it for future launches? - + Use X11 - + Continue with Wayland - + Don't show again - + Restart Required - + Restart Eden to apply the X11 backend. - + Slow - + Turbo - + Unlocked - + Select RomFS Dump Target - + Please select which RomFS you would like to dump. - + Are you sure you want to close Eden? - - - + + + Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? @@ -8079,6 +8073,11 @@ Would you like to bypass this and exit anyway? ScaleForce + + + FSR + + Area @@ -8090,52 +8089,62 @@ Would you like to bypass this and exit anyway? - + + SGSR + + + + + SGSR EdgeDir + + + + Docked - + Handheld - + Fast - + Balanced - + Accurate - + Vulkan - + OpenGL GLSL - + OpenGL SPIRV - + OpenGL GLASM - + Null @@ -8168,7 +8177,7 @@ If you wish to clean up the files which were left in the old data location, you - + Data was migrated successfully. @@ -9017,47 +9026,47 @@ p, li { white-space: pre-wrap; } - + Play Time: %1 - + Never Played - + Version: %1 - + Version: 1.0.0 - + Installed SD Titles Installerede SD-Titler - + Installed NAND Titles Installerede NAND-Titler - + System Titles Systemtitler - + Add New Game Directory Tilføj Ny Spilmappe - + Favorites @@ -9178,253 +9187,286 @@ p, li { white-space: pre-wrap; } QtCommon::Content - + Game Requires Firmware - + The game you are trying to launch requires firmware to boot or to get past the opening menu. Please <a href='https://yuzu-mirror.github.io/help/quickstart'>dump and install firmware</a>, or press "OK" to launch anyways. - + Installing Firmware... - - - - - + + + + + Cancel - + Firmware Install Failed - + Firmware Install Succeeded - + Firmware integrity verification failed! - - + + Verification failed for the following files: %1 - - + + Verifying integrity... + + + + Integrity verification succeeded! + + - Integrity verification succeeded! - - - - - The operation completed successfully. - - + + Integrity verification failed! - + File contents may be corrupt or missing. - + Integrity verification couldn't be performed - + Firmware installation cancelled, firmware may be in a bad state or corrupted. File contents could not be checked for validity. - + Select Dumped Keys Location - + Decryption Keys install succeeded - + Decryption Keys install failed - + Orphaned Profiles Detected! - + UNEXPECTED BAD THINGS MAY HAPPEN IF YOU DON'T READ THIS!<br>Eden has detected the following save directories with no attached profile:<br>%1<br><br>The following profiles are valid:<br>%2<br><br>Click "OK" to open your save folder and fix up your profiles.<br>Hint: copy the contents of the largest or last-modified folder elsewhere, delete all orphaned profiles, and move your copied contents to the good profile.<br><br>Still confused? See the <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/Orphaned.md'>help page</a>.<br> - + Really clear data? - + Important data may be lost! - + Are you REALLY sure? - + Once deleted, your data will NOT come back! Only do this if you're 100% sure you want to delete this data. - + Clearing... - + Select Export Location - + %1.zip - - + + + Zipped Archives (*.zip) - + Exporting data. This may take a while... - + Exporting - + Exported Successfully - + Data was exported successfully. - + Export Cancelled - + Export was cancelled by the user. - + Export Failed - + Ensure you have write permissions on the targeted directory and try again. - + Select Import Location - + Import Warning - + All previous data in this directory will be deleted. Are you sure you wish to proceed? - + Importing data. This may take a while... - + Importing - + Imported Successfully - + Data was imported successfully. - + Import Cancelled - + Import was cancelled by the user. - + Import Failed - + Ensure you have read permissions on the targeted directory and try again. + + + Keys not installed + + + + + Install decryption keys and restart Eden before attempting to install firmware. + + + + + Select Dumped Firmware Source Location + + + + + Select Dumped Firmware ZIP + + + + + Firmware cleanup failed + + + + + Failed to clean up extracted firmware cache. +Check write permissions in the system temp directory and try again. +OS reported error: %1 + + QtCommon::FS @@ -9668,72 +9710,72 @@ Would you like to manually select a portable folder to use? - + Create Shortcut - + Do you want to launch the game in fullscreen? - + Shortcut Created - + Successfully created a shortcut to %1 - + Shortcut may be Volatile! - + This will create a shortcut to the current AppImage. This may not work well if you update. Continue? - + Failed to Create Shortcut - + Failed to create a shortcut to %1 - + Create Icon - + Cannot create icon file. Path "%1" does not exist and cannot be created. - + No firmware available - + Please install firmware to use the home menu. - + Home Menu Applet - + Home Menu is not available. Please reinstall firmware. @@ -9741,55 +9783,55 @@ Would you like to manually select a portable folder to use? QtCommon::Mod - + Mod Name - + What should this mod be called? - + RomFS - + ExeFS/Patch - + Cheat - + Mod Type - + Could not detect mod type automatically. Please manually specify the type of mod you downloaded. Most mods are RomFS mods, but patches (.pchtxt) are typically ExeFS mods. - - + + Mod Extract Failed - + Failed to create temporary directory %1 - + Zip file %1 is empty @@ -10473,65 +10515,65 @@ By selecting "From Eden", previous save data stored in Ryujinx will be - + New Version Location - + All Files (*.*) - - - + + + Failed to save file - + Could not open file %1 for writing. - + Downloading... - + Cancel - + Could not write to file %1. - + Could not commit to file %1. - + Failed to download file - + Could not download from %1%2 Error code: %3 - + Download Complete - + Successfully downloaded %1. Would you like to open it? diff --git a/dist/languages/de.ts b/dist/languages/de.ts index 93652b117d..675ef44d9b 100644 --- a/dist/languages/de.ts +++ b/dist/languages/de.ts @@ -26,17 +26,24 @@ li.unchecked::marker { content: "\2610"; } li.checked::marker { content: "\2612"; } </style></head><body style=" font-family:'Noto Sans'; font-size:9pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:12pt;">Eden is an experimental open-source emulator for the Nintendo Switch licensed under GPLv3.0+ which is based on the yuzu emulator which ended development back in March 2024. <br /><br />This software should not be used to play games you have not legally obtained.</span></p></body></html> - + <!DOCTYPE HTML PUBLIC „-//W3C//DTD HTML 4.0//EN“ „http://www.w3.org/TR/REC-html40/strict.dtd“> +<html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style type="text/css"> +p, li { white-space: pre-wrap; } +hr { height: 1px; border-width: 0; } +li.unchecked::marker { content: „\2610“; } +li.checked::marker { content: „\2612“; } +</style></head><body style=" font-family:‚Noto Sans‘; font-size:9pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:‚MS Shell Dlg 2‘; font-size:12pt;">Eden ist ein experimenteller, quelloffener Emulator für die Nintendo Switch, der unter der GPLv3.0+ lizenziert ist und auf dem Emulator yuzu basiert, dessen Entwicklung im März 2024 eingestellt wurde. <br /><br />Diese Software sollte nicht zum Spielen von Spielen verwendet werden, die du nicht legal erworben hast.</span></p></body></html> <html><head/><body><p><a href="https://eden-emulator.github.io/"><span style=" text-decoration: underline; color:#039be5;">Website</span></a> | <a href="https://git.eden-emu.dev"><span style=" text-decoration: underline; color:#039be5;">Source Code</span></a> | <a href="https://git.eden-emu.dev/eden-emu/eden/activity/contributors"><span style=" text-decoration: underline; color:#039be5;">Contributors</span></a> | <a href="https://discord.gg/HstXbPch7X"><span style=" text-decoration: underline; color:#039be5;">Discord</span></a> | <a href="https://stt.gg/qKgFEAbH"><span style=" text-decoration: underline; color:#039be5;">Stoat</span></a> | <a href="https://nitter.poast.org/edenemuofficial"><span style=" text-decoration: underline; color:#039be5;">Twitter</span></a> | <a href="https://git.eden-emu.dev/eden-emu/eden/src/branch/master/LICENSE.txt"><span style=" text-decoration: underline; color:#039be5;">License</span></a></p></body></html> - + <html><head/><body><p><a href="https://eden-emulator.github.io/"><span style=" text-decoration: underline; color:#039be5;">Webseite</span></a> | <a href="https://git.eden-emu.dev"><span style=" text-decoration: underline; color:#039be5;">Quell-Code</span></a> | <a href="https://git.eden-emu.dev/eden-emu/eden/activity/contributors"><span style=" text-decoration: underline; color:#039be5;">Mitwirkende</span></a> | <a href="https://discord.gg/HstXbPch7X"><span style=" text-decoration: underline; color:#039be5;">Discord</span></a> | <a href="https://stt.gg/qKgFEAbH"><span style=" text-decoration: underline; color:#039be5;">Hermelin</span></a> | <a href="https://nitter.poast.org/edenemuofficial"><span style=" text-decoration: underline; color:#039be5;">Twitter</span></a> | <a href="https://git.eden-emu.dev/eden-emu/eden/src/branch/master/LICENSE.txt"><span style=" text-decoration: underline; color:#039be5;">Lizenz</span></a></p></body></html> <html><head/><body><p><span style=" font-size:7pt;">&quot;Nintendo Switch&quot; is a trademark of Nintendo. Eden is not affiliated with Nintendo in any way.</span></p></body></html> - + <html><head/><body><p><span style=" font-size:7pt;">„Nintendo Switch“ ist eine Marke von Nintendo. Eden steht in keinerlei Verbindung zu Nintendo.</span></p></body></html> @@ -231,7 +238,7 @@ Dies würde deren Forum-Benutzernamen und deren IP-Adresse sperren. <html><head/><body><p><span style=" font-size:10pt;">Should you choose to submit a test case to the </span><a href="https://eden-emulator.github.io/game/"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">eden Compatibility List</span></a><span style=" font-size:10pt;">, The following information will be collected and displayed on the site:</span></p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Hardware Information (CPU / GPU / Operating System)</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Which version of eden you are running</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">The connected eden account</li></ul></body></html> - + <html><head/><body><p><span style=" font-size:10pt;">Solltest du dich dazu entscheiden, einen Testfall an die </span><a href="https://eden-emulator.github.io/game/"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">eden-Kompatibilitätsliste</span></a><span style=" font-size:10pt;"> zu übermitteln, werden die folgenden Informationen erfasst und auf der Webseite angezeigt:</span></p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Hardware-Informationen (CPU / GPU / Betriebssystem)</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Welche Version von eden du ausführst</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Das verknüpfte eden-Konto</li></ul></body></html> @@ -256,7 +263,7 @@ Dies würde deren Forum-Benutzernamen und deren IP-Adresse sperren. No The game crashes or freezes while loading or using the menu - Nein das Spiel stürzt ab oder friert ein während des Ladens des Menüs. + Nein Das Spiel stürzt ab oder friert ein, während es geladen wird oder das Menü aufgerufen wird @@ -266,12 +273,12 @@ Dies würde deren Forum-Benutzernamen und deren IP-Adresse sperren. Yes The game works without crashes - Ja Das Spiel funktioniert ohne Abstürze. + Ja Das Spiel läuft ohne Abstürze No The game crashes or freezes during gameplay - Nein Das Spiel stürzt ab oder freezed während des spielen. + Nein Das Spiel stürzt während des Spielens ab oder friert ein @@ -281,12 +288,12 @@ Dies würde deren Forum-Benutzernamen und deren IP-Adresse sperren. Yes The game can be finished without any workarounds - Ja Das Spiel kann ohne Workarounds abgeschlossen werden. + Ja Das Spiel lässt sich ohne Umwege durchspielen No The game can't progress past a certain area - Nein Spezielle Bereiche des Spieles können nicht abgeschlossen werden. + Nein Das Spiel kommt ab einem bestimmten Punkt nicht mehr weiter @@ -351,7 +358,7 @@ Dies würde deren Forum-Benutzernamen und deren IP-Adresse sperren. An error occurred while sending the Testcase - Beim Senden des Berichtes ist ein Fehler aufgetreten. + Beim Senden des Testfalls ist ein Fehler aufgetreten @@ -379,7 +386,7 @@ Dies würde deren Forum-Benutzernamen und deren IP-Adresse sperren. Data erase - + Datenlöschung @@ -389,12 +396,12 @@ Dies würde deren Forum-Benutzernamen und deren IP-Adresse sperren. Net connect - + Netzwerkverbindung Player select - + Spielerauswahl @@ -404,52 +411,52 @@ Dies würde deren Forum-Benutzernamen und deren IP-Adresse sperren. Mii Edit - + Mii bearbeiten Online web - + Online-Web Shop - + Geschäft Photo viewer - + Fotobetrachter Offline web - + Offline-Web Login share - + Anmelden Teilen Wifi web auth - + WLAN-Web-Authentifizierung My page - + Meine Seite Enable Overlay Applet - + Überlagerungs-Applet aktivieren Enables Horizon's built-in overlay applet. Press and hold the home button for 1 second to show it. - + Aktiviert das in Horizon integrierte Überlagerungs-Applet. Halte die Home-Taste 1 Sekunde lang gedrückt, um es anzuzeigen. @@ -502,7 +509,8 @@ Dies ist vor allem eine Debug-Option und sollte nicht deaktiviert werden. Increases the amount of emulated RAM. Doesn't affect performance/stability but may allow HD texture mods to load. - + Erhöht die Menge des emulierten Arbeitsspeichers. +Hat keinen Einfluss auf Leistung oder Stabilität, ermöglicht jedoch möglicherweise das Laden von HD-Textur-Mods. @@ -548,7 +556,8 @@ Deaktivieren bedeutet das keine Wiederholratenbegrenzung stattfindet. Synchronizes CPU core speed with the game's maximum rendering speed to boost FPS without affecting game speed (animations, physics, etc.). Can help reduce stuttering at lower framerates. - + Synchronisiert die Taktfrequenz der CPU-Kerne mit der maximalen Rendering-Geschwindigkeit des Spiels, um die BpS zu steigern, ohne die Spielgeschwindigkeit (Animationen, Physik usw.) zu beeinträchtigen. +Kann dazu beitragen, Ruckler bei niedrigeren Bildraten zu reduzieren. @@ -581,12 +590,12 @@ Verwende Boost (1700MHz), um mit der höchsten nativen Taktrate der Switch zu la Custom CPU Ticks - + Benutzerdefinierte CPU-Ticks Set a custom value of CPU ticks. Higher values can increase performance, but may cause deadlocks. A range of 77-21000 is recommended. - + Lege einen benutzerdefinierten Wert für die CPU-Ticks fest. Höhere Werte können die Leistung steigern, jedoch auch zu Verklemmungen führen. Es wird ein Bereich von 77 bis 21000 empfohlen. @@ -639,7 +648,8 @@ Disabling this forces all memory accesses to use Software MMU Emulation. This option improves speed by removing NaN checking. Please note this also reduces accuracy of certain floating-point instructions. - + Diese Option verbessert die Geschwindigkeit durch Wegfall der NaN-Prüfung. +Bitte beachte, dass dies auch die Genauigkeit bestimmter Gleitkommaoperationen verringert. @@ -650,7 +660,8 @@ Please note this also reduces accuracy of certain floating-point instructions. This option improves speed by eliminating a safety check before every memory operation. Disabling it may allow arbitrary code execution. - + Diese Option verbessert die Geschwindigkeit, indem sie eine Sicherheitsprüfung vor jedem Speicherzugriff überspringt. +Wird sie deaktiviert, kann dies zur Ausführung von beliebigem Code führen. @@ -661,7 +672,8 @@ Disabling it may allow arbitrary code execution. This option improves speed by relying only on the semantics of cmpxchg to ensure safety of exclusive access instructions. Please note this may result in deadlocks and other race conditions. - + Diese Option erhöht die Geschwindigkeit, indem sie sich ausschließlich auf die Semantik von `cmpxchg` stützt, um die Sicherheit von Befehlen für exklusiven Zugriff zu gewährleisten. +Bitte beachte, dass dies zu Verklemmungen und anderen Race Conditions führen kann. @@ -672,7 +684,8 @@ Please note this may result in deadlocks and other race conditions. Changes the output graphics API. Vulkan is recommended. - + Ändert die Ausgabe-Grafik-API. +Vulkan wird empfohlen. @@ -694,7 +707,9 @@ Vulkan is recommended. Forces to render at a different resolution. Higher resolutions require more VRAM and bandwidth. Options lower than 1X can cause artifacts. - + Erzwingt das Rendern in einer anderen Auflösung. +Höhere Auflösungen erfordern mehr VRAM und Bandbreite. +Optionen unter 1x können Artefakte verursachen. @@ -704,12 +719,12 @@ Options lower than 1X can cause artifacts. FSR Sharpness: - FSR-Schärfe + FSR-Schärfe: - Determines how sharpened the image will look using FSR's dynamic contrast. - + Determines how sharpened the image will look using FSR's or SGSR's dynamic contrast. + Legt fest, wie scharf das Bild mithilfe des dynamischen Kontrasts von FSR oder SGSR dargestellt wird. @@ -721,7 +736,9 @@ Options lower than 1X can cause artifacts. The anti-aliasing method to use. SMAA offers the best quality. FXAA can produce a more stable picture in lower resolutions. - + Die zu verwendende Anti-Aliasing-Methode. +SMAA bietet die beste Bildqualität. +FXAA liefert bei niedrigeren Auflösungen ein stabileres Bild. @@ -733,7 +750,9 @@ FXAA can produce a more stable picture in lower resolutions. The method used to render the window in fullscreen. Borderless offers the best compatibility with the on-screen keyboard that some games request for input. Exclusive fullscreen may offer better performance and better Freesync/Gsync support. - + Die Methode, mit der das Fenster im Vollbildmodus dargestellt wird. +Der randlose Modus bietet die beste Kompatibilität mit der Bildschirmtastatur, die manche Spiele für die Eingabe erfordern. +Der exklusive Vollbildmodus bietet möglicherweise eine bessere Leistung und eine bessere Freesync-/G-Sync-Unterstützung. @@ -745,216 +764,249 @@ Exclusive fullscreen may offer better performance and better Freesync/Gsync supp Stretches the renderer to fit the specified aspect ratio. Most games only support 16:9, so modifications are required to get other ratios. Also controls the aspect ratio of captured screenshots. - + Dehnt den Renderer so, dass er dem angegebenen Seitenverhältnis entspricht. +Die meisten Spiele unterstützen nur das Seitenverhältnis 16:9, sodass Anpassungen erforderlich sind, um andere Seitenverhältnisse zu erhalten. +Steuert außerdem das Seitenverhältnis der aufgenommenen Bildschirmfotos. Use persistent pipeline cache - + Persistenten Rohrleitungs-Cache verwenden Allows saving shaders to storage for faster loading on following game boots. Disabling it is only intended for debugging. - - - - - Use asynchronous GPU emulation - + Ermöglicht das Speichern von Schattierern, um sie beim nächsten Spielstart schneller laden zu können. +Die Deaktivierung ist ausschließlich für Fehlerbehebungszwecke vorgesehen. - Uses an extra CPU thread for rendering. -This option should always remain enabled. - + Use asynchronous GPU emulation + Asynchrone GPU-Emulation verwenden + Uses an extra CPU thread for rendering. +This option should always remain enabled. + Verwendet einen zusätzlichen CPU-Thread für das Rendern. +Diese Option sollte immer aktiviert bleiben. + + + NVDEC emulation: NVDEC-Emulation: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. - + Legt fest, wie Videos decodiert werden sollen. +Dabei kann entweder die CPU oder die GPU für die Dekodierung verwendet werden, oder es findet überhaupt keine Dekodierung statt (schwarzer Bildschirm bei Videos). +In den meisten Fällen bietet die GPU-Dekodierung die beste Leistung. - + ASTC Decoding Method: ASTC-Dekodier-Methode: - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). CPU Asynchronously: Use the CPU to decode ASTC textures on demand. EliminatesASTC decoding stuttering but may present artifacts. - + Diese Option legt fest, wie ASTC-Texturen dekodiert werden sollen. +CPU: Die Dekodierung erfolgt über die CPU. +GPU: Die Dekodierung von ASTC-Texturen erfolgt über die Berechnungs-Schattierer der GPU (empfohlen). +CPU asynchron: Die Dekodierung von ASTC-Texturen erfolgt bei Bedarf über die CPU. Dies verhindert Ruckeln bei der ASTC-Dekodierung, +kann jedoch zu Artefakten führen. - + ASTC Recompression Method: ASTC-Rekompression-Methode: - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. - - - - - Frame Pacing Mode (Vulkan only) - + Die meisten GPUs unterstützen keine ASTC-Texturen und müssen diese daher in ein Zwischenformat dekomprimieren: RGBA8. +BC1/BC3: Das Zwischenformat wird wieder in das BC1- oder BC3-Format komprimiert, +was VRAM spart, jedoch zu Einbußen bei der Bildqualität führt. - Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. - + Frame Pacing Mode (Vulkan only) + Frame-Pacing-Modus (nur Vulkan) - + + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. + Steuert, wie der Emulator das Frame-Pacing verwaltet, um Ruckeln zu reduzieren und die Bildrate flüssiger und gleichmäßiger zu gestalten. + + + VRAM Usage Mode: VRAM-Nutzungs Modus: - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. - + Legt fest, ob der Emulator vorrangig Speicherplatz sparen oder den verfügbaren Grafikspeicher zur Leistungsoptimierung maximal ausnutzen soll. +Der aggressive Modus kann die Leistung anderer Anwendungen, wie z. B. Aufnahmesoftware, beeinträchtigen. - + Skip CPU Inner Invalidation CPU-interne Invalidierung überspringen - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. Überspringt bestimmte Cache-Invalidierungen auf CPU-Seite während Speicherupdates, reduziert die CPU-Auslastung und verbessert die Leistung. Dies verursacht vielleicht Abstürze. - + + Anti-Flicker + Anti-Flackern + + + + Forces GPU fence callbacks to wait for submitted GPU work. +Use with Fast GPU mode, to avoid flicker with lower performance impact. + Erzwingt, dass GPU-Zaun-Rückrufe auf übermittelte GPU-Arbeit warten. +In Verbindung mit dem „Schnelle GPU“-Modus verwenden, um Flackern bei geringeren Leistungseinbußen zu vermeiden. + + + VSync Mode: VSync-Modus: - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. Immediate (no synchronization) presents whatever is available and can exhibit tearing. - + FIFO (VSync) lässt keine Frames ausfallen und verursacht kein Tearing, ist jedoch durch die Bildwiederholfrequenz des Bildschirms begrenzt. +FIFO Relaxed lässt Tearing zu, während es sich von einer Verlangsamung erholt. +Postkasten kann eine geringere Latenz als FIFO aufweisen und verursacht kein Tearing, lässt jedoch möglicherweise Bilder ausfallen. +Sofort (ohne Synchronisation) zeigt alles an, was verfügbar ist, und kann Tearing verursachen. - + Sync Memory Operations Speicheroperationen synchronisieren - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. - + Gewährleistet die Datenkonsistenz zwischen Rechen- und Speicheroperationen. +Diese Option behebt Probleme in Spielen, kann jedoch zu Leistungseinbußen führen. +Bei Spielen mit Unreal Engine 4 sind die Auswirkungen oft am deutlichsten spürbar. - + Enable asynchronous presentation (Vulkan only) Aktiviere asynchrone Präsentation (Nur Vulkan) - + Slightly improves performance by moving presentation to a separate CPU thread. - + Verbessert die Leistung geringfügig, indem die Darstellung auf einen separaten CPU-Thread verlagert wird. - + Force maximum clocks (Vulkan only) Erzwinge Maximale Taktrate (Vulkan only) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. Lässt im Hintergrund die GPU Aufgaben erledigen während diese auf Grafikbefehle wartet, damit diese nicht herunter taktet. - + Anisotropic Filtering: Anisotrope Filterung: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. - + Steuert die Qualität der Texturwiedergabe bei schrägen Blickwinkeln. +Bei den meisten Grafikkarten kann dieser Wert bedenkenlos auf 16x eingestellt werden. - + GPU Mode: GPU-Modus: - + Controls the GPU emulation mode. Most games render fine with Fast or Balanced modes, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. Steuert den GPU-Emulationsmodus. Die meisten Spiele werden im Modus Schnell oder Ausgeglichen gut gerendert, für einige ist jedoch weiterhin der Modus Akkurat erforderlich. Partikel werden in der Regel nur im Modus Akkurat korrekt gerendert. - + DMA Accuracy: DMA-Genauigkeit: - + Controls the DMA precision accuracy. Safe precision fixes issues in some games but may degrade performance. - + Steuert die Genauigkeit der DMA-Präzision. Die „Sichere“-Präzision behebt Probleme in einigen Spielen, kann jedoch zu Leistungseinbußen führen. - + Enable asynchronous shader compilation Aktiviere asynchrones Shader-Kompilieren - + May reduce shader stutter. - Reduziert vielleicht Shader stottern. + Kann Schattierer-Ruckler reduzieren. - + Fast GPU Time Schnelle GPU-Zeit - + Overclocks the emulated GPU to increase dynamic resolution and render distance. Use 256 for maximal performance and 512 for maximal graphics fidelity. - + Übertaktet die emulierte GPU, um die dynamische Auflösung und die Darstellungsentfernung zu erhöhen. +Verwende den Wert 256 für maximale Leistung und 512 für maximale Grafikqualität. - + GPU Unswizzle GPU-Unswizzle - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. - + Beschleunigt die Dekodierung von BCn-3D-Texturen mithilfe von GPU-Rechenleistung. +Deaktiviere diese Option, falls Abstürze oder Grafikfehler auftreten. - + GPU Unswizzle Max Texture Size GPU-Unswizzle max. Texturgröße - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. @@ -963,1257 +1015,1289 @@ Während die GPU für mittelgroße und große Texturen schneller ist, kann die C Passen Sie diesen Wert an, um das Gleichgewicht zwischen GPU-Beschleunigung und CPU-Overhead zu finden. - + GPU Unswizzle Stream Size GPU-Unswizzle-Streamgröße - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. - + Legt die maximale Menge an Texturdaten (in MiB) fest, die pro Bild verarbeitet wird. +Höhere Werte können Ruckeln beim Laden von Texturen verringern, können sich jedoch auf die Bildrate auswirken. - + GPU Unswizzle Chunk Size GPU-Unswizzle Chunk-Größe - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. - + Legt die Anzahl der Tiefenschnitte fest, die in einem einzelnen Durchlauf verarbeitet werden. +Eine Erhöhung dieses Werts kann den Durchsatz auf High-End-GPUs verbessern, bei schwächerer Hardware jedoch zu TDR- oder Treiber-Auszeiten führen. - + Use Vulkan pipeline cache Vulkan-Pipeline-Cache verwenden - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. - + Aktiviert den herstellerspezifischen Rohrleitungs-Cache der GPU. +Diese Option kann die Ladezeit von Schattierern erheblich verkürzen, wenn der Vulkan-Treiber die Rohrleitungs-Cache-Dateien nicht intern speichert. - + Enable Compute Pipelines (Intel Vulkan Only) Aktiviere Compute-Pipelines (Nur Intel Vulkan) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. Wird von einigen Spielen benötigt. -Diese Einstellung ist nur für proprietäre Intel-Treiber und kann bei Aktivierung zu Abstürzen führen. -Bei allen anderen Treibern sind Compute-Pipelines immer aktiviert +Diese Einstellung ist nur bei proprietären Intel-Treibern vorhanden und kann bei Aktivierung zu einem Absturz führen. +Bei allen anderen Treibern sind die Rechen-Rohrleitungen immer aktiviert. - + Enable Reactive Flushing Aktiviere Reactives Flushing - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. Benutzt Reactive-Flushing anstatt Predictive-Flushing, welches akkurateres Speicher-Synchronisieren erlaubt. - + Sync to framerate of video playback Synchronisiere mit Bildrate von Video-Wiedergaben - + Run the game at normal speed during video playback, even when the framerate is unlocked. - Lasse das Spiel in der normalen Geschwindigkeit abspielen, trotz freigeschalteter Bildrate (FPS) + Lasse das Spiel in der normalen Geschwindigkeit abspielen, trotz freigeschalteter Bildrate - + Barrier feedback loops - Barrier-Feedback-Loops + Barrier-Feedback-Schleifen - + Improves rendering of transparency effects in specific games. Verbessert das Rendering von Transparenzeffekten in bestimmten Spielen. - + Enable buffer history - Aktiviere Puffer Verlauf + Puffer-Verlauf aktivieren - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. - + Ermöglicht den Zugriff auf frühere Pufferzustände. +Diese Option kann in manchen Spielen die Darstellungsqualität und die Leistungsstabilität verbessern. - + Fix bloom effects - Behebt Boomeffekte + Bloom-Effekte beheben - + Removes bloom in Burnout. Entfernt Bloom in Burnout. - + Enable Legacy Rescale Pass - + „Legacy Rescale Pass“ aktivieren - + May fix rescale issues in some games by relying on behavior from the previous implementation. Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. - + Behebt möglicherweise Probleme mit der Skalierung in einigen Spielen, indem auf das Verhalten der vorherigen Implementierung zurückgegriffen wird. +Eine Umgehungslösung für das alte Verhalten, die Linienartefakte auf AMD- und Intel-GPUs sowie graues Texturflackern auf Nvidia-GPUs in „Luigi’s Mansion 3“ behebt. - + Extended Dynamic State - - - - - Controls the number of features that can be used in Extended Dynamic State. -Higher states allow for more features and can increase performance, but may cause additional graphical issues. - + Erweiterter dynamischer Zustand - Vertex Input Dynamic State - - - - - Enables vertex input dynamic state feature for better quality and performance. - + Controls the number of features that can be used in Extended Dynamic State. +Higher states allow for more features and can increase performance, but may cause additional graphical issues. + Legt die Anzahl der Funktionen fest, die im erweiterten dynamischen Modus verwendet werden können. +Höhere Stufen ermöglichen mehr Funktionen und können die Leistung steigern, können jedoch zusätzliche grafische Probleme verursachen. - Sample Shading - + Vertex Input Dynamic State + Dynamischer Zustand der Scheitelpunkteingabe + Enables vertex input dynamic state feature for better quality and performance. + Aktiviert die Funktion für den dynamischen Status der Scheitelpunkteingabe, um eine bessere Qualität und Leistung zu erzielen. + + + + Sample Shading + Probe-Schattierung + + + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. - + Ermöglicht es dem Fragment-Schattierer, pro Abtastwert in einem mehrfach abgetasteten Fragment ausgeführt zu werden, anstatt nur einmal pro Fragment. Verbessert die Grafikqualität auf Kosten der Leistung. +Höhere Werte verbessern die Qualität, beeinträchtigen jedoch die Leistung. - + RNG Seed - RNG-Seed + Startwert für den Zufallszahlengenerator - + Controls the seed of the random number generator. Mainly used for speedrunning. - + Steuert den Startwert des Zufallszahlengenerators. +Wird hauptsächlich für Schnelldurchläufe verwendet. - + Device Name Gerätename - + The name of the console. Der Name der Konsole. - + + Homebrew Args + + + + + Command-line arguments passed to homebrew at launch (e.g. -noglsl). + + + + Custom RTC Date: Benutzerdefinierte Echtzeituhrdatum: - + This option allows to change the clock of the console. Can be used to manipulate time in games. Diese Option erlaubt die Änderung der Uhr der Konsole. Kann benutzt werden um Zeit in Spielen zu manipulieren. - + The number of seconds from the current unix time - + Die Anzahl der Sekunden seit dem aktuellen Unix-Zeitstempel - + Language: Sprache: - + This option can be overridden when region setting is auto-select - Diese Einstellung kann überschrieben werden, falls deine Region auf Automatisch eingestellt ist. + Diese Option kann überschrieben werden, wenn die Regionseinstellung auf „Automatische Auswahl“ steht - + Region: Region: - + The region of the console. Die Region der Konsole. - + Time Zone: Zeitzone: - + The time zone of the console. Die Zeitzone der Konsole. - + Sound Output Mode: Tonausgangsmodus: - + Console Mode: Konsolenmodus: - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. - + Legt fest, ob sich die Konsole im Dock- oder Handgerät-Modus befindet. +Je nach dieser Einstellung ändern sich bei Spielen die Auflösung, die Detailstufe und die unterstützten Controller. +Die Einstellung auf „Handgerät“ kann dazu beitragen, die Leistung auf Systemen mit geringerer Leistung zu verbessern. - + Prompt for user profile on boot - Beim Start nach Nutzer fragen + Beim Systemstart nach dem Benutzerprofil fragen - + Useful if multiple people use the same PC. - Nützlich falls mehrere Personen den gleichen Computer benutzen. + Nützlich, wenn mehrere Personen denselben PC nutzen. - + Pause when not in focus - Pausiere falls nicht im Fokus + Pausieren, wenn nicht im Fokus - + Pauses emulation when focusing on other windows. - Pausiere Emulation falls andere Fenster im Fokus/Vordergrund sind. + Pausiert die Emulation, wenn der Fokus auf andere Fenster wechselt. - + Confirm before stopping emulation Vor dem Stoppen der Emulation bestätigen - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. - - - - - Hide mouse on inactivity - Mauszeiger verstecken - - - - Hides the mouse after 2.5s of inactivity. - Den Mauszeiger nach 2,5 Sekunden Inaktivität verstecken. - - - - Disable controller applet - Deaktiviere Controller-Applet - - - - Forcibly disables the use of the controller applet in emulated programs. -When a program attempts to open the controller applet, it is immediately closed. - - - - - Check for updates - Auf Updates überprüfen + Überschreibt die Abfragen zur Bestätigung des Beendens der Emulation. +Wenn diese Option aktiviert ist, werden solche Abfragen umgangen und die Emulation wird direkt beendet. - Whether or not to check for updates upon startup. - Ob nach Updates während des Startens gesucht werden soll. + Hide mouse on inactivity + Mauszeiger verbergen + + + + Hides the mouse after 2.5s of inactivity. + Verbirgt den Mauszeiger nach 2,5 Sekunden Inaktivität. + + + + Disable controller applet + Controller-Applet deaktivieren - Enable Gamemode - GameMode aktivieren + Forcibly disables the use of the controller applet in emulated programs. +When a program attempts to open the controller applet, it is immediately closed. + Deaktiviert die Verwendung des Controller-Applets in emulierten Programmen zwangsweise. +Wenn ein Programm versucht, das Controller-Applet zu öffnen, wird es sofort geschlossen. + Check for updates + Auf Aktualisierungen überprüfen + + + + Whether or not to check for updates upon startup. + Ob nach Aktualisierungen während des Startens gesucht werden soll. + + + + Enable Gamemode + Spielmodus aktivieren + + + Force X11 as Graphics Backend - + X11 als Grafik-Backend erzwingen - + Custom frontend - Benutzerdefinierte Frontend + Benutzerdefiniertes Frontend - + Real applet Echtes Applet - + Never Niemals - + On Load Beim Laden - + Always Immer - + CPU CPU - + GPU GPU - + CPU Asynchronous CPU Asynchron - + Uncompressed (Best quality) Unkomprimiert (Beste Qualität) - + BC1 (Low quality) BC1 (Niedrige Qualität) - + BC3 (Medium quality) BC3 (Mittlere Qualität) - - + + Auto Auto - + 30 FPS - 30 FPS - - - - 60 FPS - 60 FPS - - - - 90 FPS - 90 FPS - - - - 120 FPS - 120 FPS + 30 BpS + 60 FPS + 60 BpS + + + + 90 FPS + 90 BpS + + + + 120 FPS + 120 BpS + + + Conservative Konservativ - + Aggressive Aggressiv - + Vulkan Vulkan - + OpenGL GLSL - + OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) - + OpenGL GLASM (Assembler-Schattierer, nur NVIDIA) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) - + OpenGL SPIR-V (experimentell, nur AMD/Mesa) - + Null Null - + Fast Schnell - + Balanced Ausgeglichen - - + + Accurate Akkurat - - + + Default - Standard + Vorgabe - + Unsafe (fast) Unsicher (schnell) - + Safe (stable) Sicher (stabil) - + Unsafe Unsicher - + Paranoid (disables most optimizations) Paranoid (deaktiviert die meisten Optimierungen) - + Debugging - Debugging + Fehlerbehebung - + Dynarmic Dynarmic - + NCE NCE - + Borderless Windowed Rahmenloses Fenster - + Exclusive Fullscreen Exklusiver Vollbildmodus - + No Video Output Keine Videoausgabe - + CPU Video Decoding CPU Video Dekodierung - + GPU Video Decoding (Default) GPU Video Dekodierung (Standard) - + 0.25X (180p/270p) [EXPERIMENTAL] 0.25X (180p/270p) [EXPERIMENTELL] - + 0.5X (360p/540p) [EXPERIMENTAL] 0,5X (360p/540p) [EXPERIMENTELL] - + 0.75X (540p/810p) [EXPERIMENTAL] 0,75X (540p/810p) [EXPERIMENTELL] - + 1X (720p/1080p) 1X (720p/1080p) - + 1.25X (900p/1350p) [EXPERIMENTAL] 1.25X (900p/1350p) [EXPERIMENTELL] - + 1.5X (1080p/1620p) [EXPERIMENTAL] 1,5X (1080p/1620p) [EXPERIMENTELL] - + 2X (1440p/2160p) 2X (1440p/2160p) - + 3X (2160p/3240p) 3X (2160p/3240p) - + 4X (2880p/4320p) 4X (2880p/4320p) - + 5X (3600p/5400p) 5X (3600p/5400p) - + 6X (4320p/6480p) 6X (4320p/6480p) - + 7X (5040p/7560p) 7X (5040p/7560p) - + 8X (5760p/8640p) 8X (5760p/8640p) - + Nearest Neighbor Nearest-Neighbor - + Bilinear Bilinear - + Bicubic Bikubisch - + Gaussian Gaussian - + Lanczos - + Lanczos - + ScaleForce ScaleForce - - - AMD FidelityFX Super Resolution - - - - - Area - - - - - MMPX - - - - - Zero-Tangent - - - - - B-Spline - - - - - Mitchell - - - Spline-1 - + AMD FidelityFX Super Resolution + AMD FidelityFX Super-Auflösung + + + + Area + Bereich + + + + MMPX + MMPX + + + + Zero-Tangent + Nulltangente - + B-Spline + B-Spline + + + + Mitchell + Mitchell + + + + Spline-1 + Spline-1 + + + + Snapdragon Game Super Resolution + Snapdragon Spiel-Superauflösung + + + + Snapdragon Game Super Resolution EdgeDir + Snapdragon Spiel-Superauflösung EdgeDir + + + + None Keiner - + FXAA FXAA - + SMAA SMAA - + Default (16:9) - Standard (16:9) + Vorgabe (16:9) - + Force 4:3 - Erzwinge 4:3 + 4:3 erzwingen - + Force 21:9 - Erzwinge 21:9 + 21:9 erzwingen - + Force 16:10 - Erzwinge 16:10 + 16:10 erzwingen - + Stretch to Window Auf Fenster anpassen - + Automatic Automatisch - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x - + 32x - + 32x - + 64x - + 64x - + Japanese (日本語) Japanisch (日本語) - + American English Amerikanisches Englisch - + French (français) Französisch (français) - + German (Deutsch) Deutsch (German) - + Italian (italiano) Italienisch (italiano) - + Spanish (español) Spanisch (español) - + Chinese Chinesisch - + Korean (한국어) Koreanisch (한국어) - + Dutch (Nederlands) Niederländisch (Nederlands) - + Portuguese (português) Portugiesisch (português) - + Russian (Русский) Russisch (Русский) - + Taiwanese Taiwanesisch - + British English Britisches Englisch - + Canadian French Kanadisches Französisch - + Latin American Spanish Lateinamerikanisches Spanisch - + Simplified Chinese Vereinfachtes Chinesisch - + Traditional Chinese (正體中文) Traditionelles Chinesisch (正體中文) - + Brazilian Portuguese (português do Brasil) Brasilianisches Portugiesisch (português do Brasil) - + Polish (polska) - + Polnisch (Polen) - + Thai (แบบไทย) - + Thailändisch (Thailändische Version) - - + + Japan Japan - + USA USA - + Europe Europa - + Australia Australien - + China China - + Korea Korea - + Taiwan Taiwan - + Auto (%1) Auto select time zone Automatisch (%1) - + Default (%1) Default time zone Standard (%1) - + CET CET - + CST6CDT CST6CDT - + Cuba Kuba - + EET EET - + Egypt Ägypten - + Eire Eire - + EST EST - + EST5EDT EST5EDT - + GB GB - + GB-Eire GB-Eire - + GMT GMT - + GMT+0 GMT+0 - + GMT-0 GMT-0 - + GMT0 GMT0 - + Greenwich Greenwich - + Hongkong Hongkong - + HST HST - + Iceland Island - + Iran Iran - + Israel Israel - + Jamaica Jamaika - + Kwajalein Kwajalein - + Libya Libyen - + MET MET - + MST MST - + MST7MDT MST7MDT - + Navajo Navajo - + NZ NZ - + NZ-CHAT NZ-CHAT - + Poland Polen - + Portugal Portugal - + PRC PRC - + PST8PDT PST8PDT - + ROC ROC - + ROK ROK - + Singapore Singapur - + Turkey Türkei - + UCT UCT - + Universal Universal - + UTC UTC - + W-SU W-SU - + WET WET - + Zulu Zulu - + Mono Mono - + Stereo Stereo - + Surround Surround - + 4GB DRAM (Default) - 4GB DRAM (Standard) + 4GB DRAM (Vorgabe) - + 6GB DRAM (Unsafe) 6GB DRAM (Unsicher) - + 8GB DRAM 8GB DRAM - + 10GB DRAM (Unsafe) 10GB DRAM (Unsicher) - + 12GB DRAM (Unsafe) 12GB DRAM (Unsicher) - + Docked - Im Dock + Angedockt - + Handheld - Handheld + Handgerät - - + + Off Aus - + Boost (1700MHz) - Boost (1700MHz) + Schub (1700MHz) - + Fast (2000MHz) Schnell (2000MHz) - + Always ask (Default) - Immer fragen (Standard) + Immer fragen (Vorgabe) - + Only if game specifies not to stop Nur wenn ein Spiel vorgibt, nicht zu stoppen - + Never ask Niemals fragen - - + + Medium (256) Mittel (256) - - + + High (512) Hoch (512) - + Very Small (16 MB) Sehr klein (16 MB) - + Small (32 MB) Klein (32 MB) - + Normal (128 MB) Normal (128 MB) - + Large (256 MB) Groß (256 MB) - + Very Large (512 MB) Sehr groß (512 MB) - + Very Low (4 MB) Sehr niedrig (4 MB) - + Low (8 MB) Niedrig (8 MB) - + Normal (16 MB) Normal (16 MB) - + Medium (32 MB) Mittel (32 MB) - + High (64 MB) Hoch (64 MB) - + Very Low (32) Sehr niedrig (32) - + Low (64) Niedrig (64) - + Normal (128) Normal (128) - + Disabled Deaktiviert - - - ExtendedDynamicState 1 - - - - - ExtendedDynamicState 2 - - - - - ExtendedDynamicState 3 - - - - - Tree View - Baum Ansicht - + ExtendedDynamicState 1 + ErweiterterDynamischerZustand 1 + + + + ExtendedDynamicState 2 + ErweiterterDynamischerZustand 2 + + + + ExtendedDynamicState 3 + ErweiterterDynamischerZustand 3 + + + + Tree View + Baumansicht + + + Grid View - Raster Ansicht + Rasteransicht @@ -2231,7 +2315,7 @@ When a program attempts to open the controller applet, it is immediately closed. Applet mode preference - + Einstellung für den Applet-Modus @@ -2258,7 +2342,7 @@ When a program attempts to open the controller applet, it is immediately closed. Camera Image Source: - Kamera Bildquelle: + Bildquelle der Kamera: @@ -2283,7 +2367,7 @@ When a program attempts to open the controller applet, it is immediately closed. Restore Defaults - Standardwerte wiederherstellen + Vorgabewerte wiederherstellen @@ -2311,7 +2395,7 @@ When a program attempts to open the controller applet, it is immediately closed. We recommend setting accuracy to "Auto". - Wir empfehlen die Genauigkeit auf "Auto" zu setzen. + Wir empfehlen, die Genauigkeit auf „Autom.“ einzustellen. @@ -2367,7 +2451,7 @@ When a program attempts to open the controller applet, it is immediately closed. Enable inline page tables - Enable inline page tables + Inline-Seitentabellen aktivieren @@ -2375,13 +2459,13 @@ When a program attempts to open the controller applet, it is immediately closed. <div>This optimization avoids dispatcher lookups by allowing emitted basic blocks to jump directly to other basic blocks if the destination PC is static.</div> - <div>This optimization avoids dispatcher lookups by allowing emitted basic blocks to jump directly to other basic blocks if the destination PC is static.</div> + <div>Diese Optimierung vermeidet Disponent-Nachschlagungen, indem sie es den ausgegebenen Basisblöcken ermöglicht, direkt zu anderen Basisblöcken zu springen, wenn der Ziel-PC statisch ist.</div> Enable block linking - Enable block linking + Blockverknüpfung aktivieren @@ -2389,13 +2473,13 @@ When a program attempts to open the controller applet, it is immediately closed. <div>This optimization avoids dispatcher lookups by keeping track potential return addresses of BL instructions. This approximates what happens with a return stack buffer on a real CPU.</div> - <div>This optimization avoids dispatcher lookups by keeping track potential return addresses of BL instructions. This approximates what happens with a return stack buffer on a real CPU.</div> + <div>Diese Optimierung vermeidet Disponent-Nachschlagungen, indem sie potenzielle Rücksprungadressen von BL-Befehlen nachverfolgt. Dies entspricht in etwa dem, was bei einem Rücksprungstapelpuffer auf einer echten CPU geschieht.</div> Enable return stack buffer - Return stack buffer aktivieren + Rückgabestapelpuffer aktivieren @@ -2403,13 +2487,13 @@ When a program attempts to open the controller applet, it is immediately closed. <div>Enable a two-tiered dispatch system. A faster dispatcher written in assembly has a small MRU cache of jump destinations is used first. If that fails, dispatch falls back to the slower C++ dispatcher.</div> - <div>Enable a two-tiered dispatch system. A faster dispatcher written in assembly has a small MRU cache of jump destinations is used first. If that fails, dispatch falls back to the slower C++ dispatcher.</div> + <div>Ein zweistufiges Dispositionssystem aktivieren. Zunächst wird ein schnellerer, in Assembler geschriebener Dispatcher mit einem kleinen MRU-Cache für Sprungziele verwendet. Wenn dieser versagt, greift das Dispositionssystem auf den langsameren C++-Disponenten zurück.</div> Enable fast dispatcher - Enable fast dispatcher + Schnellen Disponenten aktivieren @@ -2417,13 +2501,13 @@ When a program attempts to open the controller applet, it is immediately closed. <div>Enables an IR optimization that reduces unnecessary accesses to the CPU context structure.</div> - <div>Enables an IR optimization that reduces unnecessary accesses to the CPU context structure.</div> + <div>Aktiviert eine IR-Optimierung, die unnötige Zugriffe auf die CPU-Kontextstruktur reduziert.</div> Enable context elimination - Enable context elimination + Kontexteliminierung aktivieren @@ -2431,13 +2515,13 @@ When a program attempts to open the controller applet, it is immediately closed. <div>Enables IR optimizations that involve constant propagation.</div> - <div>Enables IR optimizations that involve constant propagation.</div> + <div>Aktiviert IR-Optimierungen, die die Konstantenausbreitung beinhalten.</div> Enable constant propagation - Enable constant propagation + Konstanten-Propagation aktivieren @@ -2445,13 +2529,13 @@ When a program attempts to open the controller applet, it is immediately closed. <div>Enables miscellaneous IR optimizations.</div> - <div>Enables miscellaneous IR optimizations.</div> + <div>Aktiviert verschiedene IR-Optimierungen.</div> Enable miscellaneous optimizations - Enable miscellaneous optimizations + Verschiedene Optimierungen aktivieren @@ -2460,14 +2544,14 @@ When a program attempts to open the controller applet, it is immediately closed. <div style="white-space: nowrap">When disabled, a misalignment is triggered on all misaligned accesses.</div> - <div style="white-space: nowrap">When enabled, a misalignment is only triggered when an access crosses a page boundary.</div> - <div style="white-space: nowrap">When disabled, a misalignment is triggered on all misaligned accesses.</div> + <div style="white-space: nowrap">Bei Aktivierung wird eine Fehlausrichtung nur ausgelöst, wenn ein Zugriff eine Seitengrenze überschreitet.</div><\div> + <div style="white-space: nowrap">Bei Deaktivierung wird eine Fehlausrichtung bei allen fehlausgerichteten Zugriffen ausgelöst.</div> Enable misalignment check reduction - Enable misalignment check reduction + Reduzierung der Fehlausrichtungsprüfung aktivieren @@ -2477,13 +2561,15 @@ When a program attempts to open the controller applet, it is immediately closed. <div style="white-space: nowrap">Disabling this forces all memory accesses to use Software MMU Emulation.</div> - <div style="white-space: nowrap">Diese Optimierung verschnellert Zugriff auf den Speicher durch ein Gast programm.</div> - <div style="white-space: nowrap"> + <div style="white-space: nowrap">Diese Optimierung beschleunigt die Speicherzugriffe durch das Gastprogramm.</div> + <div style="white-space: nowrap">Wenn diese Option aktiviert ist, erfolgen Lese- und Schreibvorgänge im Gastspeicher direkt im Speicher und nutzen die MMU des Hosts.</div> + <div style="white-space: nowrap">Wenn Sie diese Option deaktivieren, werden alle Speicherzugriffe zwingend über die Software-MMU-Emulation abgewickelt.</div> + Enable Host MMU Emulation (general memory instructions) - Aktiviert Host MMU Emulation (Generale Speicher Anweisung) + Host-MMU-Emulation aktivieren (allgemeine Speicherinstruktionen) @@ -2493,15 +2579,15 @@ When a program attempts to open the controller applet, it is immediately closed. <div style="white-space: nowrap">Disabling this forces all exclusive memory accesses to use Software MMU Emulation.</div> -<div style="white-space: nowrap"> Diese Optimierung beschleunigt exklusive Speicherzugriffe durch das Gastprogramm.</div> -<div style="white-space: nowrap"> Die Aktivierung führt dazu, dass gastexklusive Speicherlese- und -schreibvorgänge direkt im Speicher erfolgen und die MMU des Hosts verwendet wird.</div> -<div style="white-space: nowrap"> Die Deaktivierung dieser Funktion zwingt alle exklusiven Speicherzugriffe zur Verwendung der Software-MMU-Emulation.</div> - + <div style="white-space: nowrap">Diese Optimierung beschleunigt exklusive Speicherzugriffe durch das Gastprogramm.</div> + <div style="white-space: nowrap">Wenn diese Option aktiviert ist, werden exklusive Lese- und Schreibzugriffe des Gastes direkt im Speicher ausgeführt und nutzen die MMU des Hosts.</div> + <div style="white-space: nowrap">Durch Deaktivieren dieser Option werden alle exklusiven Speicherzugriffe gezwungen, die Software-MMU-Emulation zu verwenden.</div> + Enable Host MMU Emulation (exclusive memory instructions) - Aktiviere Host MMU Emulation (exlusive memory instructions). + Host-MMU-Emulation aktivieren (exklusive Speicherinstruktionen) @@ -2510,14 +2596,14 @@ When a program attempts to open the controller applet, it is immediately closed. <div style="white-space: nowrap">Enabling it reduces the overhead of fastmem failure of exclusive memory accesses.</div> -<div style="white-space: nowrap"> Diese Optimierung beschleunigt exklusive Speicherzugriffe durch das Gastprogramm.</div> -<div style="white-space: nowrap"> Durch die Aktivierung wird der Overhead von Fastmem-Fehlern bei exklusiven Speicherzugriffen reduziert.</div> - + <div style="white-space: nowrap">Diese Optimierung beschleunigt exklusive Speicherzugriffe durch das Gastprogramm.</div> + <div style="white-space: nowrap">Durch ihre Aktivierung wird der Overhead bei Fastmem-Fehlern bei exklusiven Speicherzugriffen reduziert.</div> + Enable recompilation of exclusive memory instructions - Neukompilierung von Anweisungen mit exklusivem Speicher aktivieren + Neukompilierung exklusiver Speicherinstruktionen aktivieren @@ -2526,14 +2612,14 @@ When a program attempts to open the controller applet, it is immediately closed. <div style="white-space: nowrap">Enabling it reduces the overhead of all memory accesses and has no impact on programs that don't access invalid memory.</div> -<div style="white-space: nowrap"> Diese Optimierung beschleunigt die Speicherzugriffe, indem sie ungültige Speicherzugriffe zulässt.</div> -<div style="white-space: nowrap"> Die Aktivierung reduziert den Overhead aller Speicherzugriffe und hat keine Auswirkungen auf Programme, die nicht auf ungültigen Speicher zugreifen.</div> - + <div style="white-space: nowrap">Diese Optimierung beschleunigt Speicherzugriffe, indem sie ungültige Speicherzugriffe zulässt.</div> + <div style="white-space: nowrap">Die Aktivierung dieser Option verringert den Overhead aller Speicherzugriffe und hat keine Auswirkungen auf Programme, die nicht auf ungültigen Speicher zugreifen.</div> + Enable fallbacks for invalid memory accesses - Fallbacks für ungültige Speicherzugriffe einschalten + Rückfälle für ungültige Speicherzugriffe aktivieren @@ -2546,7 +2632,7 @@ When a program attempts to open the controller applet, it is immediately closed. Debugger - Debugger + Fehlerbeheber @@ -2561,32 +2647,32 @@ When a program attempts to open the controller applet, it is immediately closed. Logging - Logging + Protokollierung Global Log Filter - Globaler Log-Filter + Globaler Protokoll-Filter When checked, the max size of the log increases from 100 MB to 1 GB - Wenn diese Option aktiviert ist, erhöht sich die maximale Größe des Logs von 100 MB auf 1 GB + Wenn diese Option aktiviert ist, erhöht sich die maximale Größe des Protokolls von 100 MB auf 1 GB Enable Extended Logging** - Erweitertes Logging aktivieren** + Erweiterte Protokollierung aktivieren** Show Log in Console - Log in der Konsole zeigen + Protokoll in der Konsole zeigen Open Log Location - Log-Verzeichnis öffnen + Protokoll-Verzeichnis öffnen @@ -2596,7 +2682,7 @@ When a program attempts to open the controller applet, it is immediately closed. Arguments String - String-Argumente + Argument-Zeichenkette @@ -2606,12 +2692,12 @@ When a program attempts to open the controller applet, it is immediately closed. When checked, it executes shaders without loop logic changes - Wenn diese Option aktiviert ist, werden Shader ohne Änderungen der looplogik ausgeführt. + Wenn aktiviert, führt es Schattierer ohne Änderungen an der Schleifenlogik aus Disable Loop safety checks - Loop-safety-checks deaktivieren + Sicherheitsprüfungen für Schleifen deaktivieren @@ -2621,12 +2707,12 @@ When a program attempts to open the controller applet, it is immediately closed. Dump Maxwell Macros - Maxwell-Macros dumpen + Maxwell-Macros auslesen When checked, it enables Nsight Aftermath crash dumps - Wenn diese Option aktiviert ist, werden Nsight Aftermath-Crash-Dumps zugelassen. + Wenn aktiviert, werden Nsight.-Nachwirkungsabsturz-Speicherauszüge aktiviert @@ -2641,17 +2727,17 @@ When a program attempts to open the controller applet, it is immediately closed. Dump Game Shaders - Spiele-Shader dumpen + Spiele-Schattierer auslesen Enable Renderdoc Hotkey - Renderdoc-Hotkey aktivieren + Renderdoc-Tastenkürzel aktivieren When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower - Diese Option deaktiviert den Macro-JIT-Compiler. Dies wird die Geschwindigkeit verringern. + Wenn aktiviert, wird der Gerade-noch-rechtzeitig-Kompilierer (JIT) für Makros deaktiviert. Dies führt dazu, dass Spiele langsamer laufen. @@ -2661,7 +2747,7 @@ When a program attempts to open the controller applet, it is immediately closed. When checked, it disables the macro HLE functions. Enabling this makes games run slower - When checked, it disables the macro HLE functions. Enabling this makes games run slower + Wenn aktiviert, werden die HLE-Funktionen des Makros deaktiviert. Durch die Aktivierung dieser Option laufen Spiele langsamer. @@ -2671,22 +2757,22 @@ When a program attempts to open the controller applet, it is immediately closed. When checked, the graphics API enters a slower debugging mode - Wenn diese Option aktiviert ist, wechselt die Grafik-API in einen langsameren Debug-Modus + Wenn diese Option aktiviert ist, wechselt die Grafik-API in einen langsameren Fehlerbehebungs-Modus Enable Graphics Debugging - Grafik-Debugging aktivieren + Grafik-Fehlerbehebung aktivieren When checked, yuzu will log statistics about the compiled pipeline cache - Wenn ausgewählt wird yuzu Log Statistiken über den kompilierte Pipeline Chache sammeln. + Wenn aktiviert, protokolliert Yuzu Statistiken zum kompilierten Rohrleitungs-Cache Enable Shader Feedback - Shader-Feedback aktivieren + Schattierer-Rückmeldung aktivieren @@ -2696,7 +2782,7 @@ When a program attempts to open the controller applet, it is immediately closed. Disable Buffer Reorder - Deaktiviere Buffer-Reorder + Puffer-Neuanordnung deaktivieren @@ -2716,17 +2802,17 @@ When a program attempts to open the controller applet, it is immediately closed. Disable Web Applet - Deaktiviere die Web Applikation + Web-Applet deaktivieren Enable All Controller Types - Aktiviere alle Arten von Controllern + Alle Controller-Typen aktivieren Enable Auto-Stub - + „Auto-Stub“ aktivieren @@ -2736,87 +2822,87 @@ When a program attempts to open the controller applet, it is immediately closed. Use dev.keys - + dev.keys verwenden Enable Debug Asserts - aktiviere Debug-Meldungen + Fehlerbehebungs-Meldungen aktivieren Debugging - Debugging + Fehlerbehebung Battery Serial: - + Batterie-Seriennummer: Bitmask for quick development toggles - + Bitmaske für schnelle Entwicklungs-Umschalter Set debug knobs (bitmask) - + Fehlerbehebungs-Regler einstellen (Bitmaske) 16-bit debug knob set for quick development toggles - + 16-Bit-Fehlerbehebungs-Regler für schnelle Entwicklungs-Umschalter (bitmask) - + (Bitmaske) Debug Knobs: - + Fehlerbehebungs-Regler: Unit Serial: - + Seriennummer des Geräts: Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. - Aktivieren Sie diese Option, um den zuletzt generierten Audio-Log auf der Konsole auszugeben. Betrifft nur Spiele, die den Audio-Renderer verwenden. + Aktiviere diese Option, um das zuletzt generierte Audio-Protokoll auf der Konsole auszugeben. Betrifft nur Spiele, die den Audio-Renderer verwenden. Dump Audio Commands To Console** - Audio-Befehle auf die Konsole als Dump abspeichern** + Audiobefehle in die Konsole als Speicherauszug ausgeben** Flush log output on each line - + Protokollausgabe nach jeder Zeile leeren Enable FS Access Log - FS-Zugriffslog aktivieren + FS-Zugriffsprotokoll aktivieren Enable Verbose Reporting Services** - Ausführliche Berichtsdienste aktivieren** + Ausführliche Berichtsdienste aktivieren** Censor username in logs - + Benutzernamen in Protokollen zensieren **This will be reset automatically when Eden closes. - + **Dies wird automatisch zurückgesetzt, wenn Eden geschlossen wird. @@ -2824,7 +2910,7 @@ When a program attempts to open the controller applet, it is immediately closed. Configure Debug Controller - Debug-Controller einrichten + Fehlerbehebungs-Controller konfigurieren @@ -2834,7 +2920,7 @@ When a program attempts to open the controller applet, it is immediately closed. Defaults - Standardwerte + Vorgabewerte @@ -2848,7 +2934,7 @@ When a program attempts to open the controller applet, it is immediately closed. Debug - Debug + Fehlerbehebung @@ -2861,7 +2947,7 @@ When a program attempts to open the controller applet, it is immediately closed. Eden Configuration - Eden Konfiguration + Eden-Konfiguration @@ -2888,7 +2974,7 @@ When a program attempts to open the controller applet, it is immediately closed. Debug - Debug + Fehlerbehebung @@ -2910,7 +2996,7 @@ When a program attempts to open the controller applet, it is immediately closed. GraphicsAdvanced - GraphicsAdvanced + GraphikFortgeschritten @@ -2920,7 +3006,7 @@ When a program attempts to open the controller applet, it is immediately closed. Hotkeys - Hotkeys + Tastenkürzel @@ -3000,7 +3086,7 @@ When a program attempts to open the controller applet, it is immediately closed. Gamecard - Gamecard + Spielkarte @@ -3020,17 +3106,17 @@ When a program attempts to open the controller applet, it is immediately closed. Patch Manager - Patchmanager + Patch-Verwalter Dump Decompressed NSOs - Dekomprimierte NSOs dumpen + Dekomprimierte NSOs auslesen Dump ExeFS - ExeFS dumpen + ExeFS auslesen @@ -3040,17 +3126,17 @@ When a program attempts to open the controller applet, it is immediately closed. Dump Root - Root dumpen + Root auslesen Caching - Caching + Zwischenspeicherung Cache Game List Metadata - Metadaten der Spieleliste cachen + Metadaten der Spieleliste zwischenspeichern @@ -3076,7 +3162,7 @@ When a program attempts to open the controller applet, it is immediately closed. Select Gamecard Path... - Gamecard-Pfad auswählen... + Spielkarten-Pfad auswählen... @@ -3096,17 +3182,17 @@ When a program attempts to open the controller applet, it is immediately closed. Choose an action for the save data directory: - + Wähle eine Aktion für das Speicherdatenverzeichnis: Set Custom Path - Lege einen Benutzerdefinierter Pfad fest + Benutzerdefinierten Pfad festlegen Reset to NAND - + Auf NAND zurücksetzen @@ -3117,7 +3203,13 @@ New: %2 Would you like to migrate saves from the old location? WARNING: This will overwrite any conflicting saves in the new location! - + Speicherdaten sind sowohl am alten als auch am neuen Speicherort vorhanden. + +Alt: %1 +Neu: %2 + +Möchtest du die Speicherdaten vom alten Speicherort übertragen? +WARNUNG: Dadurch werden alle Speicherdaten am neuen Speicherort überschrieben, die mit den vorhandenen Daten in Konflikt stehen! @@ -3125,7 +3217,10 @@ WARNING: This will overwrite any conflicting saves in the new location! From: %1 To: %2 - + Möchtest du deine Speicherdaten an den neuen Speicherort übertragen? + +Von: %1 +Nach: %2 @@ -3151,7 +3246,7 @@ To: %2 Failed to create destination directory. - + Zielverzeichnis konnte nicht erstellt werden. @@ -3170,7 +3265,9 @@ To: %2 Save data has been migrated successfully. Would you like to delete the old save data? - + Die Speicherdaten wurden erfolgreich übertragen. + +Möchtest du die alten Speicherdaten löschen? @@ -3194,7 +3291,7 @@ Would you like to delete the old save data? Add directories to scan for DLCs and Updates without installing to NAND - + Verzeichnisse hinzufügen, in denen nach DLCs und Aktualisierungen gesucht werden soll, ohne diese auf dem NAND zu installieren @@ -3214,7 +3311,7 @@ Would you like to delete the old save data? Eden - + Eden @@ -3224,7 +3321,7 @@ Would you like to delete the old save data? Select External Content Directory... - + Externes Inhaltsverzeichnis auswählen... @@ -3234,7 +3331,7 @@ Would you like to delete the old save data? This directory is already in the list. - + Dieses Verzeichnis ist bereits in der Liste enthalten. @@ -3265,33 +3362,33 @@ Would you like to delete the old save data? Hintergrundfarbe: - + % - FSR sharpening percentage (e.g. 50%) + FSR/SGSR sharpening percentage (e.g. 50%) % - + Off Aus - + VSync Off Vsync Aus - + Recommended Empfohlen - + On An - + VSync On Vsync An @@ -3311,7 +3408,7 @@ Would you like to delete the old save data? Advanced Graphics Settings - Erweiterte Grafik-Einstellungen + Erweiterte Grafikeinstellungen @@ -3334,12 +3431,12 @@ Would you like to delete the old save data? Changing these options from their default may cause issues. Novitii cavete! - + Das Ändern dieser Vorgabe-Einstellungen kann zu Problemen führen. Anfänger seien gewarnt! Vulkan Extensions - Vulkan Erweiterungen + Vulkan-Erweiterungen @@ -3350,7 +3447,7 @@ Would you like to delete the old save data? Extended Dynamic State is disabled on macOS due to MoltenVK compatibility issues that cause black screens. - + „Erweiterter Dynamischer Zustand“ ist unter macOS deaktiviert, da es aufgrund von Kompatibilitätsproblemen mit MoltenVK zu schwarzen Bildschirmen kommt. @@ -3358,12 +3455,12 @@ Would you like to delete the old save data? Hotkey Settings - Hotkey-Einstellungen + Tastenkürzel-Einstellungen Hotkeys - Hotkeys + Tastenkürzel @@ -3378,7 +3475,7 @@ Would you like to delete the old save data? Restore Defaults - Standardwerte wiederherstellen + Vorgabewerte wiederherstellen @@ -3388,12 +3485,12 @@ Would you like to delete the old save data? Hotkey - Hotkey + Tastenkürzel Controller Hotkey - Controller-Hotkey + Controller-Tastenkürzel @@ -3406,7 +3503,7 @@ Would you like to delete the old save data? The entered key sequence is already assigned to: %1 - Die eingegebene Sequenz ist bereits vergeben an: %1 + Die eingegebene Sequenz ist bereits zugewiesen zu: %1 @@ -3421,7 +3518,7 @@ Would you like to delete the old save data? Invalid hotkey settings - Ungültige Hotkey-Einstellungen + Ungültige Tastenkürzel-Einstellungen @@ -3431,7 +3528,7 @@ Would you like to delete the old save data? Restore Default - Standardwerte wiederherstellen + Vorgabewerte wiederherstellen @@ -3446,12 +3543,12 @@ Would you like to delete the old save data? The default button sequence is already assigned to: %1 - Die Standard Tastenfolge ist bereits belegt von: %1 + Die Vorgabe-Tastenfolge ist bereits zugewiesen zu: %1 The default key sequence is already assigned to: %1 - Die Standard-Sequenz ist bereits vergeben an: %1 + Die Vorgabe-Sequenz ist bereits zugewiesen zu: %1 @@ -3528,7 +3625,7 @@ Would you like to delete the old save data? Handheld - Handheld + Handgerät @@ -3599,7 +3696,7 @@ Would you like to delete the old save data? Defaults - Standardwerte + Vorgabewerte @@ -3617,7 +3714,7 @@ Would you like to delete the old save data? Joycon Colors - Joyconfarben + Joycon-Farben @@ -3725,7 +3822,7 @@ Would you like to delete the old save data? Touchscreen - Touchscreen + Berührungsbildschirm @@ -3770,12 +3867,12 @@ Would you like to delete the old save data? Requires restarting Eden - Erfordert einen Neustart Edens + Erfordert einen Neustart von Eden Enable XInput 8 player support (disables web applet) - Unterstützung für XInput 8-Player aktivieren (deaktiviert das Web-Applet) + Unterstützung für XInput-8-Spieler aktivieren (deaktiviert das Web-Applet) @@ -3790,12 +3887,12 @@ Would you like to delete the old save data? Enable direct JoyCon driver - Aktiviere direkten JoyCon-Treiber + Direkten JoyCon-Treiber aktivieren Enable direct Pro Controller driver [EXPERIMENTAL] - Aktiviere direkten Pro Controller-Treiber [EXPERIMENTELL] + Direkten Pro-Controller-Treiber aktivieren [EXPERIMENTELL] @@ -3810,7 +3907,7 @@ Would you like to delete the old save data? Motion / Touch - Bewegung / Touch + Bewegung / Berührung @@ -3828,57 +3925,57 @@ Would you like to delete the old save data? Input Profiles - Eingabe-Profile + Eingabeprofile Player 1 Profile - Profil Spieler 1 + Profil von Spieler 1 Player 2 Profile - Profil Spieler 2 + Profil von Spieler 2 Player 3 Profile - Profil Spieler 3 + Profil von Spieler 3 Player 4 Profile - Profil Spieler 4 + Profil von Spieler 4 Player 5 Profile - Profil Spieler 5 + Profil von Spieler 5 Player 6 Profile - Profil Spieler 6 + Profil von Spieler 6 Player 7 Profile - Profil Spieler 7 + Profil von Spieler 7 Player 8 Profile - Profil Spieler 8 + Profil von Spieler 8 Use global input configuration - Verwende globale Eingabe-Konfiguration + Globale Eingabekonfiguration verwenden Player %1 profile - Profil Spieler %1 + Profil von Spieler %1 @@ -3998,7 +4095,7 @@ Would you like to delete the old save data? Deadzone: 0% - Deadzone: 0% + Totzone: 0 % @@ -4089,7 +4186,7 @@ Would you like to delete the old save data? Home - Home + Heim @@ -4258,7 +4355,7 @@ Um die Achsen umzukehren, bewege den Joystick zuerst vertikal und dann horizonta Handheld - Handheld + Handgerät @@ -4392,7 +4489,7 @@ Um die Achsen umzukehren, bewege den Joystick zuerst vertikal und dann horizonta Defaults - Standardwerte + Vorgabewerte @@ -4400,12 +4497,12 @@ Um die Achsen umzukehren, bewege den Joystick zuerst vertikal und dann horizonta Configure Motion / Touch - Bewegung / Touch einrichten + Bewegung / Berührung einrichten Touch - Touch + Berührung @@ -4420,7 +4517,7 @@ Um die Achsen umzukehren, bewege den Joystick zuerst vertikal und dann horizonta - + Configure Einrichtung @@ -4432,7 +4529,7 @@ Um die Achsen umzukehren, bewege den Joystick zuerst vertikal und dann horizonta CemuhookUDP Config - CemuhookUDP Konfiguration + CemuhookUDP-Konfiguration @@ -4451,7 +4548,7 @@ Um die Achsen umzukehren, bewege den Joystick zuerst vertikal und dann horizonta - + Test Testen @@ -4466,77 +4563,77 @@ Um die Achsen umzukehren, bewege den Joystick zuerst vertikal und dann horizonta Server löschen - + %1:%2 %1:%2 - - - - - - + + + + + + Eden - + Eden - + Port number has invalid characters Port-Nummer hat ungültige Zeichen - + Port has to be in range 0 and 65353 Port muss zwischen 0 und 65353 liegen - + IP address is not valid IP Adresse ist ungültig - + This UDP server already exists Dieser UDP-Server existiert bereits - + Unable to add more than 8 servers Es können nicht mehr als 8 Server hinzugefügt werden - + Testing Testen - + Configuring Einrichten - + Test Successful Test erfolgreich - + Successfully received data from the server. Daten wurden erfolgreich vom Server empfangen. - + Test Failed Test fehlgeschlagen - + Could not receive valid data from the server.<br>Please verify that the server is set up correctly and the address and port are correct. Konnte keine Daten vom Server empfangen.<br>Prüfe bitte, dass der Server korrekt eingerichtet wurde und dass Adresse und Port korrekt sind. - + UDP Test or calibration configuration is in progress.<br>Please wait for them to finish. UDP-Test oder Kalibration wird gerade durchgeführt.<br>Bitte warte einen Moment. @@ -4546,12 +4643,12 @@ Um die Achsen umzukehren, bewege den Joystick zuerst vertikal und dann horizonta Configure mouse panning - Mausschwenk konfigurieren + Mausschwenken konfigurieren Enable mouse panning - Maus-Panning aktivieren + Mausschwenken aktivieren @@ -4585,7 +4682,7 @@ Um die Achsen umzukehren, bewege den Joystick zuerst vertikal und dann horizonta Deadzone counterweight - Deadzone-Gegengewicht + Totzonen-Gegengewicht @@ -4595,12 +4692,12 @@ Um die Achsen umzukehren, bewege den Joystick zuerst vertikal und dann horizonta Deadzone - Deadzone + Totzone Stick decay - Stick-Abklingzeit + Stick-Verfall @@ -4615,7 +4712,7 @@ Um die Achsen umzukehren, bewege den Joystick zuerst vertikal und dann horizonta Default - Standard + Vorgabe @@ -4688,7 +4785,7 @@ Aktuell liegen die Werte bei %1% bzw. %2%. Title ID - Titel ID + Titel-ID @@ -4721,57 +4818,57 @@ Aktuell liegen die Werte bei %1% bzw. %2%. Einige Einstellungen sind nur verfügbar, wenn kein Spiel aktiv ist. - + Add-Ons Add-Ons - + System System - + CPU CPU - + Graphics Grafik - + Adv. Graphics Erw. Grafik - + Ext. Graphics - + Ext. Grafiken - + Audio Audio - + Input Profiles Eingabe-Profile - + Network Netzwerk - + Applets - + Applets - + Properties Einstellungen @@ -4840,60 +4937,63 @@ Kontrolliere die Logs für Details. Zipped Mod Location - + Speicherort der gezippten Mod Zipped Archives (*.zip) - + ZIP-Archive (*.zip) Invalid Selection - + Ungültige Auswahl Only mods, cheats, and patches can be deleted. To delete NAND-installed updates, right-click the game in the game list and click Remove -> Remove Installed Update. - + Es können nur Mods, Cheats und Patches gelöscht werden. +Um auf dem NAND installierte Aktualisierungen zu löschen, klicke mit der rechten Maustaste auf das Spiel in der Spieleliste und wähle „Entfernen“ -> „Installierte Aktualisierung entfernen“. You are about to delete the following installed mods: - + Du bist dabei, die folgenden installierten Mods zu löschen: + Once deleted, these can NOT be recovered. Are you 100% sure you want to delete them? - + +Einmal gelöscht, können diese nicht wiederhergestellt werden. Bist du dir zu 100 % sicher, dass du sie löschen möchtest? Delete add-on(s)? - + Add-ons löschen? Successfully deleted - + Erfolgreich gelöscht Successfully deleted all selected mods. - + Alle ausgewählten Mods wurden erfolgreich gelöscht. &Delete - + &Löschen &Open in File Manager - + &Im Dateimanager öffnen @@ -4954,7 +5054,7 @@ Once deleted, these can NOT be recovered. Are you 100% sure you want to delete t Error occurred attempting to overwrite previous image at: %1. - Fehler beim Überschreiben des vorherigen Bildes bei: %1 + Beim Versuch, das vorherige Bild unter %1 zu überschreiben, ist ein Fehler aufgetreten. @@ -5054,7 +5154,7 @@ UUID: %2 Deadzone: 0% - Deadzone: 0% + Totzone: 0% @@ -5070,12 +5170,12 @@ UUID: %2 Enable - Aktiviere + Aktivieren Ring Sensor Value - Ringsensor-Wert + Ringsensor-Wert @@ -5086,7 +5186,7 @@ UUID: %2 Restore Defaults - Standardwerte wiederherstellen + Vorgabewerte wiederherstellen @@ -5112,7 +5212,7 @@ UUID: %2 Error enabling ring input - Fehler beim Aktivieren des Ring-Inputs + Fehler beim Aktivieren des Ring-Eingangs @@ -5171,7 +5271,7 @@ UUID: %2 Warning: "%1" is not a valid language for region "%2" - Achtung: "%1" ist keine valide Sprache für die Region "%2" + WARNUNG: "%1" ist keine valide Sprache für die Region "%2" @@ -5184,17 +5284,17 @@ UUID: %2 <html><head/><body><p>Reads controller input from scripts in the same format as TAS-nx scripts.<br/>For a more detailed explanation, please consult the user handbook.</p></body></html> - + <html><head/><body><p>Liest Controller-Eingaben aus Skripten im gleichen Format wie TAS-nx-Skripte ein.<br/>Eine ausführlichere Erläuterung findest du im Benutzerhandbuch.</p></body></html> To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). - Um zu überprüfen, welche Hotkeys die Wiedergabe/Aufnahme steuern, sehen Sie bitte in den Hotkey-Einstellungen nach (Konfigurieren -> Allgemein -> Hotkeys). + Um zu überprüfen, welche Tastenkürzel die Wiedergabe/Aufnahme steuern, sieh bitte in den Tastenkürzel-Einstellungen nach (Konfigurieren -> Allgemein -> Tastenkürzel). WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method. - ACHTUNG: Dies ist ein experimentes Feature.<br/>Es wird scripts nicht perfekt mit der momentanen, unperfekten Synchronisationsmethode abspielen. + WARNUNG: Dies ist eine experimentelle Funktion.<br/>Mit der derzeitigen, unvollkommenen Synchronisationsmethode werden Skripte nicht frame-genau wiedergegeben. @@ -5209,7 +5309,7 @@ UUID: %2 Loop script - Script loopen + Schleifen-Skript @@ -5219,12 +5319,12 @@ UUID: %2 Show recording dialog - + Aufnahmedialog anzeigen Script Directory - Skript-Verzeichnis + Skriptverzeichnis @@ -5247,7 +5347,7 @@ UUID: %2 Select TAS Load Directory... - TAS-Lade-Verzeichnis auswählen... + TAS-Ladeverzeichnis auswählen... @@ -5255,7 +5355,7 @@ UUID: %2 Configure Touchscreen Mappings - Touchscreen-Belegung einrichten + Berührungsbildschirm-Belegung konfigurieren @@ -5281,8 +5381,8 @@ UUID: %2 Click the bottom area to add a point, then press a button to bind. Drag points to change position, or double-click table cells to edit values. - Klicke das untere Feld um einen Punkt hinzuzufügen und drücke dann eine Taste, die diesen Punkt auslösen soll. -Ziehe die Punkte mit deiner Maus, um ihre Position zu ändern. Doppelklicke auf die Tabelle, um die Belegung des Punktes zu ändern. + Klicke auf den unteren Bereich, um einen Punkt hinzuzufügen, und drücke dann eine Taste, um die Zuordnung festzulegen. +Ziehe die Punkte, um ihre Position zu ändern, oder doppelklicke auf Tabellenzellen, um die Werte zu bearbeiten. @@ -5347,12 +5447,12 @@ Ziehe die Punkte mit deiner Maus, um ihre Position zu ändern. Doppelklicke auf Configure Touchscreen - Touchscreen einrichten: + Berührungsbildschirm konfigurieren Warning: The settings in this page affect the inner workings of Eden's emulated touchscreen. Changing them may result in undesirable behavior, such as the touchscreen partially or not working. You should only use this page if you know what you are doing. - + Warnung: Die Einstellungen auf dieser Seite beeinflussen die interne Funktionsweise des emulierten Berührungsbildschirms von Eden. Änderungen daran können zu unerwünschtem Verhalten führen – beispielsweise dazu, dass der Berührungsbildschirm nur teilweise oder gar nicht mehr funktioniert. Du solltest diese Seite nur verwenden, wenn du genau weist, was du tust. @@ -5454,7 +5554,7 @@ Ziehe die Punkte mit deiner Maus, um ihre Position zu ändern. Doppelklicke auf Theme: - Theme: + Thema: @@ -5504,7 +5604,7 @@ Ziehe die Punkte mit deiner Maus, um ihre Position zu ändern. Doppelklicke auf Screenshots - Screenshots + Bildschirmfotos @@ -5514,7 +5614,7 @@ Ziehe die Punkte mit deiner Maus, um ihre Position zu ändern. Doppelklicke auf Screenshots Path: - Screenshotpfad + Bildschirmfotos-Pfad: @@ -5524,7 +5624,7 @@ Ziehe die Punkte mit deiner Maus, um ihre Position zu ändern. Doppelklicke auf TextLabel - TextLabel + Textetikett @@ -5534,7 +5634,7 @@ Ziehe die Punkte mit deiner Maus, um ihre Position zu ändern. Doppelklicke auf Select Screenshots Path... - Screenshotpfad auswählen... + Bildschirmfotos-Pfad auswählen... @@ -5558,12 +5658,12 @@ Ziehe die Punkte mit deiner Maus, um ihre Position zu ändern. Doppelklicke auf Configure Vibration - Stelle die Vibration ein + Vibration konfigurieren Press any controller button to vibrate the controller. - Drücke einen Knopf um den Controller vibrieren zu lassen. + Drücke einen Knopf, um den Controller vibrieren zu lassen. @@ -5648,7 +5748,7 @@ Ziehe die Punkte mit deiner Maus, um ihre Position zu ändern. Doppelklicke auf Eden Web Service - + Eden-Web-Dienst @@ -5728,17 +5828,17 @@ Ziehe die Punkte mit deiner Maus, um ihre Position zu ändern. Doppelklicke auf Shaders - + Schattierer User NAND - + Nutzer-NAND System NAND - + System-NAND @@ -5761,30 +5861,30 @@ Ziehe die Punkte mit deiner Maus, um ihre Position zu ändern. Doppelklicke auf Tooltip - + Kurzinfo Open with your system file manager - Mit deinem System eigenen Dateimanager öffnen + Mit deinem System-Dateiverwalter öffnen Delete all data in this directory. THIS IS 100% IRREVERSABLE! - + Alle Daten in diesem Verzeichnis löschen. DIES IST ZU 100 % UNUMKEHRBAR! Export all data in this directory. This may take a while! - + Alle Daten in diesem Verzeichnis exportieren. Das kann eine Weile dauern! Import data for this directory. This may take a while, and will delete ALL EXISTING DATA! - + Daten für dieses Verzeichnis importieren. Dies kann eine Weile dauern und löscht ALLE BESTEHENDEN DATEN! - + Calculating... Berechnen... @@ -5827,12 +5927,12 @@ Ziehe die Punkte mit deiner Maus, um ihre Position zu ändern. Doppelklicke auf Server Address - Serveradresse + Server-Adresse <html><head/><body><p>Server address of the host</p></body></html> - <html><head/><body><p>Serveradresse des Hosts</p></body></html> + <html><head/><body><p>Server-Adresse des Hosts</p></body></html> @@ -5847,7 +5947,7 @@ Ziehe die Punkte mit deiner Maus, um ihre Position zu ändern. Doppelklicke auf Nickname - Nickname + Spitzname @@ -5883,7 +5983,7 @@ Ziehe die Punkte mit deiner Maus, um ihre Position zu ändern. Doppelklicke auf Room name is not valid. Must be 4 to 20 alphanumeric characters. - Raumname ist ungültig. Muss aus 4 bis 20 alphanumerischen Zeichen bestehen. + Der Raumname ist ungültig. Er muss aus 4 bis 20 alphanumerischen Zeichen bestehen. @@ -5893,7 +5993,7 @@ Ziehe die Punkte mit deiner Maus, um ihre Position zu ändern. Doppelklicke auf IP is not a valid IPv4 address. - + Die IP ist keine gültige IPv4-Adresse. @@ -5903,7 +6003,7 @@ Ziehe die Punkte mit deiner Maus, um ihre Position zu ändern. Doppelklicke auf You must choose a Preferred Game to host a room. If you do not have any games in your game list yet, add a game folder by clicking on the plus icon in the game list. - + Du musst ein bevorzugtes Spiel auswählen, um einen Raum zu erstellen. Wenn du noch keine Spiele in deiner Spieleliste hast, füge einen Spielordner hinzu, indem du auf das Plus-Symbol in der Spieleliste klickst. @@ -5913,7 +6013,7 @@ Ziehe die Punkte mit deiner Maus, um ihre Position zu ändern. Doppelklicke auf Unable to connect to the host. Verify that the connection settings are correct. If you still cannot connect, contact the room host and verify that the host is properly configured with the external port forwarded. - + Es kann keine Verbindung zum Host hergestellt werden. Überprüfe, ob die Verbindungseinstellungen korrekt sind. Wenn die Verbindung weiterhin nicht hergestellt werden kann, wende dich an den Host des Raums und vergewissere dich, dass dieser ordnungsgemäß konfiguriert ist und die Portweiterleitung eingerichtet ist. @@ -5928,12 +6028,12 @@ Ziehe die Punkte mit deiner Maus, um ihre Position zu ändern. Doppelklicke auf The host of the room has banned you. Speak with the host to unban you or try a different room. - + Der Host des Raums hat dich gesperrt. Sprich mit dem Host, um die Sperre aufzuheben, oder versuche es in einem anderen Raum. Version mismatch! Please update to the latest version of Eden. If the problem persists, contact the room host and ask them to update the server. - + Versionskonflikt! Bitte aktualisiere auf die neueste Version von Eden. Sollte das Problem weiterhin bestehen, wende dich bitte an den Raumhost und bitte ihn, den Server zu aktualisieren. @@ -5943,22 +6043,22 @@ Ziehe die Punkte mit deiner Maus, um ihre Position zu ändern. Doppelklicke auf An unknown error occurred. If this error continues to occur, please open an issue - Ein unbekannter Fehler ist aufgetreten. Sollte der Fehler weiterhin auftreten, erstelle bitte eine Fehlermeldung. + Es ist ein unbekannter Fehler aufgetreten. Sollte dieser Fehler weiterhin auftreten, öffne bitte ein Problem. Connection to room lost. Try to reconnect. - + Die Verbindung zum Raum wurde unterbrochen. Versuche, die Verbindung erneut herzustellen. You have been kicked by the room host. - + Du wurdest vom Raumhost rausgeworfen. IP address is already in use. Please choose another. - IP-Addresse wird bereits genutzt. Bitte wählen Sie eine andere. + IP-Adresse wird bereits genutzt. Bitte wähleeine andere. @@ -5969,13 +6069,15 @@ Ziehe die Punkte mit deiner Maus, um ihre Position zu ändern. Doppelklicke auf The user you are trying to kick/ban could not be found. They may have left the room. - + Der Nutzer, den du entfernen/sperren möchtest, konnte nicht gefunden werden. +Möglicherweise hat er den Raum bereits verlassen. No valid network interface is selected. Please go to Configure -> System -> Network and make a selection. - + Es ist keine gültige Netzwerkschnittstelle ausgewählt. +Bitte gehe zu „Konfigurieren“ -> „System“ -> „Netzwerk“ und triff eine Auswahl. @@ -5986,330 +6088,333 @@ Please go to Configure -> System -> Network and make a selection. GRenderWindow - - + + OpenGL not available! OpenGL nicht verfügbar! - + OpenGL shared contexts are not supported. Gemeinsame OpenGL-Kontexte werden nicht unterstützt. - + Eden has not been compiled with OpenGL support. - + Eden wurde nicht mit OpenGL-Unterstützung kompiliert. - - - + + + Error while initializing OpenGL! Fehler beim Initialisieren von OpenGL! - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. Deine Grafikkarte unterstützt kein OpenGL oder du hast nicht den neusten Treiber installiert. - + Error while initializing OpenGL 4.6! Fehler beim Initialisieren von OpenGL 4.6! - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 Deine Grafikkarte unterstützt OpenGL 4.6 nicht, oder du benutzt nicht die neuste Treiberversion.<br><br>GL Renderer:<br>%1 - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 Deine Grafikkarte unterstützt anscheinend nicht eine oder mehrere von yuzu benötigten OpenGL-Erweiterungen. Bitte stelle sicher, dass du den neusten Grafiktreiber installiert hast.<br><br>GL Renderer:<br>%1<br><br>Nicht unterstützte Erweiterungen:<br>%2 - + This build doesn't have OpenGL support. - + Diese Version bietet keine OpenGL-Unterstützung. GameList - + &Add New Game Directory &Neues Spieleverzeichnis hinzufügen - + Favorite Favorit - + Start Game Spiel starten - + Start Game without Custom Configuration Spiel ohne benutzerdefinierte Spiel-Einstellungen starten - + Open Save Data Location Spielstand-Verzeichnis öffnen - + Open Mod Data Location Mod-Verzeichnis öffnen - + Open Transferable Pipeline Cache - Transferierbaren Pipeline-Cache öffnen + Transferierbaren Rohrleitungs-Cache öffnen - + Link to Ryujinx - + Verknüpfung zu Ryujinx - + Remove Entfernen - + Remove Installed Update - Installiertes Update entfernen + Installierte Aktualisierung entfernen - + Remove All Installed DLC Alle installierten DLCs entfernen - + Remove Custom Configuration - Spiel-Einstellungen entfernen + Benutzerdefinierte Konfiguration entfernen - + Remove Cache Storage Cache-Speicher entfernen - + Remove OpenGL Pipeline Cache - OpenGL-Pipeline-Cache entfernen + OpenGL-Rohrleitungs-Cache entfernen - + Remove Vulkan Pipeline Cache - Vulkan-Pipeline-Cache entfernen + Vulkan-Rohrleitungs-Cache entfernen - + Remove All Pipeline Caches - Alle Pipeline-Caches entfernen + Alle Rohrleitungs-Caches entfernen - + Remove All Installed Contents Alle installierten Inhalte entfernen - + Manage Play Time Spielzeit verwalten - + Edit Play Time Data Spielzeit-Daten bearbeiten - + Remove Play Time Data Spielzeit-Daten entfernen - - + + Dump RomFS RomFS speichern - + Dump RomFS to SDMC - RomFS nach SDMC dumpen + RomFS nach SDMC auslesen - + Verify Integrity Integrität überprüfen - + Copy Title ID to Clipboard Title-ID in die Zwischenablage kopieren - + Navigate to GameDB entry GameDB-Eintrag öffnen - + Create Shortcut Verknüpfung erstellen - + Add to Desktop Zum Desktop hinzufügen - + Add to Applications Menu Zum Menü "Anwendungen" hinzufügen - + Configure Game Spiel konfigurieren - + Scan Subfolders Unterordner scannen - + Remove Game Directory Spieleverzeichnis entfernen - + ▲ Move Up ▲ Nach Oben - + ▼ Move Down ▼ Nach Unten - + Open Directory Location Verzeichnis öffnen - + Clear Löschen - - - Name - Name - - - - Compatibility - Kompatibilität - - - - Add-ons - Add-ons - - - - File type - Dateityp - - - - Size - Größe - - - - Play time - Spielzeit - GameListItemCompat - + Ingame Im Spiel - + Game starts, but crashes or major glitches prevent it from being completed. Spiel startet, stürzt jedoch ab oder hat signifikante Glitches, die es verbieten es durchzuspielen. - + Perfect Perfekt - + Game can be played without issues. Das Spiel kann ohne Probleme gespielt werden. - + Playable Spielbar - + Game functions with minor graphical or audio glitches and is playable from start to finish. Das Spiel funktioniert mit minimalen grafischen oder Tonstörungen und ist komplett spielbar. - + Intro/Menu Intro/Menü - + Game loads, but is unable to progress past the Start Screen. Das Spiel lädt, ist jedoch nicht im Stande den Startbildschirm zu passieren. - + Won't Boot Startet nicht - + The game crashes when attempting to startup. Das Spiel stürzt beim Versuch zu starten ab. - + Not Tested Nicht getestet - + The game has not yet been tested. Spiel wurde noch nicht getestet. + + GameListModel + + + Name + Name + + + + Compatibility + Kompatibilität + + + + Add-ons + Add-ons + + + + File type + Dateityp + + + + Size + Größe + + + + Play time + Spielzeit + + GameListPlaceholder - + Double-click to add a new folder to the game list Doppelklicke, um einen neuen Ordner zur Spieleliste hinzuzufügen. @@ -6317,17 +6422,17 @@ Please go to Configure -> System -> Network and make a selection. GameListSearchField - + %1 of %n result(s) %1 von %n Ergebnis%1 von %n Ergebnisse(n) - + Filter: Filter: - + Enter pattern to filter Wörter zum Filtern eingeben @@ -6382,7 +6487,7 @@ Please go to Configure -> System -> Network and make a selection. Load Previous Ban List - Vorherige Bann-Liste laden + Vorherige Bannliste laden @@ -6411,7 +6516,8 @@ Please go to Configure -> System -> Network and make a selection. Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid Eden account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: - + Der Raum konnte nicht in der öffentlichen Lobby angekündigt werden. Um einen Raum öffentlich zu hosten, muss in „Emulation -> Konfigurieren -> Web“ ein gültiges Eden-Konto eingerichtet sein. Wenn du einen Raum nicht in der öffentlichen Lobby veröffentlichen möchtest, wähle stattdessen „Nicht gelistet“. +Fehlerbehebungs-Meldung: @@ -6511,7 +6617,7 @@ Debug Message: Exit Eden - Verlasse Eden + Eden verlassen @@ -6656,12 +6762,12 @@ Debug Message: Loading Shaders 387 / 1628 - Shader 387 / 1628 wird geladen... + Schattierer werden geladen 387 / 1628 Loading Shaders %v out of %m - Shader %v / %m wird geladen... + Schattierer werden geladen: %v von %m @@ -6676,7 +6782,7 @@ Debug Message: Loading Shaders %1 / %2 - Shader %1 / %2 wird geladen... + Schattierer werden geladen %1 / %2 @@ -6700,7 +6806,7 @@ Debug Message: Nickname - Nickname + Spitzname @@ -6715,7 +6821,7 @@ Debug Message: Games I Own - Spiele die ich besitze + Spiele, die ich besitze @@ -6735,7 +6841,7 @@ Debug Message: Password Required to Join - Passwort zum Joinen benötigt + Passwort zum Beitreten erforderlich @@ -6813,1346 +6919,1335 @@ Debug Message: &Debugging - &Debugging + &Fehlerbehebung &Game List Mode - &Spieleliste Modus + &Spielelisten-Modus - + Game &Icon Size - + Spielsymbolgröße - + Reset Window Size to &720p Fenstergröße auf &720p zurücksetzen - + Reset Window Size to 720p Fenstergröße auf 720p zurücksetzen - + Reset Window Size to &900p Fenstergröße auf &900p zurücksetzen - + Reset Window Size to 900p Fenstergröße auf 900p zurücksetzen - + Reset Window Size to &1080p Fenstergröße auf &1080p zurücksetzen - + Reset Window Size to 1080p Fenstergröße auf 1080p zurücksetzen - + &Multiplayer &Mehrspieler - + &Tools &Werkzeuge - + Am&iibo - + Am&iibo - + Launch &Applet - + &Applet starten - + &TAS &TAS - + &Create Home Menu Shortcut - + &Startmenü-Verknüpfung erstellen - + Install &Firmware - + Installieren &Firmware - + &Help &Hilfe - + &Install Files to NAND... &Dateien im NAND installieren... - + L&oad File... Datei &laden... - + Load &Folder... &Verzeichnis laden... - + E&xit S&chließen - - + + &Pause &Pause - + &Stop &Stop - + &Verify Installed Contents Installierte Inhalte &überprüfen - + &About Eden &Über Eden - + Single &Window Mode &Einzelfenster-Modus - + Con&figure... - Kon&figurieren + Kon&figurieren... - + Ctrl+, - Strg+ + Strg+, - + Enable Overlay Display Applet - + Applet für die Überlagerungsanzeige aktivieren - + Show &Filter Bar &Filterleiste anzeigen - + Show &Status Bar &Statusleiste anzeigen - + Show Status Bar Statusleiste anzeigen - + &Browse Public Game Lobby &Öffentliche Spiele-Lobbys durchsuchen - + &Create Room &Raum erstellen - + &Leave Room &Raum verlassen - + &Direct Connect to Room &Direkte Verbindung zum Raum - + &Show Current Room &Aktuellen Raum anzeigen - + F&ullscreen Vollbild (&u) - + &Restart Neusta&rt - + Load/Remove &Amiibo... &Amiibo laden/entfernen... - + &Report Compatibility &Kompatibilität melden - + Open &Mods Page &Mods-Seite öffnen - + Open &Quickstart Guide &Schnellstart-Anleitung öffnen - + &FAQ &FAQ - + &Capture Screenshot &Bildschirmfoto aufnehmen - + &Album - + &Album - + &Set Nickname and Owner Spitzname und Besitzer &festlegen - + &Delete Game Data Spiel-Daten &löschen - + &Restore Amiibo Amiibo &wiederherstellen - + &Format Amiibo Amiibo &formatieren - + &Mii Editor - + &Mii-Editor - + &Configure TAS... &TAS &konfigurieren... - + Configure C&urrent Game... &Spiel-Einstellungen ändern... - - + + &Start &Start - + &Reset &Zurücksetzen - - + + R&ecord Aufnahme - + Open &Controller Menu Öffne &Controller-Menü - + Install Decryption &Keys - + Entschlüsselungsschlüssel installieren - + &Home Menu - + &Startmenü - + &Desktop - + &Desktop - + &Application Menu - + &Anwendungsmenü - + &Root Data Folder - + &Stammdatenordner - + &NAND Folder - + &NAND-Ordner - + &SDMC Folder - + &SDMC-Ordner - + &Mod Folder - + &Mod-Ordner - + &Log Folder - + &Protokoll-Ordner - + From Folder - + Aus Ordner - + From ZIP - + Aus ZIP - + &Eden Dependencies - + &Eden-Abhängigkeiten - + &Data Manager - + &Datenverwalter - + &Tree View - + &Baumansicht - + &Grid View - + &Rasteransicht - + Game Icon Size - + Spielsymbolgröße - - + + None - + Keine - + Show Game &Name - + Spiel&name anzeigen - + Show &Performance Overlay + Leistungsüberlagerung anzeigen + + + + &Carousel View - + Small (32x32) Klein (32x32) - + Standard (64x64) Standard (64x64) - + Large (128x128) Groß (128x128) - + Full Size (256x256) Volle Größe (256x256) - + Broken Vulkan Installation Detected Defekte Vulkan-Installation erkannt - + Vulkan initialization failed during boot. - + Initialisierung von Vulkan ist beim Systemstart fehlgeschlagen. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping Spiel wird ausgeführt - + Loading Web Applet... - + Web-Applet wird geladen... - - + + Disable Web Applet - + Web-Applet deaktivieren - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + Das Deaktivieren des Web-Applets kann zu unvorhersehbarem Verhalten führen und sollte nur bei „Super Mario 3D All-Stars“ verwendet werden. Möchtest du das Web-Applet wirklich deaktivieren? +(Dies kann in den Fehlerbehebungs-Einstellungen wieder aktiviert werden.) - + The amount of shaders currently being built - + Die Anzahl der Schattierer, die derzeit erstellt werden - + The current selected resolution scaling multiplier. - + Der aktuell ausgewählte Skalierungsfaktor für die Auflösung. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. - + Aktuelle Emulationsgeschwindigkeit. Werte über oder unter 100 % bedeuten, dass die Emulation schneller oder langsamer läuft als auf einer Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. - + Wie viele Bilder pro Sekunde das Spiel derzeit anzeigt. Dies variiert von Spiel zu Spiel und von Szene zu Szene. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. - + Die Zeit, die für die Emulation eines Switch-Frames benötigt wird, ohne Berücksichtigung von Framelimiting oder V-Sync. Bei einer Emulation mit voller Geschwindigkeit sollte dieser Wert höchstens 16,67 ms betragen. - + Unmute - Ton aktivieren + Stummschaltung aufheben - + Mute Stummschalten - + Reset Volume Lautstärke zurücksetzen - + &Clear Recent Files - + &Zuletzt verwendete Dateien löschen - + &Continue - + &Fortfahren - + Warning: Outdated Game Format - + Warnung: Veraltetes Spielformat - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. - + Du verwendest für dieses Spiel das dekontruierte ROM-Verzeichnisformat - ein veraltetes Format, das durch andere Formate wie NCA, NAX, XCI oder NSP abgelöst wurde. Dekonstruierten ROM-Verzeichnissen fehlen Symbole, Metadaten und die Unterstützung für Aktualisierungen. <br>Eine Erläuterung der verschiedenen Switch-Formate, die Eden unterstützt, findest du in unserem Benutzerhandbuch. Diese Meldung wird nicht erneut angezeigt. - - + + Error while loading ROM! - + Fehler beim Laden des ROMs! - + The ROM format is not supported. - + Das ROM-Format wird nicht unterstützt. - + An error occurred initializing the video core. - + Beim Initialisieren des Videokerns ist ein Fehler aufgetreten. - + Eden has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-mirror.github.io/help/reference/log-files/'>How to Upload the Log File</a>. - + Bei der Ausführung des Videokerns ist bei Eden ein Fehler aufgetreten. Dies wird in der Regel durch veraltete GPU-Treiber verursacht, einschließlich integrierter Treiber. Weitere Details findest du im Protokoll. Informationen zum Abrufen des Protokolls findest du auf der folgenden Seite: <a href='https://yuzu-mirror.github.io/help/reference/log-files/'>So lädst du die Protokolldatei hoch</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + Fehler beim Laden des ROMs! %1 - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. - + %1<br>Bitte erstelle deine Dateien erneut oder bitte auf Discord/Stoat um Hilfe. - + An unknown error occurred. Please see the log for more details. - + Es ist ein unbekannter Fehler aufgetreten. Weitere Informationen findest du im Protokoll. - + (64-bit) - + (64-bit) - + (32-bit) - + (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit - + %1 %2 - + Closing software... - + Software wird geschlossen... - + Save Data - + Speicherdaten - + Mod Data - + Mod-Daten - + Error Opening %1 Folder - + Fehler beim Öffnen des Ordners %1 - - + + Folder does not exist! - + Der Ordner existiert nicht! - + Remove Installed Game Contents? - + Installierte Spielinhalte entfernen? - + Remove Installed Game Update? - + Installierte Spielaktualisierung entfernen? - + Remove Installed Game DLC? - + Installiertes Spiel-DLC entfernen? - + Remove Entry - + Eintrag entfernen - + Delete OpenGL Transferable Shader Cache? - + Den übertragbaren Schattierer-Cache von OpenGL löschen? - + Delete Vulkan Transferable Shader Cache? - + Den übertragbaren Schattierer-Cache von Vulkan löschen? - + Delete All Transferable Shader Caches? - + Alle übertragbaren Schattierer-Caches löschen? - + Remove Custom Game Configuration? - + Benutzerdefinierte Spielkonfiguration entfernen? + + + + Remove Cache Storage? + Cache-Speicher löschen? + + + + Remove File + Datei entfernen + + + + Remove Play Time Data + Wiedergabezeitdaten löschen + + + + Reset play time? + Spielzeit zurücksetzen? + + + + + RomFS Extraction Failed! + RomFS-Extraktion fehlgeschlagen! + + + + There was an error copying the RomFS files or the user cancelled the operation. + Beim Kopieren der RomFS-Dateien ist ein Fehler aufgetreten oder der Benutzer hat den Vorgang abgebrochen. + + + + Full + Vollständig + + + + Skeleton + Skelett + + + + Select RomFS Dump Mode + RomFS-Auslesemodus auswählen + + + + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. + Bitte wähle aus, wie das RomFS kopiert werden soll. <br>Vollständig kopiert alle Dateien in das neue Verzeichnis, während <br>Skelett lediglich die Verzeichnisstruktur erstellt. + + + + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root + Unter %1 ist nicht genügend Speicherplatz vorhanden, um das RomFS zu extrahieren. Bitte schaffe Speicherplatz oder wähle unter „Emulation > Konfigurieren > System > Dateisystem > Dump-Stammverzeichnis“ ein anderes Dump-Verzeichnis aus. - Remove Cache Storage? - - - - - Remove File - - - - - Remove Play Time Data - - - - - Reset play time? - - - - - - RomFS Extraction Failed! - - - - - There was an error copying the RomFS files or the user cancelled the operation. - - - - - Full - - - - - Skeleton - - - - - Select RomFS Dump Mode - - - - - Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. - - - - - There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - - - - Extracting RomFS... - + RomFS wird extrahiert... - - + + Cancel - + Abbrechen - + RomFS Extraction Succeeded! - + RomFS wurde erfolgreich extrahiert! - + The operation completed successfully. - + Der Vorgang wurde erfolgreich abgeschlossen. - + Error Opening %1 - + Fehler beim Öffnen von %1 - + Select Directory - + Verzeichnis auswählen - + Properties - + Eigenschaften - + The game properties could not be loaded. - + Die Spieleigenschaften konnten nicht geladen werden. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. - + Switch-Ausführungsdatei (%1);;Alle Dateien (*.*) - + Load File - + Datei laden - + Open Extracted ROM Directory - + Verzeichnis mit dem extrahierten ROM öffnen + + + + Invalid Directory Selected + Ungültiges Verzeichnis ausgewählt + + + + The directory you have selected does not contain a 'main' file. + Das von dir ausgewählte Verzeichnis enthält keine „main“-Datei. + + + + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) + Installierbare Switch-Dateien (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX-Kassetten-Image (*.xci) + + + + Install Files + Dateien installieren + + + + %n file(s) remaining + %n Datei(en) verbleiben%n Datei(en) verbleiben + + + + Installing file "%1"... + Datei „%1“ wird installiert... + + + + + Install Results + Installationsergebnisse + + + + To avoid possible conflicts, we discourage users from installing base games to the NAND. +Please, only use this feature to install updates and DLC. + Um mögliche Konflikte zu vermeiden, raten wir davon ab, Basisspiele auf dem NAND-Speicher zu installieren. +Bitte nutze diese Funktion ausschließlich zur Installation von Aktualisierungen und DLCs. + + + + %n file(s) were newly installed + + %n Datei(en) wurden neu installiert +%n Datei(en) wurden neu installiert + + + + + %n file(s) were overwritten + + %n Datei(en) wurden überschrieben +%n Datei(en) wurden überschrieben + + + + + %n file(s) failed to install + + %n Datei(en) konnten nicht installiert werden +%n Datei(en) konnten nicht installiert werden + + + + + System Application + Systemanwendung + + + + System Archive + Systemarchiv + + + + System Application Update + System-Anwendungsaktualisierung + + + + Firmware Package (Type A) + Firmware-Paket (Typ A) + + + + Firmware Package (Type B) + Firmware-Paket (Typ B) + + + + Game + Spiel + + + + Game Update + Spielaktualisierung + + + + Game DLC + Spiel-DLC - Invalid Directory Selected - + Delta Title + Delta-Titel - - The directory you have selected does not contain a 'main' file. - + + Select NCA Install Type... + NCA-Installationstyp auswählen... + + + + Please select the type of title you would like to install this NCA as: +(In most instances, the default 'Game' is fine.) + Bitte wähle den Titeltyp aus, unter dem du diese NCA installieren möchtest: +(In den meisten Fällen ist die Vorgabe „Spiel“ ausreichend.) + + + + Failed to Install + Installation fehlgeschlagen - Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - - - - - Install Files - - - - - %n file(s) remaining - - - - - Installing file "%1"... - - - - - - Install Results - - - - - To avoid possible conflicts, we discourage users from installing base games to the NAND. -Please, only use this feature to install updates and DLC. - - - - - %n file(s) were newly installed - - - - - - %n file(s) were overwritten - - - - - - %n file(s) failed to install - - - - - - System Application - - - - - System Archive - - - - - System Application Update - - - - - Firmware Package (Type A) - - - - - Firmware Package (Type B) - - - - - Game - - - - - Game Update - - - - - Game DLC - - - - - Delta Title - - - - - Select NCA Install Type... - - - - - Please select the type of title you would like to install this NCA as: -(In most instances, the default 'Game' is fine.) - - - - - Failed to Install - - - - The title type you selected for the NCA is invalid. - + Der von dir für die NCA ausgewählte Titel ist ungültig. - + File not found - + Datei nicht gefunden - + File "%1" not found - + Datei „%1“ nicht gefunden - + OK - + OK - + Function Disabled - + Funktion deaktiviert - + Compatibility list reporting is currently disabled. Check back later! - + Die Anzeige der Kompatibilitätsliste ist derzeit deaktiviert. Schaue später noch einmal vorbei! - + Error opening URL - + Fehler beim Öffnen der URL - + Unable to open the URL "%1". - + Die URL „%1“ kann nicht geöffnet werden. - + TAS Recording - + TAS-Aufnahme - + Overwrite file of player 1? - + Datei von Spieler 1 überschreiben? - + Invalid config detected - + Ungültige Konfiguration erkannt - + Handheld controller can't be used on docked mode. Pro controller will be selected. - + Der Handgerät-Controller kann im angedockten Modus nicht verwendet werden. Es wird der Pro-Controller ausgewählt. - - + + Amiibo - + Amiibo - - + + The current amiibo has been removed - + Das aktuelle amiibo wurde entfernt - + Error - + Fehler - - + + The current game is not looking for amiibos - + Das aktuelle Spiel sucht nicht nach Amiibos - + Amiibo File (%1);; All Files (*.*) - + Amiibo-Datei (%1);; Alle Dateien (*.*) - + Load Amiibo - + Amiibo laden - + Error loading Amiibo data - + Fehler beim Laden der Amiibo-Daten - + The selected file is not a valid amiibo - + Die ausgewählte Datei ist kein gültiges amiibo - + The selected file is already on use - + Die ausgewählte Datei wird bereits verwendet - + An unknown error occurred - + Ein unbekannter Fehler ist aufgetreten - - - Keys not installed - - - - - - Install decryption keys and restart Eden before attempting to install firmware. - - - - - Select Dumped Firmware Source Location - - - - - Select Dumped Firmware ZIP - - - - - Zipped Archives (*.zip) - - - - - Firmware cleanup failed - - - - - Failed to clean up extracted firmware cache. -Check write permissions in the system temp directory and try again. -OS reported error: %1 - - - - + No firmware available - + Keine Firmware verfügbar - + Firmware Corrupted - + Firmware beschädigt - + Unknown applet - + Unbekanntes Applet - + Applet doesn't map to a known value. - + „Applet“ lässt sich keinem bekannten Wert zuordnen. - + Record not found - + Aufnahme nicht gefunden - + Applet not found. Please reinstall firmware. - + Applet nicht gefunden. Bitte installiere die Firmware neu. - + Capture Screenshot - + Bildschirmfoto aufnehmen - + PNG Image (*.png) - + PNG-Bild (*.png) - + TAS state: Running %1/%2 - + TAS-Status: %1/%2 läuft - + TAS state: Recording %1 - + TAS-Status: %1 wird aufgenommen - + TAS state: Idle %1/%2 - + TAS-Status: Leerlauf %1/%2 - + TAS State: Invalid - + TAS-Status: Ungültig - + &Stop Running - + &Ausführung beenden - + Stop R&ecording - + Aufnahme beenden - + Building: %n shader(s) - + Erstellung: %n SchattiererErstellung: %n Schattierer - + Scale: %1x %1 is the resolution scaling factor - + Maßstab: 1:1 - + Speed: %1% / %2% - + Geschwindigkeit: %1% / %2% - + Speed: %1% - + Geschwindigkeit: %1% - + Game: %1 FPS - + Spiel: %1 FPS - + Frame: %1 ms - + Frame: %1 ms - - - FSR - - - - + NO AA - + Kein AA - + VOLUME: MUTE - + LAUTSTÄRKE: STUMM - + VOLUME: %1% Volume percentage (e.g. 50%) - + LAUTSTÄRKE: %1% - + Derivation Components Missing - + Ableitungskomponenten fehlen - + Decryption keys are missing. Install them now? - + Entschlüsselungsschlüssel fehlen. Möchtest du diese jetzt installieren? - + Wayland Detected! - + Wayland erkannt! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. Would you like to force it for future launches? - + Wayland ist bekannt für erhebliche Leistungsprobleme und mysteriöse Fehler. +Es wird empfohlen, stattdessen X11 zu verwenden. + +Möchtest du dies für zukünftige Starts erzwingen? - + Use X11 - + X11 verwenden - + Continue with Wayland - + Mit Wayland fortfahren - + Don't show again - + Nicht mehr anzeigen - + Restart Required - + Neustart erforderlich - + Restart Eden to apply the X11 backend. - + Starte Eden neu, um das X11-Backend zu aktivieren. - + Slow - + Langsam - + Turbo - + Turbo - + Unlocked - + Entsperrt - + Select RomFS Dump Target - + RomFS-Ausleseziel auswählen - + Please select which RomFS you would like to dump. - + Bitte wähle aus, welches RomFS du auslesen möchtest. - + Are you sure you want to close Eden? - + Möchtest du Eden wirklich schließen? - - - + + + Eden - + Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. - + Möchtest du die Emulation wirklich beenden? Alle nicht gespeicherten Fortschritte gehen dabei verloren. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? - + Die derzeit ausgeführte Anwendung hat Eden angewiesen, nicht beendet zu werden. + +Möchtest du diese Anweisung ignorieren und das Programm trotzdem beenden? FXAA - + FXAA SMAA - + SMAA Nearest - + Nächstgelegen Bilinear - + Bilinear Bicubic - + Bikubisch Zero-Tangent - + Nulltangente B-Spline - + B-Spline Mitchell - + Mitchell Spline-1 - + Spline-1 Gaussian - + Gauß Lanczos - + Lanczos ScaleForce - + ScaleForce + + + + FSR + FSR Area - + Bereich MMPX - + MMPX - + + SGSR + SGSR + + + + SGSR EdgeDir + SGSR EdgeDir + + + Docked - + Angedockt - + Handheld - - - - - Fast - - - - - Balanced - + Handgerät - Accurate - + Fast + Schnell - - Vulkan - + + Balanced + Ausgewogen + + + + Accurate + Genau - OpenGL GLSL - + Vulkan + Vulkan - OpenGL SPIRV - + OpenGL GLSL + OpenGL GLSL - OpenGL GLASM - + OpenGL SPIRV + OpenGL SPIRV - + + OpenGL GLASM + OpenGL GLASM + + + Null - + Null @@ -8161,7 +8256,8 @@ Would you like to bypass this and exit anyway? Linking the old directory failed. You may need to re-run with administrative privileges on Windows. OS gave error: %1 - + Das Verknüpfen des alten Verzeichnisses ist fehlgeschlagen. Unter Windows musst du den Vorgang möglicherweise mit Administratorrechten erneut ausführen. +Das Betriebssystem hat folgenden Fehler gemeldet: %1 @@ -8172,7 +8268,13 @@ If this is not desirable, delete the following files: %2 %3 %4 - + + +Bitte beachte, dass deine Konfiguration und deine Daten mit %1 geteilt werden. +Solltest du dies nicht wünschen, lösche bitte die folgenden Dateien: +%2 +%3 +%4 @@ -8180,12 +8282,15 @@ If this is not desirable, delete the following files: If you wish to clean up the files which were left in the old data location, you can do so by deleting the following directory: %1 - + + +Wenn du die Dateien bereinigen möchtest, die am alten Speicherort zurückgeblieben sind, kannst du dies tun, indem du das folgende Verzeichnis löschst: +%1 - + Data was migrated successfully. - + Die Daten wurden erfolgreich migriert. @@ -8193,12 +8298,12 @@ If you wish to clean up the files which were left in the old data location, you Import Mods - + Mods importieren The specified folder or archive contains the following mods. Select which ones to install. - + Der angegebene Ordner oder das angegebene Archiv enthält die folgenden Mods. Wähle aus, welche installiert werden sollen. @@ -8242,7 +8347,7 @@ If you wish to clean up the files which were left in the old data location, you IP Address - IP-Addresse + IP-Adresse @@ -8286,8 +8391,8 @@ If you wish to clean up the files which were left in the old data location, you Failed to update the room information. Please check your Internet connection and try hosting the room again. Debug Message: - Aktualisieren der Rauminformationen fehlgeschlagen. Überprüfe deine Internetverbindung und versuche erneut einen Raum zu erstellen. -Debug Message: + Die Rauminformationen konnten nicht aktualisiert werden. Bitte überprüfe deine Internetverbindung und versuche erneut, den Raum zu hosten. +Fehlerbehebungs-Meldung: @@ -8311,7 +8416,7 @@ Proceed anyway? You are about to close the room. Any network connections will be closed. - Du bist dabei den Raum zu schließen. Jede Verbindung zum Netzwerk wird geschlossen. + Du bist dabei, den Raum zu schließen. Alle Netzwerkverbindungen werden getrennt. @@ -8321,7 +8426,7 @@ Proceed anyway? You are about to leave the room. Any network connections will be closed. - Du bist dabei den Raum zu verlassen. Jede Verbindung zum Netzwerk wird geschlossen. + Du bist dabei, den Raum zu verlassen. Alle Netzwerkverbindungen werden geschlossen. @@ -8330,127 +8435,127 @@ Proceed anyway? New User - + Neuer Benutzer Change Avatar - + Avatar ändern Set Image - + Bild festlegen UUID - + UUID Eden - + Eden Username - + Benutzername UUID must be 32 hex characters (0-9, A-F) - + Die UUID muss aus 32 Hexadezimalzeichen bestehen (0–9, A–F) Generate - + Generieren Select User Image - + Benutzerbild auswählen Image Formats (*.jpg *.jpeg *.png *.bmp) - + Bildformate (*.jpg *.jpeg *.png *.bmp) No firmware available - + Keine Firmware verfügbar Please install the firmware to use firmware avatars. - + Installiere die Firmware, um Firmware-Avatare nutzen zu können. Error loading archive - + Fehler beim Laden des Archivs Archive is not available. Please install/reinstall firmware. - + Das Archiv ist nicht verfügbar. Bitte installiere oder aktualisiere die Firmware. Could not locate RomFS. Your file or decryption keys may be corrupted. - + RomFS konnte nicht gefunden werden. Deine Datei oder deine Entschlüsselungsschlüssel sind möglicherweise beschädigt. Error extracting archive - + Fehler beim Entpacken des Archivs Could not extract RomFS. Your file or decryption keys may be corrupted. - + RomFS konnte nicht extrahiert werden. Deine Datei oder deine Entschlüsselungsschlüssel sind möglicherweise beschädigt. Error finding image directory - + Fehler beim Finden des Bildverzeichnisses Failed to find image directory in the archive. - + Das Bildverzeichnis im Archiv konnte nicht gefunden werden. No images found - + Keine Bilder gefunden No avatar images were found in the archive. - + Im Archiv wurden keine Avatar-Bilder gefunden. All Good Tooltip - + Alles bestens Must be 32 hex characters (0-9, a-f) Tooltip - + Muss aus 32 Hexadezimalzeichen bestehen (0–9, a–f) Must be between 1 and 32 characters Tooltip - + Muss zwischen 1 und 32 Zeichen lang sein @@ -8491,73 +8596,73 @@ p, li { white-space: pre-wrap; } Form - + Formular Frametime - + Frame-Zeit 0 ms - + 0 ms Min: 0 - + Min: 0 Max: 0 - + Max: 0 Avg: 0 - + Durchschnitt: 0 FPS - + BpS 0 fps - + 0 BpS %1 fps - + %1 BpS Avg: %1 - + Durchschnitt: %1 Min: %1 - + Min: %1 Max: %1 - + Max: %1 %1 ms - + %1 ms @@ -8573,22 +8678,22 @@ p, li { white-space: pre-wrap; } Select - + Auswählen Cancel - + Abbrechen Background Color - + Hintergrundfarbe Select Firmware Avatar - + Firmware-Avatar auswählen @@ -8598,56 +8703,59 @@ p, li { white-space: pre-wrap; } Migration - + Migration Clear Shader Cache - + Schattierer-Cache leeren Keep Old Data - + Alte Daten behalten Clear Old Data - + Alte Daten löschen Link Old Directory - + Altes Verzeichnis verknüpfen - + + + No - + Nein You can manually re-trigger this prompt by deleting the new config directory: %1 - + Du kannst diese Aufforderung manuell erneut auslösen, indem du das neue Konfigurationsverzeichnis löschst: +%1 Migrating - + Migrieren Migrating, this may take a while... - + Die Migration läuft, das kann eine Weile dauern... @@ -8970,7 +9078,7 @@ p, li { white-space: pre-wrap; } Touch - Touch + Berühren @@ -9037,47 +9145,47 @@ p, li { white-space: pre-wrap; } %1 spielt %2 - + Play Time: %1 - + Spielzeit: %1 - + Never Played - + Noch nie gespielt - + Version: %1 - + Version: %1 - + Version: 1.0.0 - + Version: 1.0.0 - + Installed SD Titles Installierte SD-Titel - + Installed NAND Titles Installierte NAND-Titel - + System Titles Systemtitel - + Add New Game Directory Neues Spieleverzeichnis hinzufügen - + Favorites Favoriten @@ -9142,7 +9250,7 @@ p, li { white-space: pre-wrap; } dd/MM/yyyy - TT/MM/JJJJ + TT/MM/JJJJ @@ -9197,253 +9305,291 @@ p, li { white-space: pre-wrap; } QtCommon::Content - - - Game Requires Firmware - - + Game Requires Firmware + Für das Spiel ist eine Firmware erforderlich + + + The game you are trying to launch requires firmware to boot or to get past the opening menu. Please <a href='https://yuzu-mirror.github.io/help/quickstart'>dump and install firmware</a>, or press "OK" to launch anyways. - + Das Spiel, das du starten möchtest, benötigt Firmware, um zu booten oder das Startmenü zu verlassen. Bitte <a href='https://yuzu-mirror.github.io/help/quickstart'>lade die Firmware herunter und installiere sie</a>, oder klicke auf „OK“, um das Spiel trotzdem zu starten. - + Installing Firmware... - + Firmware wird installiert... - - - - - + + + + + Cancel - - - - - Firmware Install Failed - + Abbrechen - Firmware Install Succeeded - + Firmware Install Failed + Firmware-Installation fehlgeschlagen - - Firmware integrity verification failed! - + + Firmware Install Succeeded + Firmware-Installation erfolgreich - + Firmware integrity verification failed! + Überprüfung der Firmware-Integrität fehlgeschlagen! + + + + Verification failed for the following files: %1 - + Die Überprüfung der folgenden Dateien ist fehlgeschlagen: + +%1 - - + + Verifying integrity... - + Integrität wird überprüft... + + + + + Integrity verification succeeded! + Die Integritätsprüfung war erfolgreich! - Integrity verification succeeded! - + The operation completed successfully. + Der Vorgang wurde erfolgreich abgeschlossen. - - - The operation completed successfully. - + + + Integrity verification failed! + Die Integritätsprüfung ist fehlgeschlagen! - - Integrity verification failed! - + File contents may be corrupt or missing. + Der Inhalt der Datei ist möglicherweise beschädigt oder fehlt. - - File contents may be corrupt or missing. - + + Integrity verification couldn't be performed + Die Integritätsprüfung konnte nicht durchgeführt werden - Integrity verification couldn't be performed - - - - Firmware installation cancelled, firmware may be in a bad state or corrupted. File contents could not be checked for validity. - + Die Firmware-Installation wurde abgebrochen. Die Firmware ist möglicherweise fehlerhaft oder beschädigt. Der Inhalt der Datei konnte nicht auf Gültigkeit überprüft werden. - + Select Dumped Keys Location - + Speicherort der ausgelesenen Schlüssel auswählen - + Decryption Keys install succeeded - + Installation der Entschlüsselungsschlüssel erfolgreich - + Decryption Keys install failed - + Installation der Entschlüsselungsschlüssel fehlgeschlagen + + + + Orphaned Profiles Detected! + Verwaiste Profile erkannt! - Orphaned Profiles Detected! - - - - UNEXPECTED BAD THINGS MAY HAPPEN IF YOU DON'T READ THIS!<br>Eden has detected the following save directories with no attached profile:<br>%1<br><br>The following profiles are valid:<br>%2<br><br>Click "OK" to open your save folder and fix up your profiles.<br>Hint: copy the contents of the largest or last-modified folder elsewhere, delete all orphaned profiles, and move your copied contents to the good profile.<br><br>Still confused? See the <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/Orphaned.md'>help page</a>.<br> - + UNERWARTETE PROBLEME KÖNNTEN AUFTRETEN, WENN DU DIES NICHT LIEST!<br>Eden hat die folgenden Speicherverzeichnisse ohne zugeordnetes Profil erkannt:<br>%1<br><br>Die folgenden Profile sind gültig:<br>%2<br><br>Klicke auf „OK“, um deinen Speicherordner zu öffnen und deine Profile zu korrigieren.<br>Tipp: Kopiere den Inhalt des größten oder zuletzt geänderten Ordners an einen anderen Ort, lösche alle verwaisten Profile und verschiebe die kopierten Inhalte in das intakte Profil.<br><br>Immer noch unsicher? Lies auf der <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/Orphaned.md'>Hilfeseite</a> nach.<br> - + Really clear data? - + Daten wirklich löschen? - + Important data may be lost! - + Wichtige Daten könnten verloren gehen! + + + + Are you REALLY sure? + Bist du dir WIRKLICH sicher? - Are you REALLY sure? - - - - Once deleted, your data will NOT come back! Only do this if you're 100% sure you want to delete this data. - + Sobald deine Daten gelöscht sind, können sie NICHT wiederhergestellt werden! +Führe diesen Vorgang nur durch, wenn du dir zu 100 % sicher bist, dass du diese Daten löschen möchtest. - + Clearing... - + Säuberung... - + Select Export Location - + Exportziel auswählen - + %1.zip - + %1.zip - - + + + Zipped Archives (*.zip) - + ZIP-Archive (*.zip) - + Exporting data. This may take a while... - + Daten werden exportiert. Das kann eine Weile dauern... - + Exporting - + Exportieren - + Exported Successfully - + Erfolgreich exportiert - + Data was exported successfully. - + Die Daten wurden erfolgreich exportiert. - + Export Cancelled - + Export abgebrochen - + Export was cancelled by the user. - + Der Export wurde vom Benutzer abgebrochen. + + + + Export Failed + Export fehlgeschlagen - Export Failed - - - - Ensure you have write permissions on the targeted directory and try again. - + Stelle sicher, dass du Schreibrechte für das Zielverzeichnis hast, und versuche es erneut. - + Select Import Location - + Importziel auswählen + + + + Import Warning + Importwarnung - Import Warning - - - - All previous data in this directory will be deleted. Are you sure you wish to proceed? - + Alle bisherigen Daten in diesem Verzeichnis werden gelöscht. Möchtest du wirklich fortfahren? - + Importing data. This may take a while... - + Daten werden importiert. Das kann eine Weile dauern... - + Importing - + Importieren - + Imported Successfully - + Erfolgreich importiert - + Data was imported successfully. - + Die Daten wurden erfolgreich importiert. - + Import Cancelled - + Import abgebrochen - + Import was cancelled by the user. - + Der Import wurde vom Benutzer abgebrochen. + + + + Import Failed + Import fehlgeschlagen - Import Failed - + Ensure you have read permissions on the targeted directory and try again. + Stelle sicher, dass du Lesezugriff auf das Zielverzeichnis hast, und versuche es erneut. - - Ensure you have read permissions on the targeted directory and try again. - + + Keys not installed + Schlüssel nicht installiert + + + + Install decryption keys and restart Eden before attempting to install firmware. + Installiere die Entschlüsselungsschlüssel und starte Eden neu, bevor du versuchst, die Firmware zu installieren. + + + + Select Dumped Firmware Source Location + Speicherort der ausgelesenen Firmware auswählen + + + + Select Dumped Firmware ZIP + ZIP-Datei der ausgelesenen Firmware auswählen + + + + Firmware cleanup failed + Firmware-Bereinigung ist fehlgeschlagen + + + + Failed to clean up extracted firmware cache. +Check write permissions in the system temp directory and try again. +OS reported error: %1 + Der Cache für die extrahierte Firmware konnte nicht bereinigt werden. +Überprüfe die Schreibrechte im temporären Systemverzeichnis und versuche es erneut. +Vom Betriebssystem gemeldeter Fehler: %1 @@ -9451,17 +9597,17 @@ Only do this if you're 100% sure you want to delete this data. Linked Save Data - + Verknüpfte Speicherdaten Save data has been linked. - + Die Speicherdaten wurden verknüpft. Failed to link save data - + Verknüpfung der Speicherdaten fehlgeschlagen @@ -9469,76 +9615,81 @@ Only do this if you're 100% sure you want to delete this data. %1 To: %2 - + Verzeichnis konnte nicht verknüpft werden: + %1 +Nach: + %2 Already Linked - + Bereits verknüpft This title is already linked to Ryujinx. Would you like to unlink it? - + Dieser Titel ist bereits mit Ryujinx verknüpft. Möchtest du die Verknüpfung aufheben? Failed to unlink old directory - + Fehler beim Löschen des alten Verzeichnisses OS returned error: %1 - + Das Betriebssystem hat folgenden Fehler zurückgegeben: %1 Failed to copy save data - + Speicherdaten konnten nicht kopiert werden. Unlink Successful - + Verknüpfung erfolgreich aufgehoben Successfully unlinked Ryujinx save data. Save data has been kept intact. - + Ryujinx-Speicherdaten erfolgreich entknüpft. Die Speicherdaten blieben intakt. Could not find Ryujinx installation - + Ryujinx-Installation konnte nicht gefunden werden. Could not find a valid Ryujinx installation. This may typically occur if you are using Ryujinx in portable mode. Would you like to manually select a portable folder to use? - + Es konnte keine gültige Ryujinx-Installation gefunden werden. Dies tritt in der Regel auf, wenn du Ryujinx im portablen Modus verwendest. + +Möchtest du manuell einen portablen Ordner auswählen? Ryujinx Portable Location - + Standort von Ryujinx-Portabel Not a valid Ryujinx directory - + Kein gültiges Ryujinx-Verzeichnis The specified directory does not contain valid Ryujinx data. - + Das angegebene Verzeichnis enthält keine gültigen Ryujinx-Daten. Could not find Ryujinx save data - + Ryujinx-Speicherdaten konnten nicht gefunden werden. @@ -9546,17 +9697,17 @@ Would you like to manually select a portable folder to use? Error Removing Contents - + Fehler beim Entfernen der Inhalte Error Removing Update - + Fehler beim Entfernen der Aktualisierung Error Removing DLC - + Fehler beim Entfernen des DLC @@ -9566,252 +9717,254 @@ Would you like to manually select a portable folder to use? Successfully Removed - + Erfolgreich entfernt Successfully removed the installed base game. - + Das installierte Basisspiel wurde erfolgreich entfernt. The base game is not installed in the NAND and cannot be removed. - + Das Basisspiel ist nicht im NAND installiert und kann nicht entfernt werden. Successfully removed the installed update. - + Die installierte Aktualisierung wurde erfolgreich entfernt. There is no update installed for this title. - + Für diesen Titel ist keine Aktualisierung installiert. There are no DLCs installed for this title. - + Für diesen Titel sind keine DLCs installiert. Successfully removed %1 installed DLC. - + Der installierte DLC %1 wurde erfolgreich entfernt. Error Removing Transferable Shader Cache - + Fehler beim Entfernen des übertragbaren Schattierer-Caches A shader cache for this title does not exist. - + Für diesen Titel gibt es keinen Schattierer-Cache. Successfully removed the transferable shader cache. - + Der übertragbare Schattierer-Cache wurde erfolgreich entfernt. Failed to remove the transferable shader cache. - + Der übertragbare Schattierer-Cache konnte nicht entfernt werden. Error Removing Vulkan Driver Pipeline Cache - + Fehler beim Entfernen des Vulkan-Treiber-Rohrleitungs-Caches Failed to remove the driver pipeline cache. - + Der Treiber-Rohrleitungs-Cache konnte nicht gelöscht werden. Error Removing Transferable Shader Caches - + Fehler beim Entfernen übertragbarer Schattierer-Caches Successfully removed the transferable shader caches. - + Die übertragbaren Schattierer-Caches wurden erfolgreich entfernt. Failed to remove the transferable shader cache directory. - + Das Verzeichnis des übertragbaren Schattierer-Caches konnte nicht entfernt werden. Error Removing Custom Configuration - + Fehler beim Entfernen der benutzerdefinierten Konfiguration A custom configuration for this title does not exist. - + Für diesen Titel gibt es keine benutzerdefinierte Konfiguration. Successfully removed the custom game configuration. - + Die benutzerdefinierte Spielkonfiguration wurde erfolgreich entfernt. Failed to remove the custom game configuration. - + Die benutzerdefinierte Spielkonfiguration konnte nicht entfernt werden. Reset Metadata Cache - + Metadaten-Cache zurücksetzen The metadata cache is already empty. - + Der Metadaten-Cache ist bereits leer. The operation completed successfully. - + Der Vorgang wurde erfolgreich abgeschlossen. The metadata cache couldn't be deleted. It might be in use or non-existent. - + Der Metadaten-Cache konnte nicht gelöscht werden. Möglicherweise wird er gerade verwendet oder ist nicht vorhanden. - + Create Shortcut - + Verknüpfung erstellen - + Do you want to launch the game in fullscreen? - + Möchtest du das Spiel im Vollbildmodus starten? - + Shortcut Created - + Verknüpfung erstellt - + Successfully created a shortcut to %1 - - - - - Shortcut may be Volatile! - + Verknüpfung zu %1 erfolgreich erstellt. - This will create a shortcut to the current AppImage. This may not work well if you update. Continue? - + Shortcut may be Volatile! + Die Verknüpfung kann unzuverlässig sein! - - Failed to Create Shortcut - + + This will create a shortcut to the current AppImage. This may not work well if you update. Continue? + Dadurch wird eine Verknüpfung zum aktuellen AppImage erstellt. Nach einer Aktualisierung funktioniert dies möglicherweise nicht mehr einwandfrei. Möchtest du fortfahren? - Failed to create a shortcut to %1 - + Failed to Create Shortcut + Verknüpfung konnte nicht erstellt werden. - - Create Icon - + + Failed to create a shortcut to %1 + Verknüpfung zu %1 konnte nicht erstellt werden. - Cannot create icon file. Path "%1" does not exist and cannot be created. - + Create Icon + Symbol erstellen - - No firmware available - + + Cannot create icon file. Path "%1" does not exist and cannot be created. + Die Icon-Datei kann nicht erstellt werden. Der Pfad „%1“ existiert nicht und kann nicht erstellt werden. - Please install firmware to use the home menu. - + No firmware available + Keine Firmware verfügbar - - Home Menu Applet - + + Please install firmware to use the home menu. + Bitte installiere die Firmware, um das Startmenü nutzen zu können. + Home Menu Applet + Startseiten-Menü-Applet + + + Home Menu is not available. Please reinstall firmware. - + Das Startmenü ist nicht verfügbar. Bitte installiere die Firmware neu. QtCommon::Mod - + Mod Name - + Mod-Name - + What should this mod be called? Wie soll diese Mod heißen? - + RomFS - + RomFS - + ExeFS/Patch - + ExeFS/Patch - + Cheat - + Cheat - + Mod Type Mod Typ - + Could not detect mod type automatically. Please manually specify the type of mod you downloaded. Most mods are RomFS mods, but patches (.pchtxt) are typically ExeFS mods. - + Der Mod-Typ konnte nicht automatisch erkannt werden. Bitte gib den Typ des heruntergeladenen Mods manuell an. + +Die meisten Mods sind RomFS-Mods, während Patches (.pchtxt) in der Regel ExeFS-Mods sind. - - + + Mod Extract Failed - + Mod-Extraktion fehlgeschlagen - + Failed to create temporary directory %1 - + Fehler beim Erstellen des temporären Verzeichnisses %1 - + Zip file %1 is empty - + ZIP-Datei %1 ist leer @@ -9819,12 +9972,12 @@ Most mods are RomFS mods, but patches (.pchtxt) are typically ExeFS mods. Error Opening Shader Cache - + Fehler beim Öffnen des Schattierer-Caches Failed to create or open shader cache for this title, ensure your app data directory has write permissions. - + Der Schattierer-Cache für diesen Titel konnte nicht erstellt oder geöffnet werden. Stelle sicher, dass dein App-Datenverzeichnis über Schreibrechte verfügt. @@ -9832,153 +9985,159 @@ Most mods are RomFS mods, but patches (.pchtxt) are typically ExeFS mods. Contains game save data. DO NOT REMOVE UNLESS YOU KNOW WHAT YOU'RE DOING! - + Enthält Spielspeicherdaten. NICHT ENTFERNEN, ES SEI DENN, DU WEIßT, WAS DU TUST! Contains Vulkan and OpenGL pipeline caches. Generally safe to remove. - + Enthält Vulkan- und OpenGL-Rohrleitungs-Caches. Kann in der Regel bedenkenlos entfernt werden. Contains updates and DLC for games. - + Enthält Aktualisierungen und DLCs für Spiele. Contains firmware and applet data. - + Enthält Firmware- und Applet-Daten. Contains game mods, patches, and cheats. - + Enthält Spiel-Mods, Patches und Cheats. Decryption Keys were successfully installed - + Entschlüsselungsschlüssel wurden erfolgreich installiert Unable to read key directory, aborting - + Schlüsselverzeichnis kann nicht gelesen werden – Abbruch. One or more keys failed to copy. - + Ein oder mehrere Schlüssel konnten nicht kopiert werden. Verify your keys file has a .keys extension and try again. - + Überprüfe, ob deine Schlüsseldatei die Erweiterung .keys hat, und versuche es erneut. Decryption Keys failed to initialize. Check that your dumping tools are up to date and re-dump keys. - + Die Entschlüsselungsschlüssel konnten nicht initialisiert werden. Überprüfe, ob deine Auslesewerkzeuge auf dem neuesten Stand sind, und lies die Schlüssel erneut aus. Successfully installed firmware version %1 - + Firmware-Version %1 erfolgreich installiert Unable to locate potential firmware NCA files - + Potenzielle NCA-Dateien für die Firmware konnten nicht gefunden werden Failed to delete one or more firmware files. - + Das Löschen einer oder mehrerer Firmware-Dateien ist fehlgeschlagen. One or more firmware files failed to copy into NAND. - + Eine oder mehrere Firmware-Dateien konnten nicht in den NAND kopiert werden. Firmware installation cancelled, firmware may be in a bad state or corrupted. Restart Eden or re-install firmware. - + Die Firmware-Installation wurde abgebrochen. Die Firmware ist möglicherweise fehlerhaft oder beschädigt. Starte Eden neu oder installiere die Firmware erneut. Firmware missing. Firmware is required to run certain games and use the Home Menu. - + Firmware fehlt. Für die Ausführung bestimmter Spiele und die Nutzung des Startmenüs ist Firmware erforderlich. Firmware reported as present, but was unable to be read. Check for decryption keys and redump firmware if necessary. - + Die Firmware wurde als vorhanden gemeldet, konnte jedoch nicht gelesen werden. Überprüfe die Entschlüsselungsschlüssel und speichere die Firmware gegebenenfalls erneut. Eden has detected user data for the following emulators: - + Eden hat Benutzerdaten für die folgenden Emulatoren erkannt: Would you like to migrate your data for use in Eden? Select the corresponding button to migrate data from that emulator. This may take a while. - + Möchtest du deine Daten für die Verwendung in Eden migrieren? +Klicke auf die entsprechende Schaltfläche, um Daten aus diesem Emulator zu migrieren. +Dies kann eine Weile dauern. Clearing shader cache is recommended for all users. Do not uncheck unless you know what you're doing. - + Das Leeren des Schattierer-Caches wird allen Nutzern empfohlen. +Deaktiviere diese Option nur, wenn du genau weißt, was du tust. Keeps the old data directory. This is recommended if you aren't space-constrained and want to keep separate data for the old emulator. - + Behält das alte Datenverzeichnis bei. Dies wird empfohlen, wenn du nicht unter Platzmangel leidest +und die Daten für den alten Emulator separat aufbewahren möchtest. Deletes the old data directory. This is recommended on devices with space constraints. - + Löscht das alte Datenverzeichnis. +Dies wird auf Geräten mit begrenztem Speicherplatz empfohlen. Creates a filesystem link between the old directory and Eden directory. This is recommended if you want to share data between emulators. - + Erstellt eine Dateisystemverknüpfung zwischen dem alten Verzeichnis und dem Eden-Verzeichnis. +Dies wird empfohlen, wenn du Daten zwischen Emulatoren austauschen möchtest. Ryujinx title database does not exist. - + Ryujinx-Titeldatenbank existiert nicht. Invalid header on Ryujinx title database. - + Ungültiger Header in der Ryujinx-Titeldatenbank. Invalid magic header on Ryujinx title database. - + Ungültiger Magic-Header in der Ryujinx-Titeldatenbank. Invalid byte alignment on Ryujinx title database. - + Ungültige Byte-Ausrichtung in der Ryujinx-Titeldatenbank. No items found in Ryujinx title database. - + In der Ryujinx-Titel-Datenbank wurden keine Einträge gefunden. Title %1 not found in Ryujinx title database. - + Der Titel %1 wurde in der Ryujinx-Titeldatenbank nicht gefunden. @@ -10087,7 +10246,7 @@ This is recommended if you want to share data between emulators. Handheld - Handheld + Handgerät @@ -10402,24 +10561,26 @@ p, li { white-space: pre-wrap; } Ryujinx Link - + Ryujinx-Verknüpfung Linking save data to Ryujinx lets both Ryujinx and Eden reference the same save files for your games. By selecting "From Eden", previous save data stored in Ryujinx will be deleted, and vice versa for "From Ryujinx". - + Wenn du deine Speicherdaten mit Ryujinx verknüpfst, können sowohl Ryujinx als auch Eden auf dieselben Speicherdateien für deine Spiele zugreifen. + +Wenn du „Von Eden“ auswählst, werden die zuvor in Ryujinx gespeicherten Daten gelöscht, und umgekehrt, wenn du „Von Ryujinx“ wählst. From Eden - + Von Eden From Ryujinx - + Von Ryujinx @@ -10429,12 +10590,12 @@ By selecting "From Eden", previous save data stored in Ryujinx will be Failed to link save data - + Verknüpfen der Speicherdaten fehlgeschlagen OS returned error: %1 - + Betriebssystem hat folgenden Fehler zurückgegeben: %1 @@ -10450,7 +10611,7 @@ By selecting "From Eden", previous save data stored in Ryujinx will be Set Play Time Data - + Spielzeitdaten festlegen @@ -10470,7 +10631,7 @@ By selecting "From Eden", previous save data stored in Ryujinx will be Total play time reached maximum. - + Die maximale Spielzeit wurde erreicht. @@ -10478,92 +10639,93 @@ By selecting "From Eden", previous save data stored in Ryujinx will be Update Available - + Aktualisierung verfügbar <a href="%1">View on Forgejo</a> - + <a href="%1">Auf Forgejo ansehen</a> Would you like to install this update? - + Möchtest du diese Aktualisierung installieren? Available Versions - + Verfügbare Versionen %1 is available for download. - + %1 steht zum Herunterladen bereit. - + New Version Location - + Speicherort der neuen Version - + All Files (*.*) - - - - - - - Failed to save file - + Alle Dateien (*.*) - Could not open file %1 for writing. - - - - - Downloading... - - - - - Cancel - - - - Could not write to file %1. - - - - Could not commit to file %1. - + Failed to save file + Speichern der Datei fehlgeschlagen - - Failed to download file - + + Could not open file %1 for writing. + Datei %1 konnte nicht zum Schreiben geöffnet werden. + + + + Downloading... + Wird heruntergeladen... + + + + Cancel + Abbrechen + + + + Could not write to file %1. + Es konnte nicht in die Datei %1 geschrieben werden. + + + + Could not commit to file %1. + Änderungen konnten nicht für Datei %1 übernommen werden. - Could not download from %1%2 -Error code: %3 - + Failed to download file + Herunterladen der Datei fehlgeschlagen - - Download Complete - + + Could not download from %1%2 +Error code: %3 + Das Herunterladen von %1%2 konnte nicht durchgeführt werden +Fehler-Code: %3 + Download Complete + Download abgeschlossen + + + Successfully downloaded %1. Would you like to open it? - + %1 wurde erfolgreich heruntergeladen. Möchtest du die Datei öffnen? \ No newline at end of file diff --git a/dist/languages/el.ts b/dist/languages/el.ts index ba849128fa..0ed40a0f61 100644 --- a/dist/languages/el.ts +++ b/dist/languages/el.ts @@ -704,7 +704,7 @@ Options lower than 1X can cause artifacts. - Determines how sharpened the image will look using FSR's dynamic contrast. + Determines how sharpened the image will look using FSR's or SGSR's dynamic contrast. @@ -755,35 +755,35 @@ Disabling it is only intended for debugging. - + Use asynchronous GPU emulation - + Uses an extra CPU thread for rendering. This option should always remain enabled. - + NVDEC emulation: Εξομοίωση NVDEC: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. - + ASTC Decoding Method: - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -792,55 +792,66 @@ stuttering but may present artifacts. - + ASTC Recompression Method: - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. - + Frame Pacing Mode (Vulkan only) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. - + VRAM Usage Mode: - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. - + Skip CPU Inner Invalidation - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. - + + Anti-Flicker + + + + + Forces GPU fence callbacks to wait for submitted GPU work. +Use with Fast GPU mode, to avoid flicker with lower performance impact. + + + + VSync Mode: - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -848,1361 +859,1381 @@ Immediate (no synchronization) presents whatever is available and can exhibit te - + Sync Memory Operations - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. - + Enable asynchronous presentation (Vulkan only) - + Slightly improves performance by moving presentation to a separate CPU thread. - + Force maximum clocks (Vulkan only) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. - + Anisotropic Filtering: Ανισοτροπικό Φιλτράρισμα: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. - + GPU Mode: - + Controls the GPU emulation mode. Most games render fine with Fast or Balanced modes, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. - + DMA Accuracy: - + Controls the DMA precision accuracy. Safe precision fixes issues in some games but may degrade performance. - + Enable asynchronous shader compilation - + May reduce shader stutter. - + Fast GPU Time - + Overclocks the emulated GPU to increase dynamic resolution and render distance. Use 256 for maximal performance and 512 for maximal graphics fidelity. - + GPU Unswizzle - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. - + GPU Unswizzle Max Texture Size - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. - + GPU Unswizzle Stream Size - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. - + GPU Unswizzle Chunk Size - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. - + Use Vulkan pipeline cache - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. - + Enable Compute Pipelines (Intel Vulkan Only) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. - + Enable Reactive Flushing - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. - + Sync to framerate of video playback - + Run the game at normal speed during video playback, even when the framerate is unlocked. - + Barrier feedback loops - + Improves rendering of transparency effects in specific games. - + Enable buffer history - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. - + Fix bloom effects - + Removes bloom in Burnout. - + Enable Legacy Rescale Pass - + May fix rescale issues in some games by relying on behavior from the previous implementation. Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. - + Extended Dynamic State - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. - + Vertex Input Dynamic State - + Enables vertex input dynamic state feature for better quality and performance. - + Sample Shading - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. - + RNG Seed RNG Seed - + Controls the seed of the random number generator. Mainly used for speedrunning. - + Device Name - + The name of the console. - + + Homebrew Args + + + + + Command-line arguments passed to homebrew at launch (e.g. -noglsl). + + + + Custom RTC Date: - + This option allows to change the clock of the console. Can be used to manipulate time in games. - + The number of seconds from the current unix time - + Language: - + This option can be overridden when region setting is auto-select - + Region: Περιφέρεια: - + The region of the console. - + Time Zone: Ζώνη Ώρας: - + The time zone of the console. - + Sound Output Mode: - + Console Mode: - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. - + Prompt for user profile on boot - + Useful if multiple people use the same PC. - + Pause when not in focus - + Pauses emulation when focusing on other windows. - + Confirm before stopping emulation - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. - + Hide mouse on inactivity Απόκρυψη δρομέα ποντικιού στην αδράνεια - + Hides the mouse after 2.5s of inactivity. - + Disable controller applet - + Forcibly disables the use of the controller applet in emulated programs. When a program attempts to open the controller applet, it is immediately closed. - + Check for updates - + Whether or not to check for updates upon startup. - + Enable Gamemode - + Force X11 as Graphics Backend - + Custom frontend - + Real applet - + Never - + On Load - + Always - + CPU CPU - + GPU - + CPU Asynchronous - + Uncompressed (Best quality) - + BC1 (Low quality) - + BC3 (Medium quality) - - + + Auto Αυτόματη - + 30 FPS - + 60 FPS - + 90 FPS - + 120 FPS - + Conservative - + Aggressive - + Vulkan Vulkan - + OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) - + Null - + Fast - + Balanced - - + + Accurate Ακριβής - - + + Default Προεπιλεγμένο - + Unsafe (fast) - + Safe (stable) - + Unsafe Επισφαλής - + Paranoid (disables most optimizations) - + Debugging - + Dynarmic - + NCE - + Borderless Windowed Παραθυροποιημένο Χωρίς Όρια - + Exclusive Fullscreen Αποκλειστική Πλήρης Οθόνη - + No Video Output Χωρίς Έξοδο Βίντεο - + CPU Video Decoding Αποκωδικοποίηση Βίντεο CPU - + GPU Video Decoding (Default) Αποκωδικοποίηση Βίντεο GPU (Προεπιλογή) - + 0.25X (180p/270p) [EXPERIMENTAL] - + 0.5X (360p/540p) [EXPERIMENTAL] - + 0.75X (540p/810p) [EXPERIMENTAL] 0.75X (540p/810p) [ΠΕΙΡΑΜΑΤΙΚΟ] - + 1X (720p/1080p) 1X (720p/1080p) - + 1.25X (900p/1350p) [EXPERIMENTAL] - + 1.5X (1080p/1620p) [EXPERIMENTAL] - + 2X (1440p/2160p) 2X (1440p/2160p) - + 3X (2160p/3240p) 3X (2160p/3240p) - + 4X (2880p/4320p) 4X (2880p/4320p) - + 5X (3600p/5400p) 5X (3600p/5400p) - + 6X (4320p/6480p) 6X (4320p/6480p) - + 7X (5040p/7560p) - + 8X (5760p/8640p) - + Nearest Neighbor Πλησιέστερος Γείτονας - + Bilinear Διγραμμικό - + Bicubic Δικυβικό - + Gaussian Gaussian - + Lanczos - + ScaleForce ScaleForce - + AMD FidelityFX Super Resolution - + Area - + MMPX - + Zero-Tangent - + B-Spline - + Mitchell - + Spline-1 - - + + Snapdragon Game Super Resolution + + + + + Snapdragon Game Super Resolution EdgeDir + + + + + None Κανένα - + FXAA FXAA - + SMAA SMAA - + Default (16:9) Προεπιλογή (16:9) - + Force 4:3 Επιβολή 4:3 - + Force 21:9 Επιβολή 21:9 - + Force 16:10 Επιβολή 16:10 - + Stretch to Window Επέκταση στο Παράθυρο - + Automatic Αυτόματα - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x - + 32x - + 64x - + Japanese (日本語) Ιαπωνικά (日本語) - + American English - + French (français) Γαλλικά (Français) - + German (Deutsch) Γερμανικά (Deutsch) - + Italian (italiano) Ιταλικά (Italiano) - + Spanish (español) Ισπανικά (Español) - + Chinese Κινέζικα - + Korean (한국어) Κορεάτικα (한국어) - + Dutch (Nederlands) Ολλανδικά (Nederlands) - + Portuguese (português) Πορτογαλικά (Português) - + Russian (Русский) Ρώσικα (Русский) - + Taiwanese Ταϊβανέζικα - + British English Βρετανικά Αγγλικά - + Canadian French Καναδικά Γαλλικά - + Latin American Spanish Λατινοαμερικάνικα Ισπανικά - + Simplified Chinese Απλοποιημένα Κινέζικα - + Traditional Chinese (正體中文) Παραδοσιακά Κινέζικα (正體中文) - + Brazilian Portuguese (português do Brasil) Πορτογαλικά Βραζιλίας (Português do Brasil) - + Polish (polska) - + Thai (แบบไทย) - - + + Japan Ιαπωνία - + USA ΗΠΑ - + Europe Ευρώπη - + Australia Αυστραλία - + China Κίνα - + Korea Κορέα - + Taiwan Ταϊβάν - + Auto (%1) Auto select time zone - + Default (%1) Default time zone - + CET CET - + CST6CDT CST6CDT - + Cuba Κούβα - + EET EET - + Egypt Αίγυπτος - + Eire - + EST EST - + EST5EDT EST5EDT - + GB - + GB-Eire - + GMT GMT - + GMT+0 GMT+0 - + GMT-0 GMT-0 - + GMT0 GMT0 - + Greenwich Γκρήνουιτς - + Hongkong Χονγκ Κονγκ - + HST HST - + Iceland Ισλανδία - + Iran Ιράν - + Israel Ισραήλ - + Jamaica Ιαμαϊκή - + Kwajalein - + Libya Λιβύη - + MET MET - + MST MST - + MST7MDT MST7MDT - + Navajo Ναβάχο - + NZ - + NZ-CHAT - + Poland Πολωνία - + Portugal Πορτογαλία - + PRC - + PST8PDT PST8PDT - + ROC ROC - + ROK ROK - + Singapore Σιγκαπούρη - + Turkey Τουρκία - + UCT UCT - + Universal Παγκόσμια - + UTC UTC - + W-SU W-SU - + WET WET - + Zulu - + Mono Μονοφωνικό - + Stereo Στέρεοφωνικό - + Surround - + 4GB DRAM (Default) - + 6GB DRAM (Unsafe) - + 8GB DRAM - + 10GB DRAM (Unsafe) - + 12GB DRAM (Unsafe) - + Docked Docked - + Handheld Handheld - - + + Off - + Boost (1700MHz) - + Fast (2000MHz) - + Always ask (Default) - + Only if game specifies not to stop - + Never ask - - + + Medium (256) - - + + High (512) - + Very Small (16 MB) - + Small (32 MB) - + Normal (128 MB) - + Large (256 MB) - + Very Large (512 MB) - + Very Low (4 MB) - + Low (8 MB) - + Normal (16 MB) - + Medium (32 MB) - + High (64 MB) - + Very Low (32) - + Low (64) - + Normal (128) - + Disabled - + ExtendedDynamicState 1 - + ExtendedDynamicState 2 - + ExtendedDynamicState 3 - + Tree View - + Grid View @@ -3246,33 +3277,33 @@ Would you like to delete the old save data? Χρώμα Φόντου: - + % - FSR sharpening percentage (e.g. 50%) - % + FSR/SGSR sharpening percentage (e.g. 50%) + - + Off - + VSync Off - + Recommended - + On - + VSync On @@ -4401,7 +4432,7 @@ To invert the axes, first move your joystick vertically, and then horizontally.< - + Configure Διαμόρφωση @@ -4432,7 +4463,7 @@ To invert the axes, first move your joystick vertically, and then horizontally.< - + Test Τεστ @@ -4447,77 +4478,77 @@ To invert the axes, first move your joystick vertically, and then horizontally.< Κατάργηση Διακομιστή - + %1:%2 %1:%2 - - - - - - + + + + + + Eden - + Port number has invalid characters Ο αριθμός θύρας έχει μη έγκυρους χαρακτήρες - + Port has to be in range 0 and 65353 Η θύρα πρέπει να ανήκει στο εύρος 0 και 65353 - + IP address is not valid Η διεύθυνση IP δεν είναι έγκυρη - + This UDP server already exists Αυτός ο διακομιστής UDP υπάρχει ήδη - + Unable to add more than 8 servers Δεν είναι δυνατή η προσθήκη περισσότερων από 8 διακομιστών - + Testing Δοκιμή - + Configuring Διαμόρφωση - + Test Successful Τεστ Επιτυχές - + Successfully received data from the server. Λήφθηκαν με επιτυχία δεδομένα από τον διακομιστή. - + Test Failed Η Δοκιμή Απέτυχε - + Could not receive valid data from the server.<br>Please verify that the server is set up correctly and the address and port are correct. Δεν ήταν δυνατή η λήψη έγκυρων δεδομένων από τον διακομιστή.<br>Βεβαιωθείτε ότι ο διακομιστής έχει ρυθμιστεί σωστά και ότι η διεύθυνση και η θύρα είναι σωστές. - + UDP Test or calibration configuration is in progress.<br>Please wait for them to finish. Η δοκιμή UDP ή η διαμόρφωση βαθμονόμησης είναι σε εξέλιξη.<br>Παρακαλώ περιμένετε να τελειώσουν. @@ -4701,57 +4732,57 @@ Current values are %1% and %2% respectively. - + Add-Ons Πρόσθετα - + System Σύστημα - + CPU CPU - + Graphics Γραφικά - + Adv. Graphics Προχ. Γραφικά - + Ext. Graphics - + Audio Ήχος - + Input Profiles - + Network - + Applets - + Properties Ιδιότητες @@ -5760,7 +5791,7 @@ Drag points to change position, or double-click table cells to edit values. - + Calculating... @@ -5962,50 +5993,50 @@ Please go to Configure -> System -> Network and make a selection. GRenderWindow - - + + OpenGL not available! Το OpenGL δεν είναι διαθέσιμο! - + OpenGL shared contexts are not supported. - + Eden has not been compiled with OpenGL support. - - - + + + Error while initializing OpenGL! Σφάλμα κατα την αρχικοποίηση του OpenGL! - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. - + Error while initializing OpenGL 4.6! - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 - + This build doesn't have OpenGL support. @@ -6013,279 +6044,282 @@ Please go to Configure -> System -> Network and make a selection. GameList - + &Add New Game Directory - + Favorite Αγαπημένο - + Start Game Έναρξη παιχνιδιού - + Start Game without Custom Configuration - + Open Save Data Location Άνοιγμα Τοποθεσίας Αποθήκευσης Δεδομένων - + Open Mod Data Location Άνοιγμα Τοποθεσίας Δεδομένων Mod - + Open Transferable Pipeline Cache - + Link to Ryujinx - + Remove Αφαίρεση - + Remove Installed Update Αφαίρεση Εγκατεστημένης Ενημέρωσης - + Remove All Installed DLC Αφαίρεση Όλων των Εγκατεστημένων DLC - + Remove Custom Configuration - + Remove Cache Storage - + Remove OpenGL Pipeline Cache - + Remove Vulkan Pipeline Cache - + Remove All Pipeline Caches Καταργήστε Όλη την Κρυφή μνήμη του Pipeline - + Remove All Installed Contents Καταργήστε Όλο το Εγκατεστημένο Περιεχόμενο - + Manage Play Time - + Edit Play Time Data - + Remove Play Time Data - - + + Dump RomFS Απόθεση του RomFS - + Dump RomFS to SDMC Απόθεση του RomFS στο SDMC - + Verify Integrity - + Copy Title ID to Clipboard Αντιγραφή του Title ID στο Πρόχειρο - + Navigate to GameDB entry Μεταβείτε στην καταχώρηση GameDB - + Create Shortcut - + Add to Desktop - + Add to Applications Menu - + Configure Game - + Scan Subfolders Σκανάρισμα Υποφακέλων - + Remove Game Directory Αφαίρεση Φακέλου Παιχνιδιών - + ▲ Move Up ▲ Μετακίνηση Επάνω - + ▼ Move Down ▼ Μετακίνηση Κάτω - + Open Directory Location Ανοίξτε την Τοποθεσία Καταλόγου - + Clear Καθαρισμός - - - Name - Όνομα - - - - Compatibility - Συμβατότητα - - - - Add-ons - Πρόσθετα - - - - File type - Τύπος αρχείου - - - - Size - Μέγεθος - - - - Play time - - GameListItemCompat - + Ingame - + Game starts, but crashes or major glitches prevent it from being completed. - + Perfect Τέλεια - + Game can be played without issues. - + Playable - + Game functions with minor graphical or audio glitches and is playable from start to finish. - + Intro/Menu Εισαγωγή/Μενου - + Game loads, but is unable to progress past the Start Screen. - + Won't Boot Δεν ξεκινά - + The game crashes when attempting to startup. Το παιχνίδι διακόπτεται κατά την προσπάθεια εκκίνησης. - + Not Tested Μη Τεσταρισμένο - + The game has not yet been tested. Το παιχνίδι δεν έχει ακόμα τεσταριστεί. + + GameListModel + + + Name + + + + + Compatibility + + + + + Add-ons + + + + + File type + + + + + Size + + + + + Play time + + + GameListPlaceholder - + Double-click to add a new folder to the game list Διπλο-κλικ για προσθήκη νεου φακέλου στη λίστα παιχνιδιών @@ -6293,17 +6327,17 @@ Please go to Configure -> System -> Network and make a selection. GameListSearchField - + %1 of %n result(s) - + Filter: Φίλτρο: - + Enter pattern to filter Εισαγάγετε μοτίβο για φιλτράρισμα @@ -6797,1139 +6831,1099 @@ Debug Message: - + Game &Icon Size - + Reset Window Size to &720p - + Reset Window Size to 720p - + Reset Window Size to &900p - + Reset Window Size to 900p - + Reset Window Size to &1080p - + Reset Window Size to 1080p - + &Multiplayer &Πολλαπλών Παικτών - + &Tools - + Am&iibo - + Launch &Applet - + &TAS &TAS - + &Create Home Menu Shortcut - + Install &Firmware - + &Help - + &Install Files to NAND... - + L&oad File... - + Load &Folder... - + E&xit - - + + &Pause &Παύση - + &Stop &Σταμάτημα - + &Verify Installed Contents - + &About Eden - + Single &Window Mode - + Con&figure... - + Ctrl+, - + Enable Overlay Display Applet - + Show &Filter Bar - + Show &Status Bar - + Show Status Bar - + &Browse Public Game Lobby &Περιήγηση σε δημόσιο λόμπι παιχνιδιού - + &Create Room &Δημιουργία δωματίου - + &Leave Room &Αποχωρήσει από το δωμάτιο - + &Direct Connect to Room &Άμεση σύνδεση σε Δωμάτιο - + &Show Current Room &Εμφάνιση τρέχοντος δωματίου - + F&ullscreen - + &Restart - + Load/Remove &Amiibo... - + &Report Compatibility - + Open &Mods Page - + Open &Quickstart Guide - + &FAQ - + &Capture Screenshot - + &Album - + &Set Nickname and Owner - + &Delete Game Data - + &Restore Amiibo - + &Format Amiibo - + &Mii Editor - + &Configure TAS... - + Configure C&urrent Game... - - + + &Start &Έναρξη - + &Reset - - + + R&ecord - + Open &Controller Menu - + Install Decryption &Keys - + &Home Menu - + &Desktop - + &Application Menu - + &Root Data Folder - + &NAND Folder - + &SDMC Folder - + &Mod Folder - + &Log Folder - + From Folder - + From ZIP - + &Eden Dependencies - + &Data Manager - + &Tree View - + &Grid View - + Game Icon Size - - + + None - + Show Game &Name - + Show &Performance Overlay - + + &Carousel View + + + + Small (32x32) - + Standard (64x64) - + Large (128x128) - + Full Size (256x256) - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping - + Loading Web Applet... - - + + Disable Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. - + Unmute - + Mute - + Reset Volume - + &Clear Recent Files - + &Continue - + Warning: Outdated Game Format - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. - - + + Error while loading ROM! - + The ROM format is not supported. - + An error occurred initializing the video core. - + Eden has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-mirror.github.io/help/reference/log-files/'>How to Upload the Log File</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. - + (64-bit) - + (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit - + Closing software... - + Save Data - + Mod Data - + Error Opening %1 Folder - - + + Folder does not exist! - + Remove Installed Game Contents? - + Remove Installed Game Update? - + Remove Installed Game DLC? - + Remove Entry - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? - + Remove Cache Storage? - + Remove File - + Remove Play Time Data - + Reset play time? - - + + RomFS Extraction Failed! - + There was an error copying the RomFS files or the user cancelled the operation. - + Full - + Skeleton - + Select RomFS Dump Mode - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... - - + + Cancel - + RomFS Extraction Succeeded! - + The operation completed successfully. - + Error Opening %1 - + Select Directory - + Properties - + The game properties could not be loaded. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. - + Load File - + Open Extracted ROM Directory - + Invalid Directory Selected - + The directory you have selected does not contain a 'main' file. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files - + %n file(s) remaining - + Installing file "%1"... - - + + Install Results - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application - + System Archive - + System Application Update - + Firmware Package (Type A) - + Firmware Package (Type B) - + Game - + Game Update - + Game DLC - + Delta Title - + Select NCA Install Type... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) - + Failed to Install - + The title type you selected for the NCA is invalid. - + File not found - + File "%1" not found - + OK - + Function Disabled - + Compatibility list reporting is currently disabled. Check back later! - + Error opening URL - + Unable to open the URL "%1". - + TAS Recording - + Overwrite file of player 1? - + Invalid config detected - + Handheld controller can't be used on docked mode. Pro controller will be selected. - - + + Amiibo - - + + The current amiibo has been removed - + Error - - + + The current game is not looking for amiibos - + Amiibo File (%1);; All Files (*.*) - + Load Amiibo - + Error loading Amiibo data - + The selected file is not a valid amiibo - + The selected file is already on use - + An unknown error occurred - - - Keys not installed - - - - - - Install decryption keys and restart Eden before attempting to install firmware. - - - - - Select Dumped Firmware Source Location - - - - - Select Dumped Firmware ZIP - - - - - Zipped Archives (*.zip) - - - - - Firmware cleanup failed - - - - - Failed to clean up extracted firmware cache. -Check write permissions in the system temp directory and try again. -OS reported error: %1 - - - - + No firmware available - + Firmware Corrupted - + Unknown applet - + Applet doesn't map to a known value. - + Record not found - + Applet not found. Please reinstall firmware. - + Capture Screenshot - + PNG Image (*.png) - + TAS state: Running %1/%2 - + TAS state: Recording %1 - + TAS state: Idle %1/%2 - + TAS State: Invalid - + &Stop Running - + Stop R&ecording - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor - + Speed: %1% / %2% - + Speed: %1% - + Game: %1 FPS - + Frame: %1 ms - - - FSR - - - - + NO AA - + VOLUME: MUTE - + VOLUME: %1% Volume percentage (e.g. 50%) - + Derivation Components Missing - + Decryption keys are missing. Install them now? - + Wayland Detected! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -7937,74 +7931,74 @@ Would you like to force it for future launches? - + Use X11 - + Continue with Wayland - + Don't show again - + Restart Required - + Restart Eden to apply the X11 backend. - + Slow - + Turbo - + Unlocked - + Select RomFS Dump Target - + Please select which RomFS you would like to dump. - + Are you sure you want to close Eden? - - - + + + Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? @@ -8070,6 +8064,11 @@ Would you like to bypass this and exit anyway? ScaleForce + + + FSR + + Area @@ -8081,52 +8080,62 @@ Would you like to bypass this and exit anyway? - + + SGSR + + + + + SGSR EdgeDir + + + + Docked - + Handheld - + Fast - + Balanced - + Accurate - + Vulkan - + OpenGL GLSL - + OpenGL SPIRV - + OpenGL GLASM - + Null @@ -8159,7 +8168,7 @@ If you wish to clean up the files which were left in the old data location, you - + Data was migrated successfully. @@ -9009,47 +9018,47 @@ p, li { white-space: pre-wrap; } %1 παίζει %2 - + Play Time: %1 - + Never Played - + Version: %1 - + Version: 1.0.0 - + Installed SD Titles - + Installed NAND Titles - + System Titles Τίτλοι Συστήματος - + Add New Game Directory Προσθήκη Νέας Τοποθεσίας Παιχνιδιών - + Favorites Αγαπημένα @@ -9170,253 +9179,286 @@ p, li { white-space: pre-wrap; } QtCommon::Content - + Game Requires Firmware - + The game you are trying to launch requires firmware to boot or to get past the opening menu. Please <a href='https://yuzu-mirror.github.io/help/quickstart'>dump and install firmware</a>, or press "OK" to launch anyways. - + Installing Firmware... - - - - - + + + + + Cancel - + Firmware Install Failed - + Firmware Install Succeeded - + Firmware integrity verification failed! - - + + Verification failed for the following files: %1 - - + + Verifying integrity... + + + + Integrity verification succeeded! + + - Integrity verification succeeded! - - - - - The operation completed successfully. - - + + Integrity verification failed! - + File contents may be corrupt or missing. - + Integrity verification couldn't be performed - + Firmware installation cancelled, firmware may be in a bad state or corrupted. File contents could not be checked for validity. - + Select Dumped Keys Location - + Decryption Keys install succeeded - + Decryption Keys install failed - + Orphaned Profiles Detected! - + UNEXPECTED BAD THINGS MAY HAPPEN IF YOU DON'T READ THIS!<br>Eden has detected the following save directories with no attached profile:<br>%1<br><br>The following profiles are valid:<br>%2<br><br>Click "OK" to open your save folder and fix up your profiles.<br>Hint: copy the contents of the largest or last-modified folder elsewhere, delete all orphaned profiles, and move your copied contents to the good profile.<br><br>Still confused? See the <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/Orphaned.md'>help page</a>.<br> - + Really clear data? - + Important data may be lost! - + Are you REALLY sure? - + Once deleted, your data will NOT come back! Only do this if you're 100% sure you want to delete this data. - + Clearing... - + Select Export Location - + %1.zip - - + + + Zipped Archives (*.zip) - + Exporting data. This may take a while... - + Exporting - + Exported Successfully - + Data was exported successfully. - + Export Cancelled - + Export was cancelled by the user. - + Export Failed - + Ensure you have write permissions on the targeted directory and try again. - + Select Import Location - + Import Warning - + All previous data in this directory will be deleted. Are you sure you wish to proceed? - + Importing data. This may take a while... - + Importing - + Imported Successfully - + Data was imported successfully. - + Import Cancelled - + Import was cancelled by the user. - + Import Failed - + Ensure you have read permissions on the targeted directory and try again. + + + Keys not installed + + + + + Install decryption keys and restart Eden before attempting to install firmware. + + + + + Select Dumped Firmware Source Location + + + + + Select Dumped Firmware ZIP + + + + + Firmware cleanup failed + + + + + Failed to clean up extracted firmware cache. +Check write permissions in the system temp directory and try again. +OS reported error: %1 + + QtCommon::FS @@ -9660,72 +9702,72 @@ Would you like to manually select a portable folder to use? - + Create Shortcut - + Do you want to launch the game in fullscreen? - + Shortcut Created - + Successfully created a shortcut to %1 - + Shortcut may be Volatile! - + This will create a shortcut to the current AppImage. This may not work well if you update. Continue? - + Failed to Create Shortcut - + Failed to create a shortcut to %1 - + Create Icon - + Cannot create icon file. Path "%1" does not exist and cannot be created. - + No firmware available - + Please install firmware to use the home menu. - + Home Menu Applet - + Home Menu is not available. Please reinstall firmware. @@ -9733,55 +9775,55 @@ Would you like to manually select a portable folder to use? QtCommon::Mod - + Mod Name - + What should this mod be called? - + RomFS - + ExeFS/Patch - + Cheat - + Mod Type - + Could not detect mod type automatically. Please manually specify the type of mod you downloaded. Most mods are RomFS mods, but patches (.pchtxt) are typically ExeFS mods. - - + + Mod Extract Failed - + Failed to create temporary directory %1 - + Zip file %1 is empty @@ -10469,65 +10511,65 @@ By selecting "From Eden", previous save data stored in Ryujinx will be - + New Version Location - + All Files (*.*) - - - + + + Failed to save file - + Could not open file %1 for writing. - + Downloading... - + Cancel - + Could not write to file %1. - + Could not commit to file %1. - + Failed to download file - + Could not download from %1%2 Error code: %3 - + Download Complete - + Successfully downloaded %1. Would you like to open it? diff --git a/dist/languages/es.ts b/dist/languages/es.ts index 115ca93dc6..1413c12cac 100644 --- a/dist/languages/es.ts +++ b/dist/languages/es.ts @@ -629,7 +629,7 @@ Desactivando este opción obliga a que todos los accesos a la memoria utilicen e This option improves the speed of some approximate floating-point functions by using less accurate native approximations. - Esta opción mejora el rendimiento de algunas funciones aproximadas de punto flotante al utilizar aproximaciones nativas menos precisas. + Esta opción mejora la velocidad de algunas funciones aproximadas de punto flotante mediante el uso de aproximaciones nativas menos precisas. @@ -724,8 +724,8 @@ Las opciones inferiores a 1X pueden generar errores visuales. - Determines how sharpened the image will look using FSR's dynamic contrast. - Determina el nivel de nitidez de la imagen utilizando el contraste dinámico de FSR. + Determines how sharpened the image will look using FSR's or SGSR's dynamic contrast. + Determina el grado de nitidez que tendrá la imagen usando el contraste dinámico de FSR o SGSR. @@ -782,24 +782,24 @@ Disabling it is only intended for debugging. Desactivarlo solo está destinado para depuración. - + Use asynchronous GPU emulation Usar emulación asíncrona de la GPU - + Uses an extra CPU thread for rendering. This option should always remain enabled. Usa un hilo adicional de la CPU para el renderizado. Esta opción siempre debe permanecer activada. - + NVDEC emulation: Emulación NVDEC: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. @@ -808,12 +808,12 @@ Puede usar la CPU, GPU o no descodificar (mostrará una pantalla en negro durant En la mayoría de casos, descodificar mediante la GPU es la mejor opción. - + ASTC Decoding Method: Modo descodificación ASTC: - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -825,12 +825,12 @@ GPU: utiliza los sombreadores computables de la GPU para descodificar las textur CPU asíncrona: usa la CPU para descodificar las texturas ASTC bajo demanda. Elimina los tirones causados por la descodificación ASTC, pero puede generar errores visuales. - + ASTC Recompression Method: Modo recompresión ASTC: - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. @@ -838,44 +838,56 @@ BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, BC1/BC3: El formato intermedio será recomprimido a BC1 o BC3, lo que ahorra VRAM, pero degrada la calidad de la imagen. - + Frame Pacing Mode (Vulkan only) Modo de ritmo de fotogramas (solo Vulkan) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. Controla cómo el emulador gestiona el ritmo de los fotogramas para reducir los tirones y hacer que la velocidad de los fotogramas sea más suave y consistente. - + VRAM Usage Mode: Modo de uso de la VRAM: - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. Selecciona si el emulador prefiere ahorrar memoria o maximizar el uso de memoria de video disponible para mejorar el rendimiento. El modo agresivo puede afectar al rendimiento de otras aplicaciones, como a los programas de grabación. - + Skip CPU Inner Invalidation Saltar invalidación interna de la CPU - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. Omite ciertas invalidaciones de caché durante las actualizaciones de memoria, reduciendo el uso de la CPU y mejorando la latencia. Esto puede causar bloqueos leves. - + + Anti-Flicker + Antiparpadeo + + + + Forces GPU fence callbacks to wait for submitted GPU work. +Use with Fast GPU mode, to avoid flicker with lower performance impact. + Fuerza a las funciones de devolución de llamada de la GPU a esperar a que se envíen las tareas a la GPU. +Úsalo con el modo de GPU rápida para evitar el parpadeo con un menor impacto en el rendimiento. + + + VSync Mode: Modo VSync: - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -886,12 +898,12 @@ Mailbox ofrece menor latencia que FIFO y evita el tearing, aunque puede perder a Immediate (sin sincronización) muestra los fotogramas tan pronto están disponibles, pero puede generar desgarros de imagen. - + Sync Memory Operations Sincronizar operaciones de memoria - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. @@ -900,44 +912,44 @@ Esta opción arregla errores en los juegos, pero puede afectar negativamente al Los juegos con Unreal Engine 4 son con frecuencia los que muestran cambios significantes en esto. - + Enable asynchronous presentation (Vulkan only) Activar presentación asíncrona (solo Vulkan) - + Slightly improves performance by moving presentation to a separate CPU thread. Mejora el rendimiento ligeramente al usar un hilo de CPU adicional para la presentación. - + Force maximum clocks (Vulkan only) Forzar relojes al máximo (solo Vulkan) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. Ejecuta los procesos en segundo plano mientras se espera a las instrucciones gráficas para evitar que la GPU reduzca su velocidad de reloj. - + Anisotropic Filtering: Filtrado anisotrópico: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. Controla la calidad del renderizado de texturas en ángulos oblicuos. Es seguro configurarlo en 16x en la mayoría de las GPU. - + GPU Mode: Modo de la GPU: - + Controls the GPU emulation mode. Most games render fine with Fast or Balanced modes, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. @@ -946,56 +958,56 @@ La mayoridad de juegos renderizarán bien con el modo rápido o balanceado, pero Las partículas tienden a representarse correctamente solo con el modo preciso. - + DMA Accuracy: Precisión de DMA: - + Controls the DMA precision accuracy. Safe precision fixes issues in some games but may degrade performance. Controla la precisión del DMA. La opción de precisión segura corrige errores en algunos juegos, pero puede reducir el rendimiento - + Enable asynchronous shader compilation Activa la compilación de shaders asincrónicos - + May reduce shader stutter. Puede reducir los tirones causados por la carga de sombreadores. - + Fast GPU Time Tiempo rápido de la GPU - + Overclocks the emulated GPU to increase dynamic resolution and render distance. Use 256 for maximal performance and 512 for maximal graphics fidelity. Overcloquea la GPU emulada para aumentar la resolución dinámica y la distancia de renderizado. Use 256 para el máximo rendimiento y 512 para la máxima fidelidad gráfica. - + GPU Unswizzle Desentrelazado de la GPU - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. Acelera la decodificación de texturas 3D BCn mediante computación de la GPU. Desactívela si experimenta problemas o fallos gráficos. - + GPU Unswizzle Max Texture Size Tamaño máximo de textura de desentrelazado de la GPU - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. @@ -1004,46 +1016,46 @@ Aunque la GPU es más rápida para texturas medianas y grandes, la CPU puede ser Ajuste este valor para encontrar el equilibrio entre la aceleración de la GPU y la sobrecarga de la CPU. - + GPU Unswizzle Stream Size Tamaño del flujo de desentrelazado de la GPU - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. Establece la cantidad máxima de datos de textura (en MiB) procesados ​​por cada fotograma. Valores más altos pueden reducir la distorsión durante la carga de texturas, pero pueden afectar a la consistencia de los fotogramas. - + GPU Unswizzle Chunk Size Tamaño del trozo de desentrelazado de la GPU - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. Determina la cantidad de cortes de profundidad procesados ​​en un solo envío. Aumentar este valor puede mejorar el rendimiento en una GPU de gama alta, pero puede causar tirones y problemas en los tiempos de respuesta en hardware más modesto. - + Use Vulkan pipeline cache Usar caché de canalización de Vulkan - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. Activa la caché de canalización específica del fabricante. Esta opción puede mejorar los tiempos de cargas de sombreadores en el caso de que el controlador de Vulkan no lo almacene internamente. - + Enable Compute Pipelines (Intel Vulkan Only) Activar la canalizaciones de cómputo (solo Intel Vulkan) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. @@ -1052,184 +1064,194 @@ Este ajuste solo existe para los controladores propietarios de Intel y puede cau En los demás controladores, la canalización de cómputo siempre está activada. - + Enable Reactive Flushing Activar limpieza reactiva - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. Usa limpieza de memoria reactiva en vez de predictiva, permitiendo una sincronización de memoria más precisa. - + Sync to framerate of video playback Sincronizar a fotogramas de reproducción de vídeo - + Run the game at normal speed during video playback, even when the framerate is unlocked. Ejecuta el juego a velocidad normal durante la reproducción de vídeos, incluso cuando no hay límite de fotogramas. - + Barrier feedback loops Bucles de feedback de barrera - + Improves rendering of transparency effects in specific games. Mejora la renderización de los efectos de transparencia en ciertos juegos. - + Enable buffer history Activar el historial del búfer - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. Habilita el acceso a estados de búfer anteriores. Esta opción puede mejorar la calidad de renderizado y la consistencia en el rendimiento de algunos juegos. - + Fix bloom effects Arreglar efectos de resplandor - + Removes bloom in Burnout. Elimina el resplandor en Burnout. - + Enable Legacy Rescale Pass Activar el pase de reescalado heredado - + May fix rescale issues in some games by relying on behavior from the previous implementation. Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. Puede arreglar los problemas de reescalado en algunos juegos confiando en el comportamiento de la implementación anterior. Solución alternativa de comportamiento heredado que corrige los artefactos de línea de la GPU AMD y el parpadeo de texturas gris de la GPU Nvidia en Luigis Mansion 3. - + Extended Dynamic State Estado dinámico extendido - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. Controla el número de funciones que pueden ser usadas en el Estado Dinámico Extendido. Números más altos permiten úsar más funciones y pueden aumentar el rendimiento, pero tambén pueden causar errores gráficos. - + Vertex Input Dynamic State Estado dinámico de entrada de vértices - + Enables vertex input dynamic state feature for better quality and performance. Activa la función de estado dinámico de entrada de vértices para una mejor calidad y rendimiento. - + Sample Shading Sombreado de muestra - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. Permite que el sombreador de fragmentos se ejecute por muestra en un fragmento multimuestreado, en lugar de una sola vez por fragmento. Mejora la calidad de los gráficos a costa de rendimiento. Los valores más altos mejoran la calidad, pero reducen el rendimiento. - + RNG Seed Semilla de GNA - + Controls the seed of the random number generator. Mainly used for speedrunning. Controla la semilla del generador de números aleatorios. Usado principalmente para speedrunning. - + Device Name Nombre del dispositivo - + The name of the console. El nombre de la consola - + + Homebrew Args + Argumentos Homebrew + + + + Command-line arguments passed to homebrew at launch (e.g. -noglsl). + Argumentos en la línea de comandos pasados al homebrew al ser lanzado (p.e. -noglsl). + + + Custom RTC Date: Fecha Personalizada RTC: - + This option allows to change the clock of the console. Can be used to manipulate time in games. Esta opción permite cambiar el reloj de la consola. Puede ser usado para manipular el tiempo en juegos. - + The number of seconds from the current unix time Número de segundos de la hora actual de Unix - + Language: Idioma: - + This option can be overridden when region setting is auto-select Esta opción puede ser reemplazada cuando la configuración de región está en selección automática. - + Region: Región: - + The region of the console. La Región de la Consola. - + Time Zone: Zona horaria: - + The time zone of the console. La zona horaria de la consola. - + Sound Output Mode: Método de salida de sonido: - + Console Mode: Modo consola: - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. @@ -1238,1030 +1260,1040 @@ Los juegos cambiarán su resolución, detalles y compatibilidad con los mandos s Configurar el modo portátil puede mejorar el rendimiento en sistemas de gama baja. - + Prompt for user profile on boot Solicitud para perfil de usuario al arrancar - + Useful if multiple people use the same PC. Útil si múltiples personas usan la misma PC - + Pause when not in focus Pausar cuando la ventana no esté activa - + Pauses emulation when focusing on other windows. - Pausa la emulación cuando esta activa otra ventana diferente. + Pausa la emulación cuando otra ventana diferente está activa. - + Confirm before stopping emulation Confirmar detención - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. Anula las solicitudes de confirmación para detener la emulación. Al habilitar esta opción, se omiten dichas solicitudes y se sale directamente de la emulación. - + Hide mouse on inactivity Ocultar el cursor por inactividad. - + Hides the mouse after 2.5s of inactivity. Oculta el mouse después de 2.5s de inactividad. - + Disable controller applet Desactivar applet de mandos - + Forcibly disables the use of the controller applet in emulated programs. When a program attempts to open the controller applet, it is immediately closed. Desactiva forzosamente el uso del applet del controlador en programas emulados. Cuando un programa intenta abrir el applet del controlador, se cierra inmediatamente. - + Check for updates Buscar actualizaciones - + Whether or not to check for updates upon startup. Si se deben buscar actualizaciones al iniciar o no. - + Enable Gamemode Activar Modo Juego - + Force X11 as Graphics Backend Forzar X11 como motor gráfico - + Custom frontend Interfaz personalizada - + Real applet Applet real - + Never Nunca - + On Load Al cargar - + Always Siempre - + CPU CPU - + GPU GPU - + CPU Asynchronous CPU asíncrona - + Uncompressed (Best quality) Sin compresión (Mejor calidad) - + BC1 (Low quality) BC1 (Calidad baja) - + BC3 (Medium quality) BC3 (Calidad media) - - + + Auto Auto - + 30 FPS 30 FPS - + 60 FPS 60 FPS - + 90 FPS 90 FPS - + 120 FPS 120 FPS - + Conservative Conservativo - + Aggressive Agresivo - + Vulkan Vulkan - + OpenGL GLSL OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) OpenGL GLASM (Ensamblado de sombreadores, solo NVIDIA) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) OpenGL SPIR-V (Experimental, solo AMD/Mesa) - + Null Ninguno - + Fast Rápido - + Balanced Balanceado - - + + Accurate Preciso - - + + Default Predeterminado - + Unsafe (fast) Inseguro (rápido) - + Safe (stable) Seguro (estable) - + Unsafe Impreciso - + Paranoid (disables most optimizations) Paranoico (Deshabilita la mayoría de optimizaciones) - + Debugging Depuración - + Dynarmic DynARMic - + NCE NCE - + Borderless Windowed Ventana sin bordes - + Exclusive Fullscreen Pantalla completa - + No Video Output Sin salida de vídeo - + CPU Video Decoding Decodificación de vídeo en la CPU - + GPU Video Decoding (Default) Decodificación de vídeo en GPU (Por defecto) - + 0.25X (180p/270p) [EXPERIMENTAL] 0.25X (180p/270p) [EXPERIMENTAL] - + 0.5X (360p/540p) [EXPERIMENTAL] x0,5 (360p/540p) [EXPERIMENTAL] - + 0.75X (540p/810p) [EXPERIMENTAL] x0,75 (540p/810p) [EXPERIMENTAL] - + 1X (720p/1080p) x1 (720p/1080p) - + 1.25X (900p/1350p) [EXPERIMENTAL] 1.25X (900p/1350p) [EXPERIMENTAL] - + 1.5X (1080p/1620p) [EXPERIMENTAL] x1,5 (1080p/1620p) [EXPERIMENTAL] - + 2X (1440p/2160p) x2 (1440p/2160p) - + 3X (2160p/3240p) x3 (2160p/3240p) - + 4X (2880p/4320p) x4 (2880p/4320p) - + 5X (3600p/5400p) x5 (3600p/5400p) - + 6X (4320p/6480p) x6 (4320p/6480p) - + 7X (5040p/7560p) x7 (5040p/7560p) - + 8X (5760p/8640p) x8 (5760p/8640p) - + Nearest Neighbor Vecino más próximo - + Bilinear Bilineal - + Bicubic Bicúbico - + Gaussian Gaussiano - + Lanczos Lanczos - + ScaleForce ScaleForce - + AMD FidelityFX Super Resolution Super Resolución AMD FidelityFX - + Area Área - + MMPX MMPX - + Zero-Tangent Tangente cero - + B-Spline Ranura B - + Mitchell Mitchell - + Spline-1 Spline-1 - - + + Snapdragon Game Super Resolution + Super resolución de juego Snapdragon + + + + Snapdragon Game Super Resolution EdgeDir + Filo direcorio de super resolución de juego Snapdragon + + + + None Ninguno - + FXAA FXAA - + SMAA SMAA - + Default (16:9) Predeterminado (16:9) - + Force 4:3 Forzar 4:3 - + Force 21:9 Forzar 21:9 - + Force 16:10 Forzar 16:10 - + Stretch to Window Estirar a la ventana - + Automatic Automático - + 2x x2 - + 4x x4 - + 8x x8 - + 16x x16 - + 32x 32x - + 64x 64x - + Japanese (日本語) Japonés (日本語) - + American English Inglés estadounidense - + French (français) Francés (français) - + German (Deutsch) Alemán (deutsch) - + Italian (italiano) Italiano (italiano) - + Spanish (español) Español - + Chinese Chino - + Korean (한국어) Coreano (한국어) - + Dutch (Nederlands) Holandés (nederlands) - + Portuguese (português) Portugués (português) - + Russian (Русский) Ruso (Русский) - + Taiwanese Taiwanés - + British English Inglés británico - + Canadian French Francés canadiense - + Latin American Spanish Español latinoamericano - + Simplified Chinese Chino simplificado - + Traditional Chinese (正體中文) Chino tradicional (正體中文) - + Brazilian Portuguese (português do Brasil) Portugués brasileño (português do Brasil) - + Polish (polska) Polaco (polska) - + Thai (แบบไทย) Tailandés (แบบไทย) - - + + Japan Japón - + USA EEUU - + Europe Europa - + Australia Australia - + China China - + Korea Corea - + Taiwan Taiwán - + Auto (%1) Auto select time zone Auto (%1) - + Default (%1) Default time zone Predeterminada (%1) - + CET CET - + CST6CDT CST6CDT - + Cuba Cuba - + EET EET - + Egypt Egipto - + Eire Eire - + EST EST - + EST5EDT EST5EDT - + GB GB - + GB-Eire GB-Eire - + GMT GMT - + GMT+0 GMT+0 - + GMT-0 GMT-0 - + GMT0 GMT0 - + Greenwich Greenwich - + Hongkong Hongkong - + HST HST - + Iceland Islandia - + Iran Irán - + Israel Israel - + Jamaica Jamaica - + Kwajalein Kwajalein - + Libya Libia - + MET MET - + MST MST - + MST7MDT MST7MDT - + Navajo Navajo - + NZ NZ - + NZ-CHAT NZ-CHAT - + Poland Polonia - + Portugal Portugal - + PRC PRC - + PST8PDT PST8PDT - + ROC ROC - + ROK ROK - + Singapore Singapur - + Turkey Turquía - + UCT UCT - + Universal Universal - + UTC UTC - + W-SU W-SU - + WET WET - + Zulu Zulú - + Mono Mono - + Stereo Estéreo - + Surround Envolvente - + 4GB DRAM (Default) 4GB DRAM (Por defecto) - + 6GB DRAM (Unsafe) 6GB DRAM (Inseguro) - + 8GB DRAM 8GB DRAM - + 10GB DRAM (Unsafe) 10GB DRAM (Inseguro) - + 12GB DRAM (Unsafe) 12GB DRAM (Inseguro) - + Docked Sobremesa - + Handheld Portátil - - + + Off Apagado - + Boost (1700MHz) Boost (1700MHz) - + Fast (2000MHz) Rápido (2000MHz) - + Always ask (Default) Preguntar siempre (Por defecto) - + Only if game specifies not to stop Solo si el juego pide no ser cerrado - + Never ask Nunca preguntar - - + + Medium (256) Medio (256) - - + + High (512) Alto (512) - + Very Small (16 MB) Muy pequeño (16 MB) - + Small (32 MB) Pequeño (32 MB) - + Normal (128 MB) Normal (128 MB) - + Large (256 MB) Grande (256 MB) - + Very Large (512 MB) Muy grande (512 MB) - + Very Low (4 MB) Muy bajo (4 MB) - + Low (8 MB) Bajo (8 MB) - + Normal (16 MB) Normal (16 MB) - + Medium (32 MB) Medio (32 MB) - + High (64 MB) Alto (64 MB) - + Very Low (32) Muy bajo (32) - + Low (64) Bajo (64) - + Normal (128) Normal (128) - + Disabled Desactivado - + ExtendedDynamicState 1 ModoDynamicoExtendido 1 - + ExtendedDynamicState 2 ModoDynamicoExtendido 2 - + ExtendedDynamicState 3 ModoDynamicoExtendido 3 - + Tree View Vista en árbol - + Grid View Vista en cuadrícula @@ -2758,7 +2790,7 @@ Cuando un programa intenta abrir el applet del controlador, se cierra inmediatam Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. - Permite a Eden comprobar si el entorno de Vulkan funciona cuando el programa se inicia. Desactive esto si está causando problemas a programas externos que ven a Eden. + Permite a Eden comprobar si el entorno de Vulkan funciona cuando el programa se inicia. Desactiva esto si está causando problemas a programas externos que ven a Eden. @@ -3328,33 +3360,33 @@ Would you like to delete the old save data? Color de fondo: - + % - FSR sharpening percentage (e.g. 50%) + FSR/SGSR sharpening percentage (e.g. 50%) % - + Off Desactivado - + VSync Off VSync Desactivado - + Recommended Recomendado - + On Activado - + VSync On VSync Activado @@ -4483,7 +4515,7 @@ Para invertir los ejes, mueve primero el joystick de manera vertical, y luego ho - + Configure Configurar @@ -4514,7 +4546,7 @@ Para invertir los ejes, mueve primero el joystick de manera vertical, y luego ho - + Test Probar @@ -4529,77 +4561,77 @@ Para invertir los ejes, mueve primero el joystick de manera vertical, y luego ho Eliminar servidor - + %1:%2 %1:%2 - - - - - - + + + + + + Eden Eden - + Port number has invalid characters El número del puerto tiene caracteres que no son válidos - + Port has to be in range 0 and 65353 El puerto debe estar en un rango entre 0 y 65353 - + IP address is not valid Dirección IP no válida - + This UDP server already exists Este servidor UDP ya existe - + Unable to add more than 8 servers No es posible añadir más de 8 servidores - + Testing Probando - + Configuring Configurando - + Test Successful Prueba existosa - + Successfully received data from the server. Se han recibido con éxito los datos del servidor. - + Test Failed Prueba fallida - + Could not receive valid data from the server.<br>Please verify that the server is set up correctly and the address and port are correct. No se han podido recibir datos válidos del servidor.<br>Por favor, verifica que el servidor esté configurado correctamente y que la dirección y el puerto sean correctos. - + UDP Test or calibration configuration is in progress.<br>Please wait for them to finish. La prueba de UDP o la configuración de la calibración está en curso.<br>Por favor, espera a que termine el proceso. @@ -4784,57 +4816,57 @@ Los valores actuales son %1% y %2% respectivamente. Algunos ajustes solo están disponibles cuando no se estén ejecutando los juegos. - + Add-Ons Complementos - + System Sistema - + CPU CPU - + Graphics Gráficos - + Adv. Graphics Gráficos avanz. - + Ext. Graphics Extensiones Gráficas - + Audio Audio - + Input Profiles Perfiles de entrada - + Network Red - + Applets Applets - + Properties Propiedades @@ -5534,7 +5566,7 @@ Arrastre los puntos para cambiar de posición, o haga doble clic en las celdas d Show Add-Ons Column - Mostrar columna de complementos + Mostrar la columna de complementos @@ -5849,7 +5881,7 @@ Arrastre los puntos para cambiar de posición, o haga doble clic en las celdas d Importar todos los datos en este directorio. Esto puede tomar un tiempo, ¡y borrará TODOS LOS DATOS EXISTENTES! - + Calculating... Calculando... @@ -6053,50 +6085,50 @@ Por favor, vaya a Configuración -> Sistema -> Red y selecciona una interf GRenderWindow - - + + OpenGL not available! ¡OpenGL no está disponible! - + OpenGL shared contexts are not supported. Los contextos compartidos de OpenGL no son compatibles. - + Eden has not been compiled with OpenGL support. Eden no ha sido compilado con soporte para OpenGL. - - - + + + Error while initializing OpenGL! ¡Error al inicializar OpenGL! - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. Tu GPU no soporta OpenGL, o no tienes instalados los últimos controladores gráficos. - + Error while initializing OpenGL 4.6! ¡Error al iniciar OpenGL 4.6! - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 Tu GPU no soporta OpenGL 4.6, o no tienes instalado el último controlador de la tarjeta gráfica.<br><br>GL Renderer:<br>%1 - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 Es posible que la GPU no soporte una o más extensiones necesarias de OpenGL . Por favor, asegúrate de tener los últimos controladores de la tarjeta gráfica.<br><br>GL Renderer:<br>%1<br><br>Extensiones no soportadas:<br>%2 - + This build doesn't have OpenGL support. Esta compilación no tiene soporte para OpenGL. @@ -6104,279 +6136,282 @@ Por favor, vaya a Configuración -> Sistema -> Red y selecciona una interf GameList - + &Add New Game Directory &Añadir un nuevo directorio de juego - + Favorite Favorito - + Start Game Iniciar juego - + Start Game without Custom Configuration Iniciar juego sin la configuración personalizada - + Open Save Data Location Abrir ubicación de los archivos de guardado - + Open Mod Data Location Abrir ubicación de los mods - + Open Transferable Pipeline Cache Abrir caché de canalización de shaders transferibles - + Link to Ryujinx Enlace a Ryujinx - + Remove Eliminar - + Remove Installed Update Eliminar la actualización instalada - + Remove All Installed DLC Eliminar todos los DLC instalados - + Remove Custom Configuration Eliminar la configuración personalizada - + Remove Cache Storage Quitar almacenamiento de caché - + Remove OpenGL Pipeline Cache Eliminar caché de canalización de OpenGL - + Remove Vulkan Pipeline Cache Eliminar caché de canalización de Vulkan - + Remove All Pipeline Caches Eliminar todas las cachés de canalización - + Remove All Installed Contents Eliminar todo el contenido instalado - + Manage Play Time Gestionar tiempo de juego - + Edit Play Time Data Editar los datos del tiempo de juego - + Remove Play Time Data Eliminar información del tiempo de juego - - + + Dump RomFS Volcar RomFS - + Dump RomFS to SDMC Volcar RomFS a SDMC - + Verify Integrity Verificar integridad - + Copy Title ID to Clipboard Copiar la ID del título al portapapeles - + Navigate to GameDB entry Ir a la sección de bases de datos del juego - + Create Shortcut Crear acceso directo - + Add to Desktop Añadir al escritorio - + Add to Applications Menu Añadir al menú de aplicaciones - + Configure Game Configurar juego - + Scan Subfolders Escanear subdirectorios - + Remove Game Directory Eliminar directorio de juegos - + ▲ Move Up ▲ Mover hacia arriba - + ▼ Move Down ▼ Mover hacia abajo - + Open Directory Location Abrir ubicación del directorio - + Clear Limpiar - - - Name - Nombre - - - - Compatibility - Compatibilidad - - - - Add-ons - Complementos - - - - File type - Tipo de archivo - - - - Size - Tamaño - - - - Play time - Tiempo de juego - GameListItemCompat - + Ingame En juego - + Game starts, but crashes or major glitches prevent it from being completed. El juego se inicia, pero se bloquea o se producen fallos importantes que impiden completarlo. - + Perfect Perfecta - + Game can be played without issues. El juego se puede jugar sin problemas. - + Playable Jugable - + Game functions with minor graphical or audio glitches and is playable from start to finish. El juego tiene algunos errores gráficos o de sonido, pero se puede jugar de principio a fin. - + Intro/Menu Inicio/Menu - + Game loads, but is unable to progress past the Start Screen. El juego se ejecuta, pero no puede avanzar de la pantalla de inicio. - + Won't Boot No funciona - + The game crashes when attempting to startup. El juego se bloquea al intentar iniciar. - + Not Tested Sin testear - + The game has not yet been tested. El juego todavía no ha sido testeado todavía. + + GameListModel + + + Name + Nombre + + + + Compatibility + Compatibilidad + + + + Add-ons + Complementos + + + + File type + Tipo de archivo + + + + Size + Tamaño + + + + Play time + Tiempo de juego + + GameListPlaceholder - + Double-click to add a new folder to the game list Haz doble clic para agregar un nuevo directorio a la lista de juegos. @@ -6384,17 +6419,17 @@ Por favor, vaya a Configuración -> Sistema -> Red y selecciona una interf GameListSearchField - + %1 of %n result(s) %1 de %n resulto(s)%1 de %n resultado(s)%1 de %n resultado(s) - + Filter: Búsqueda: - + Enter pattern to filter Introduce un patrón para buscar @@ -6890,772 +6925,777 @@ Mensaje de depuración: Modo de la lista de &juegos - + Game &Icon Size Tamaño del &icono de los juegos - + Reset Window Size to &720p Reiniciar el tamaño de la ventana a &720p - + Reset Window Size to 720p Reiniciar el tamaño de la ventana a 720p - + Reset Window Size to &900p Reiniciar el tamaño de la ventana a &900p - + Reset Window Size to 900p Reiniciar el tamaño de la ventana a 900p - + Reset Window Size to &1080p Reiniciar el tamaño de la ventana a &1080p - + Reset Window Size to 1080p Reiniciar el tamaño de la ventana a 1080p - + &Multiplayer &Multijugador - + &Tools &Herramientas - + Am&iibo Am&iibo - + Launch &Applet Ejecutar &Applet - + &TAS &TAS - + &Create Home Menu Shortcut &Crear un acceso directo al menú de inicio - + Install &Firmware Instalar &Firmware - + &Help &Ayuda - + &Install Files to NAND... &Instalar archivos en NAND... - + L&oad File... C&argar archivo... - + Load &Folder... Cargar &carpeta - + E&xit S&alir - - + + &Pause &Pausar - + &Stop &Detener - + &Verify Installed Contents &Verificar los contenidos instalados - + &About Eden &Acerca de Eden - + Single &Window Mode Modo &ventana - + Con&figure... Con&figurar... - + Ctrl+, Ctrl+, - + Enable Overlay Display Applet Activar applet de visualización superpuesta - + Show &Filter Bar Mostrar barra de &búsqueda - + Show &Status Bar Mostrar barra de &estado - + Show Status Bar Mostrar barra de estado - + &Browse Public Game Lobby &Buscar en la sala de juegos públicos - + &Create Room &Crear sala - + &Leave Room &Abandonar sala - + &Direct Connect to Room &Conexión directa a una sala - + &Show Current Room &Mostrar sala actual - + F&ullscreen P&antalla completa - + &Restart &Reiniciar - + Load/Remove &Amiibo... Cargar/Eliminar &Amiibo... - + &Report Compatibility &Reporte de compatibilidad - + Open &Mods Page Abrir página de &mods - + Open &Quickstart Guide Abrir guía de &inicio rápido - + &FAQ &Preguntas frecuentes - + &Capture Screenshot &Captura de pantalla - + &Album &Álbum - + &Set Nickname and Owner &Darle nombre y propietario - + &Delete Game Data &Borrar datos de juego - + &Restore Amiibo &Restaurar Amiibo - + &Format Amiibo &Formatear Amiibo - + &Mii Editor &Editor de Mii - + &Configure TAS... &Configurar TAS... - + Configure C&urrent Game... Configurar el j&uego actual... - - + + &Start &Iniciar - + &Reset &Reiniciar - - + + R&ecord G&rabar - + Open &Controller Menu Abrir el menú de &mandos - + Install Decryption &Keys Instalar &llaves de desencriptación - + &Home Menu &Menú de inicio - + &Desktop &Escritorio - + &Application Menu &Menú de la aplicación - + &Root Data Folder Carpeta de datos &raíz - + &NAND Folder Carpeta &NAND - + &SDMC Folder Carpeta &SDMC - + &Mod Folder Carpeta de &mods - + &Log Folder Carpeta de &registros - + From Folder Desde una carpeta - + From ZIP Desde un archivo ZIP - + &Eden Dependencies &Dependencias de Eden - + &Data Manager Gestor de &datos - + &Tree View Vista en &árbol - + &Grid View Vista en &cuadrícula - + Game Icon Size Tamaño del icono de los juegos - - + + None Nada - + Show Game &Name Mostrar el &nombre del juego - + Show &Performance Overlay Mostrar superposición de &rendimiento - + + &Carousel View + Vista de &carrusel + + + Small (32x32) Pequeño (32x32) - + Standard (64x64) Estandar (64x64) - + Large (128x128) Grande (128x128) - + Full Size (256x256) Tamaño completo (256x256) - + Broken Vulkan Installation Detected Detectada instalación fallida de Vulkan - + Vulkan initialization failed during boot. La inicialización de Vulkan falló durante el arranque. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping Ejecutando un juego - + Loading Web Applet... Cargando applet web... - - + + Disable Web Applet Desactivar applet web - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) Desactivar el applet web puede causar comportamientos inesperados y debería solo ser usado con Super Mario 3D All-Stars. ¿Está seguro de que quiere desactivar el applet web? (Puede ser activado en las ajustes de depuración.) - + The amount of shaders currently being built La cantidad de sombreadores que se están contruyendo actualmente - + The current selected resolution scaling multiplier. El multiplicador de escalado de resolución seleccionado actualmente. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. Velocidad de emulación actual. Los valores superiores o inferiores al 100% indican que la emulación se ejecuta más rápida o más lenta que en una Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. La cantidad de fotogramas por segundo que muestra el juego actualmente. Esto varía según el juego y la escena. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Tiempo empleado en emular un fotograma de la Switch, sin contar la limitación de fotogramas ni la sincronización vertical. Para una emulación a máxima velocidad, debería ser de 16,67 ms como máximo. - + Unmute Desilenciar - + Mute Silenciar - + Reset Volume Restablecer volumen - + &Clear Recent Files &Limpiar archivos recientes - + &Continue &Continuar - + Warning: Outdated Game Format Advertencia: formato del juego obsoleto - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. - Está usando el formato de directorio ROM deconstruido para este juego, el cual es un formato obsoleto que ha sido reemplazado por otros como NCA, NAX, XCI o NSP. Los directorios ROM deconstruidos carecen de iconos, metadatos y compatibilidad con actualizaciones.<br>Para obtener una explicación de los distintos formatos de la Switch compatibles con Eden, consulte nuestro manual de usuario. Este mensaje no volverá a aparecer de nuevo. + Estás usando el formato de directorio ROM deconstruido para este juego, el cual es un formato obsoleto que ha sido reemplazado por otros como NCA, NAX, XCI o NSP. Los directorios ROM deconstruidos carecen de iconos, metadatos y compatibilidad con actualizaciones.<br>Para obtener una explicación de los distintos formatos de la Switch compatibles con Eden, consulte nuestro manual de usuario. Este mensaje no volverá a aparecer de nuevo. - - + + Error while loading ROM! ¡Error al cargar la ROM! - + The ROM format is not supported. El formato de la ROM no está soportado. - + An error occurred initializing the video core. Se produjo un error al inicializar el núcleo de video. - + Eden has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-mirror.github.io/help/reference/log-files/'>How to Upload the Log File</a>. Eden ha detectado un error al ejecutar el núcleo de vídeo. Esto suele deberse por usar controladores de la GPU obsoletos, incluidos los integrados. Consulte el registro para obtener más información. Para más información sobre cómo acceder al archivo de registro, por favor consulte la siguiente página: <a href='https://yuzu-mirror.github.io/help/reference/log-files/'>Cómo subir el archivo de registro</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. ¡Error al cargar la ROM! %1 - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. %1<br>Por favor vuelva a volcar sus archivos, o pida ayuda en Discord/Stoat. - + An unknown error occurred. Please see the log for more details. Ha ocurrido un error desconocido. Por favor mire el registro para más detalles. - + (64-bit) (64-bit) - + (32-bit) (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Closing software... Cerrando el programa... - + Save Data Datos de guardado - + Mod Data Datos del mod - + Error Opening %1 Folder Error abriendo la carpeta %1 - - + + Folder does not exist! ¡La carpeta no existe! - + Remove Installed Game Contents? ¿Eliminar el contenido instalado del juego? - + Remove Installed Game Update? ¿Eliminar la actualización instalada del juego? - + Remove Installed Game DLC? ¿Eliminar el contenido descargable instalado del juego? - + Remove Entry Eliminar entrada - + Delete OpenGL Transferable Shader Cache? ¿Desea eliminar el caché transferible de sombreadores de OpenGL? - + Delete Vulkan Transferable Shader Cache? ¿Desea eliminar el caché transferible de sombreadores de Vulkan? - + Delete All Transferable Shader Caches? ¿Desea eliminar todo el caché transferible de sombreadores? - + Remove Custom Game Configuration? ¿Borrar configuración personalizada del juego? - + Remove Cache Storage? ¿Borrar el almacenamiento de caché? - + Remove File Eliminar archivo - + Remove Play Time Data Eliminar datos del tiempo de juego - + Reset play time? ¿Reiniciar el tiempo de juego? - - + + RomFS Extraction Failed! ¡Fallo al extraer el RomFS! - + There was an error copying the RomFS files or the user cancelled the operation. Ocurrió un error al copiar los archivos de la RomFS o el usuario canceló la operación. - + Full Completo - + Skeleton Esqueleto - + Select RomFS Dump Mode Seleccione el método de volcado del RomFS - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. Por favor seleccione como quiere que sea volcada la RomFS.<br>Completa copiará todos los archivos en el nuevo directorio mientras que <br>esqueleto solo creará la estructura de los directorios. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root No hay suficiente espacio libre en %1 para extraer la RomFS. Por favor libera espacio o selecciona un directorio de volcado diferente en Emulación > Configurar > Sistema > Sistema de archivos > Volcado raiz - + Extracting RomFS... Extrayendo el RomFS... - - + + Cancel Cancelar - + RomFS Extraction Succeeded! ¡La extracción del RomFS ha sido un éxito! - + The operation completed successfully. La operación se completó con éxito. - + Error Opening %1 Error al abrir %1 - + Select Directory Seleccionar directorio - + Properties Propiedades - + The game properties could not be loaded. No se pudieron cargar las propiedades del juego. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Ejecutable de Switch (%1);;Todos los archivos (*.*) - + Load File Cargar archivo - + Open Extracted ROM Directory Abrir directorio de la ROM extraída - + Invalid Directory Selected Seleccionado directorio inválido - + The directory you have selected does not contain a 'main' file. El directorio seleccionado no contiene un archivo 'main'. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) Archivo de Switch instalable (*.nca *.nsp *.xci);;Archivo de contenido de Nintendo (*.nca);;Paquete de envío de Nintendo (*.nsp);;Imagen de cartucho NX (*.xci) - + Install Files Instalar archivos - + %n file(s) remaining %n archivo(s) restantes%n archivo(s) restantes%n archivo(s) restantes - + Installing file "%1"... Instalando el archivo "%1"... - - + + Install Results Resultados de la instalación - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. Para evitar posibles conflictos, desaconsejamos instalar juegos base en la NAND. Por favor, use solo esta función para instalar actualizaciones y contenido descargable. - + %n file(s) were newly installed %n archivo(s) se instalaron recientemente @@ -7664,7 +7704,7 @@ Por favor, use solo esta función para instalar actualizaciones y contenido desc - + %n file(s) were overwritten %n archivo(s) fueron sobreescritos @@ -7673,7 +7713,7 @@ Por favor, use solo esta función para instalar actualizaciones y contenido desc - + %n file(s) failed to install fallo al instalar %n archivo(s) @@ -7682,361 +7722,314 @@ Por favor, use solo esta función para instalar actualizaciones y contenido desc - + System Application Aplicación del sistema - + System Archive Archivo del sistema - + System Application Update Actualización de la aplicación del sistema - + Firmware Package (Type A) Paquete de firmware (Tipo A) - + Firmware Package (Type B) Paquete de firmware (Tipo B) - + Game Juego - + Game Update Actualización del juego - + Game DLC Contenido descargable del juego - + Delta Title Título Delta - + Select NCA Install Type... Seleccione el tipo de instalación NCA... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) Seleccione el tipo de título en el que desea instalar este NCA: (En la mayoría de los casos, la opción predeterminada "Juego" es suficiente). - + Failed to Install Fallo al instalar - + The title type you selected for the NCA is invalid. El tipo de título que ha seleccionado para el NCA es inválido. - + File not found Archivo no encontrado - + File "%1" not found Archivo "%1" no encontrado - + OK Aceptar - + Function Disabled Función desactivada - + Compatibility list reporting is currently disabled. Check back later! Los informes de la lista de compatibilidad están deshabilitados. ¡Vuelva más tarde! - + Error opening URL Error abriendo URL - + Unable to open the URL "%1". No se pudo abrir URL "%1". - + TAS Recording Grabación TAS - + Overwrite file of player 1? ¿Sobrescribir el archivo del jugador 1? - + Invalid config detected Detectada configuración inválida - + Handheld controller can't be used on docked mode. Pro controller will be selected. El mando del modo portátil no puede usarse en el modo sobremesa. El mando pro será seleccionado en su lugar. - - + + Amiibo Amiibo - - + + The current amiibo has been removed El amiibo actual fue borrado - + Error Error - - + + The current game is not looking for amiibos El juego actual no está buscando amiibos - + Amiibo File (%1);; All Files (*.*) Archivo Amiibo (%1);; Todos los archivos (*.*) - + Load Amiibo Cargar Amiibo - + Error loading Amiibo data Error al cargar los datos del Amiibo - + The selected file is not a valid amiibo El archivo seleccionado no es un amiibo válido - + The selected file is already on use El archivo seleccionado se encuentra en uso - + An unknown error occurred Ha ocurrido un error desconocido - - - Keys not installed - Claves no instaladas - - - - - Install decryption keys and restart Eden before attempting to install firmware. - Instale las claves de desencriptado y reinicie Eden antes de intentar instalar el firmware. - - - - Select Dumped Firmware Source Location - Seleccionar ubicación fuente del firmware volcado - - - - Select Dumped Firmware ZIP - Seleccionar ZIP del firmware volcado - - - - Zipped Archives (*.zip) - Archivos comprimidos (*.zip) - - - - Firmware cleanup failed - Fallo al limpiar firmware - - - - Failed to clean up extracted firmware cache. -Check write permissions in the system temp directory and try again. -OS reported error: %1 - Fallo al limpiar el cache del firmware extraído. -Compruebe los permisos de escritura en el directorio de temporales del sistema e inténtelo de nuevo. -Error reportado por el sistema operativo: %1 - - - + No firmware available No hay firmware disponible - + Firmware Corrupted Firmware corrompido - + Unknown applet Applet desconocido - + Applet doesn't map to a known value. Applet no asigna a un valor conocido. - + Record not found Grabación no encontrada - + Applet not found. Please reinstall firmware. Apple no encontrado. Por favor vuelva a instalar el firmware. - + Capture Screenshot Captura de pantalla - + PNG Image (*.png) Imagen PNG (*.png) - + TAS state: Running %1/%2 Estado de TAS: Ejecutando %1/%2 - + TAS state: Recording %1 Estado de TAS: Grabando %1 - + TAS state: Idle %1/%2 Estado de TAS: Inactivo %1/%2 - + TAS State: Invalid Estado de TAS: Inválido - + &Stop Running &Parar de ejecutarse - + Stop R&ecording &Parar grabación - + Building: %n shader(s) Construyendo: %n sombreador(es)Construyendo: %n sombreador(es)Construyendo: %n sombreador(es) - + Scale: %1x %1 is the resolution scaling factor Escala: %1x - + Speed: %1% / %2% Velocidad: %1% / %2% - + Speed: %1% Velocidad: %1% - + Game: %1 FPS Juego: %1 FPS - + Frame: %1 ms Fotograma: %1 ms - - - FSR - FSR - - - + NO AA NO AA - + VOLUME: MUTE VOLUMEN: SILENCIADO - + VOLUME: %1% Volume percentage (e.g. 50%) VOLUMEN: %1% - + Derivation Components Missing Componentes de derivación ausentes - + Decryption keys are missing. Install them now? Las claves de desencriptado están ausentes. ¿Desea instalarlas ahora? - + Wayland Detected! ¡Wayland detectado! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -8047,74 +8040,74 @@ Se recomienda usar X11 en su lugar. ¿Le gustaría forzar a usarlo en futuras ejecuciones? - + Use X11 Usar X11 - + Continue with Wayland Continuar con Wayland - + Don't show again No mostrar de nuevo - + Restart Required Reinicio requerido - + Restart Eden to apply the X11 backend. Reinicie Eden para aplicar el motor X11. - + Slow Lento - + Turbo Turbo - + Unlocked Desbloqueado - + Select RomFS Dump Target Seleccione el destinatario para el volcado del RomFS - + Please select which RomFS you would like to dump. Por favor seleccione que RomFS desea volcar. - + Are you sure you want to close Eden? ¿Está seguro de que quiere cerrar Eden? - - - + + + Eden Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. ¿Está seguro de que desea parar la emulación? Cualquier progreso sin guardar se perderá. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? @@ -8182,6 +8175,11 @@ Would you like to bypass this and exit anyway? ScaleForce ScaleForce + + + FSR + FSR + Area @@ -8193,52 +8191,62 @@ Would you like to bypass this and exit anyway? MMPX - + + SGSR + SGSR + + + + SGSR EdgeDir + SGSR EdgeDir + + + Docked Anclado - + Handheld Portátil - + Fast Rápido - + Balanced Equilibrado - + Accurate Preciso - + Vulkan Vulkan - + OpenGL GLSL OpenGL GLSL - + OpenGL SPIRV OpenGL SPIRV - + OpenGL GLASM OpenGL GLASM - + Null Nulo @@ -8281,7 +8289,7 @@ Si quieres limpiar los archivos que se quedaron en el ubicacion de datos anticua %1 - + Data was migrated successfully. Datos se migraron con exito. @@ -9139,47 +9147,47 @@ p, li { white-space: pre-wrap; } %1 esta jugando %2 - + Play Time: %1 Tiempo de juego: %1 - + Never Played Nunca jugado - + Version: %1 Versión: %1 - + Version: 1.0.0 Versión: 1.0.0 - + Installed SD Titles Títulos instalados en la SD - + Installed NAND Titles Títulos instalados en NAND - + System Titles Títulos del sistema - + Add New Game Directory Añadir un nuevo directorio de juego - + Favorites Favoritos @@ -9300,47 +9308,47 @@ p, li { white-space: pre-wrap; } QtCommon::Content - + Game Requires Firmware El juego requiere firmware - + The game you are trying to launch requires firmware to boot or to get past the opening menu. Please <a href='https://yuzu-mirror.github.io/help/quickstart'>dump and install firmware</a>, or press "OK" to launch anyways. El juego que está tratando de abrir requiere firmware para arrancar o para pasar el menú de apertura. Por favor, <a href='https://yuzu-mirror.github.io/help/quickstart'>volcar e instalar el firmware</a>, o presionar "OK" para abrir de todas formas. - + Installing Firmware... Instalando firmware... - - - - - + + + + + Cancel Cancelar - + Firmware Install Failed Instalación de Firmware Fallida. - + Firmware Install Succeeded Firmware instalado exitosamente. - + Firmware integrity verification failed! ¡Error en la verificación de integridad del firmware! - - + + Verification failed for the following files: %1 @@ -9349,209 +9357,244 @@ p, li { white-space: pre-wrap; } %1 - - + + Verifying integrity... Verificando integridad... - - + + Integrity verification succeeded! ¡La verificación de integridad ha sido un éxito! - - + + The operation completed successfully. La operación se completó con éxito. - - + + Integrity verification failed! ¡Verificación de integridad se fallo! - + File contents may be corrupt or missing. Los contenidos del archivo pueden estar corruptos. - + Integrity verification couldn't be performed No se pudo ejecutar la verificación de integridad - + Firmware installation cancelled, firmware may be in a bad state or corrupted. File contents could not be checked for validity. Instalacion de firmware cancellado , firmware podria estar en un mal estado o coruptos. contenidos de el archivo no pudieron ser verificados para validez. - + Select Dumped Keys Location Seleccionar ubicación de origen de los llaves volcados - + Decryption Keys install succeeded Instalación de llaves de descifra salo con exito - + Decryption Keys install failed Instalacion de las llaves de descifra se fallo - + Orphaned Profiles Detected! ¡Se detectaron perfiles huérfanos! - + UNEXPECTED BAD THINGS MAY HAPPEN IF YOU DON'T READ THIS!<br>Eden has detected the following save directories with no attached profile:<br>%1<br><br>The following profiles are valid:<br>%2<br><br>Click "OK" to open your save folder and fix up your profiles.<br>Hint: copy the contents of the largest or last-modified folder elsewhere, delete all orphaned profiles, and move your copied contents to the good profile.<br><br>Still confused? See the <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/Orphaned.md'>help page</a>.<br> ¡PODRÍAN SUCEDER COSAS MALAS E INESPERADAS SI NO LEE ESTO!<br>Eden ha detectado que los siguientes directorios de guardado no tienen perfil asociado:<br>%1<br><br>Los siguientes perfiles son válidos:<br>%2<br><br>Haga clic en "Aceptar" para abrir la carpeta de guardado y arreglar sus perfiles.<br>Consejo: copie el contenido de la carpeta más grande o la última modificada en otro lugar, elimine todos los perfiles huérfanos y mueva el contenido copiado al perfil correcto.<br><br>¿Aún tiene dudas? Consulte la <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/Orphaned.md'>página de ayuda</a>.<br> - + Really clear data? ¿Realmente deseas borrar los datos? - + Important data may be lost! ¡Podrías perder información importante! - + Are you REALLY sure? ¿Estás REALMENTE seguro? - + Once deleted, your data will NOT come back! Only do this if you're 100% sure you want to delete this data. ¡Una vez eliminados, tus datos no podrán recuperarse! Haz esto solo si estás 100% seguro de que deseas borrarlos. - + Clearing... Limpiando... - + Select Export Location Selecciona la Ubicación de Exportación. - + %1.zip %1.zip - - + + + Zipped Archives (*.zip) Archivos comprimidos (*.zip) - + Exporting data. This may take a while... Exportando datos. Esto puede tardar un poco... - + Exporting Exportando - + Exported Successfully Exportación Exitosa. - + Data was exported successfully. Los datos se exportaron correctamente. - + Export Cancelled Exportación cancelada. - + Export was cancelled by the user. La exportación fue cancelada por el usuario. - + Export Failed Exportación Fallida - + Ensure you have write permissions on the targeted directory and try again. Asegúrate de tener permisos de escritura en el directorio seleccionado e inténtalo nuevamente. - + Select Import Location Seleccionar ubicación de importación. - + Import Warning Advertencia al importar datos - + All previous data in this directory will be deleted. Are you sure you wish to proceed? Todos los datos anteriores en este directorio serán eliminados. ¿Estás seguro de que deseas continuar? - + Importing data. This may take a while... Importando datos. Esto puede tomar unos minutos... - + Importing Importando - + Imported Successfully Importación completada con éxito. - + Data was imported successfully. Los datos se importaron correctamente. - + Import Cancelled La importación fue cancelada. - + Import was cancelled by the user. La importación fue cancelada por el usuario. - + Import Failed Importación Fallida. - + Ensure you have read permissions on the targeted directory and try again. Asegúrate de tener permisos de lectura en el directorio seleccionado e inténtalo nuevamente. + + + Keys not installed + Claves no instaladas + + + + Install decryption keys and restart Eden before attempting to install firmware. + Instalar las claves de desencriptación y reiniciar Eden antes de intentar instalar el firmware. + + + + Select Dumped Firmware Source Location + Seleccionar ubicación origen del firmware volcado + + + + Select Dumped Firmware ZIP + Seleccionar el archivo ZIP del firmware volcado + + + + Firmware cleanup failed + Fallo al limpiar firmware + + + + Failed to clean up extracted firmware cache. +Check write permissions in the system temp directory and try again. +OS reported error: %1 + Fallo al limpiar el caché del firmware extraído. +Verifique los permisos de escritura en el directorio temporal del sistema e inténtalo de nuevo. +Error reportado por el sistema operativo: %1 + QtCommon::FS @@ -9627,9 +9670,9 @@ A: Could not find a valid Ryujinx installation. This may typically occur if you are using Ryujinx in portable mode. Would you like to manually select a portable folder to use? - No se pudo encontrar una instalación válida de Ryujinx. Esto suele ocurrir si usa Ryujinx en modo portable. + No se pudo encontrar una instalación válida de Ryujinx. Esto suele ocurrir si usas Ryujinx en modo portable. -¿Desea seleccionar manualmente una carpeta portable? +¿Deseas seleccionar manualmente una carpeta portable? @@ -9800,72 +9843,72 @@ Would you like to manually select a portable folder to use? El caché de metadatos no se pudo eliminar. Podría estar en uso actualmente o no existe. - + Create Shortcut Crear acceso directo - + Do you want to launch the game in fullscreen? ¿Desea iniciar el juego en pantalla completa? - + Shortcut Created Acceso directo creado - + Successfully created a shortcut to %1 Se ha creado un acceso directo a %1 con éxito - + Shortcut may be Volatile! ¡El acceso directo podría ser volátil! - + This will create a shortcut to the current AppImage. This may not work well if you update. Continue? Esto creará un acceso directo a la AppImage actual. Puede que no funcione bien si actualiza. ¿Continuar? - + Failed to Create Shortcut Fallo al crear el acceso directo - + Failed to create a shortcut to %1 Fallo al crear un acceso directo a %1 - + Create Icon Crear icono - + Cannot create icon file. Path "%1" does not exist and cannot be created. No se puede crear el archivo de icono. La ruta "%1" no existe y no se pudo creer. - + No firmware available No hay firmware disponible - + Please install firmware to use the home menu. Por favor intenta instalar firmware para usar el menu de inicio. - + Home Menu Applet Applet del Menu de Inicio - + Home Menu is not available. Please reinstall firmware. Menu de inicio no esta disponible. Por favor intenta reinstalar firmware. @@ -9873,37 +9916,37 @@ Would you like to manually select a portable folder to use? QtCommon::Mod - + Mod Name Nombre del mod - + What should this mod be called? ¿Cómo debería llamarse este mod? - + RomFS RomFS - + ExeFS/Patch ExeFS/Parche - + Cheat Truco - + Mod Type Tipo de mod - + Could not detect mod type automatically. Please manually specify the type of mod you downloaded. Most mods are RomFS mods, but patches (.pchtxt) are typically ExeFS mods. @@ -9912,18 +9955,18 @@ Most mods are RomFS mods, but patches (.pchtxt) are typically ExeFS mods. - - + + Mod Extract Failed Fallo al extraer el mod - + Failed to create temporary directory %1 Fallo al crear directorio temporal %1 - + Zip file %1 is empty El archivo zip %1 está vacío @@ -10629,66 +10672,66 @@ Seleccionando "Desde Eden", los datos de guardado anteriores alojados %1 está disponible para descargar. - + New Version Location Ubicación de la nueva versión - + All Files (*.*) Todos los archivos (*.*) - - - + + + Failed to save file Fallo al guardar el archivo - + Could not open file %1 for writing. No se pudo abrir el archivo %1 para su escritura. - + Downloading... Descargando... - + Cancel Cancelar - + Could not write to file %1. No se pudo escribir en el archivo %1. - + Could not commit to file %1. No se pudo cometer en el archivo %1. - + Failed to download file Fallo al descargar el archivo - + Could not download from %1%2 Error code: %3 No se pudo descargar desde %1%2 Código de error: %3 - + Download Complete Descarga completada - + Successfully downloaded %1. Would you like to open it? %1 descargado con éxito. ¿Desea abrirlo? diff --git a/dist/languages/fi.ts b/dist/languages/fi.ts index 41917c09db..d7fc505e74 100644 --- a/dist/languages/fi.ts +++ b/dist/languages/fi.ts @@ -704,7 +704,7 @@ Options lower than 1X can cause artifacts. - Determines how sharpened the image will look using FSR's dynamic contrast. + Determines how sharpened the image will look using FSR's or SGSR's dynamic contrast. @@ -755,35 +755,35 @@ Disabling it is only intended for debugging. - + Use asynchronous GPU emulation - + Uses an extra CPU thread for rendering. This option should always remain enabled. - + NVDEC emulation: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. - + ASTC Decoding Method: - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -792,55 +792,66 @@ stuttering but may present artifacts. - + ASTC Recompression Method: - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. - + Frame Pacing Mode (Vulkan only) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. - + VRAM Usage Mode: - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. - + Skip CPU Inner Invalidation - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. - + + Anti-Flicker + + + + + Forces GPU fence callbacks to wait for submitted GPU work. +Use with Fast GPU mode, to avoid flicker with lower performance impact. + + + + VSync Mode: - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -848,1361 +859,1381 @@ Immediate (no synchronization) presents whatever is available and can exhibit te - + Sync Memory Operations - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. - + Enable asynchronous presentation (Vulkan only) - + Slightly improves performance by moving presentation to a separate CPU thread. - + Force maximum clocks (Vulkan only) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. - + Anisotropic Filtering: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. - + GPU Mode: - + Controls the GPU emulation mode. Most games render fine with Fast or Balanced modes, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. - + DMA Accuracy: - + Controls the DMA precision accuracy. Safe precision fixes issues in some games but may degrade performance. - + Enable asynchronous shader compilation - + May reduce shader stutter. - + Fast GPU Time - + Overclocks the emulated GPU to increase dynamic resolution and render distance. Use 256 for maximal performance and 512 for maximal graphics fidelity. - + GPU Unswizzle - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. - + GPU Unswizzle Max Texture Size - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. - + GPU Unswizzle Stream Size - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. - + GPU Unswizzle Chunk Size - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. - + Use Vulkan pipeline cache - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. - + Enable Compute Pipelines (Intel Vulkan Only) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. - + Enable Reactive Flushing - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. - + Sync to framerate of video playback - + Run the game at normal speed during video playback, even when the framerate is unlocked. - + Barrier feedback loops - + Improves rendering of transparency effects in specific games. - + Enable buffer history - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. - + Fix bloom effects - + Removes bloom in Burnout. - + Enable Legacy Rescale Pass - + May fix rescale issues in some games by relying on behavior from the previous implementation. Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. - + Extended Dynamic State - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. - + Vertex Input Dynamic State - + Enables vertex input dynamic state feature for better quality and performance. - + Sample Shading - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. - + RNG Seed - + Controls the seed of the random number generator. Mainly used for speedrunning. - + Device Name - + The name of the console. - + + Homebrew Args + + + + + Command-line arguments passed to homebrew at launch (e.g. -noglsl). + + + + Custom RTC Date: - + This option allows to change the clock of the console. Can be used to manipulate time in games. - + The number of seconds from the current unix time - + Language: - + This option can be overridden when region setting is auto-select - + Region: - + The region of the console. - + Time Zone: - + The time zone of the console. - + Sound Output Mode: - + Console Mode: - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. - + Prompt for user profile on boot - + Useful if multiple people use the same PC. - + Pause when not in focus - + Pauses emulation when focusing on other windows. - + Confirm before stopping emulation - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. - + Hide mouse on inactivity - + Hides the mouse after 2.5s of inactivity. - + Disable controller applet - + Forcibly disables the use of the controller applet in emulated programs. When a program attempts to open the controller applet, it is immediately closed. - + Check for updates - + Whether or not to check for updates upon startup. - + Enable Gamemode - + Force X11 as Graphics Backend - + Custom frontend - + Real applet - + Never - + On Load - + Always - + CPU - + GPU - + CPU Asynchronous - + Uncompressed (Best quality) - + BC1 (Low quality) - + BC3 (Medium quality) - - + + Auto - + 30 FPS - + 60 FPS - + 90 FPS - + 120 FPS - + Conservative - + Aggressive - + Vulkan - + OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) - + Null - + Fast - + Balanced - - + + Accurate - - + + Default - + Unsafe (fast) - + Safe (stable) - + Unsafe - + Paranoid (disables most optimizations) - + Debugging - + Dynarmic - + NCE - + Borderless Windowed - + Exclusive Fullscreen - + No Video Output - + CPU Video Decoding - + GPU Video Decoding (Default) - + 0.25X (180p/270p) [EXPERIMENTAL] - + 0.5X (360p/540p) [EXPERIMENTAL] - + 0.75X (540p/810p) [EXPERIMENTAL] - + 1X (720p/1080p) - + 1.25X (900p/1350p) [EXPERIMENTAL] - + 1.5X (1080p/1620p) [EXPERIMENTAL] - + 2X (1440p/2160p) - + 3X (2160p/3240p) - + 4X (2880p/4320p) - + 5X (3600p/5400p) - + 6X (4320p/6480p) - + 7X (5040p/7560p) - + 8X (5760p/8640p) - + Nearest Neighbor - + Bilinear - + Bicubic - + Gaussian - + Lanczos - + ScaleForce - + AMD FidelityFX Super Resolution - + Area - + MMPX - + Zero-Tangent - + B-Spline - + Mitchell - + Spline-1 - - - None + + Snapdragon Game Super Resolution - - FXAA - - - - - SMAA - - - - - Default (16:9) - - - - - Force 4:3 + + Snapdragon Game Super Resolution EdgeDir - Force 21:9 + + None - Force 16:10 + FXAA - Stretch to Window + SMAA - Automatic + Default (16:9) + + + + + Force 4:3 - 2x + Force 21:9 - 4x + Force 16:10 + Stretch to Window + + + + + Automatic + + + + + 2x + + + + + 4x + + + + 8x - + 16x - + 32x - + 64x - + Japanese (日本語) - + American English - + French (français) - + German (Deutsch) - + Italian (italiano) - + Spanish (español) - + Chinese - + Korean (한국어) - + Dutch (Nederlands) - + Portuguese (português) - + Russian (Русский) - + Taiwanese - + British English - + Canadian French - + Latin American Spanish - + Simplified Chinese - + Traditional Chinese (正體中文) - + Brazilian Portuguese (português do Brasil) - + Polish (polska) - + Thai (แบบไทย) - - + + Japan - + USA - + Europe - + Australia - + China - + Korea - + Taiwan - + Auto (%1) Auto select time zone - + Default (%1) Default time zone - + CET - + CST6CDT - + Cuba - + EET - + Egypt - + Eire - + EST - + EST5EDT - + GB - + GB-Eire - + GMT - + GMT+0 - + GMT-0 - + GMT0 - + Greenwich - + Hongkong - + HST - + Iceland - + Iran - + Israel - + Jamaica - + Kwajalein - + Libya - + MET - + MST - + MST7MDT - + Navajo - + NZ - + NZ-CHAT - + Poland - + Portugal - + PRC - + PST8PDT - + ROC - + ROK - + Singapore - + Turkey - + UCT - + Universal - + UTC - + W-SU - + WET - + Zulu - + Mono - + Stereo - + Surround - + 4GB DRAM (Default) - + 6GB DRAM (Unsafe) - + 8GB DRAM - + 10GB DRAM (Unsafe) - + 12GB DRAM (Unsafe) - + Docked - + Handheld - - + + Off - + Boost (1700MHz) - + Fast (2000MHz) - + Always ask (Default) - + Only if game specifies not to stop - + Never ask - - + + Medium (256) - - + + High (512) - + Very Small (16 MB) - + Small (32 MB) - + Normal (128 MB) - + Large (256 MB) - + Very Large (512 MB) - + Very Low (4 MB) - + Low (8 MB) - + Normal (16 MB) - + Medium (32 MB) - + High (64 MB) - + Very Low (32) - + Low (64) - + Normal (128) - + Disabled - + ExtendedDynamicState 1 - + ExtendedDynamicState 2 - + ExtendedDynamicState 3 - + Tree View - + Grid View @@ -3225,33 +3256,33 @@ Would you like to delete the old save data? Taustan väri: - + % - FSR sharpening percentage (e.g. 50%) + FSR/SGSR sharpening percentage (e.g. 50%) - + Off - + VSync Off - + Recommended - + On - + VSync On @@ -4379,7 +4410,7 @@ To invert the axes, first move your joystick vertically, and then horizontally.< - + Configure Säädä @@ -4410,7 +4441,7 @@ To invert the axes, first move your joystick vertically, and then horizontally.< - + Test @@ -4425,77 +4456,77 @@ To invert the axes, first move your joystick vertically, and then horizontally.< - + %1:%2 - - - - - - + + + + + + Eden - + Port number has invalid characters - + Port has to be in range 0 and 65353 - + IP address is not valid - + This UDP server already exists - + Unable to add more than 8 servers - + Testing - + Configuring - + Test Successful - + Successfully received data from the server. - + Test Failed - + Could not receive valid data from the server.<br>Please verify that the server is set up correctly and the address and port are correct. - + UDP Test or calibration configuration is in progress.<br>Please wait for them to finish. @@ -4679,57 +4710,57 @@ Current values are %1% and %2% respectively. - + Add-Ons Lisäosat - + System Järjestelmä - + CPU CPU (prosessori) - + Graphics Grafiikat - + Adv. Graphics - + Ext. Graphics - + Audio Ääni - + Input Profiles - + Network - + Applets - + Properties Ominaisuudet @@ -5738,7 +5769,7 @@ Drag points to change position, or double-click table cells to edit values. - + Calculating... @@ -5940,50 +5971,50 @@ Please go to Configure -> System -> Network and make a selection. GRenderWindow - - + + OpenGL not available! openGL ei ole saatavilla! - + OpenGL shared contexts are not supported. - + Eden has not been compiled with OpenGL support. - - - + + + Error while initializing OpenGL! Virhe käynnistäessä OpenGL ydintä! - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. - + Error while initializing OpenGL 4.6! - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 - + This build doesn't have OpenGL support. @@ -5991,279 +6022,282 @@ Please go to Configure -> System -> Network and make a selection. GameList - + &Add New Game Directory - + Favorite - + Start Game - + Start Game without Custom Configuration - + Open Save Data Location Avaa tallennuskansio - + Open Mod Data Location Avaa modien tallennuskansio - + Open Transferable Pipeline Cache - + Link to Ryujinx - + Remove Poista - + Remove Installed Update Poista asennettu päivitys - + Remove All Installed DLC Poista kaikki asennetut DLC:t - + Remove Custom Configuration Poista mukautettu määritys - + Remove Cache Storage - + Remove OpenGL Pipeline Cache - + Remove Vulkan Pipeline Cache - + Remove All Pipeline Caches - + Remove All Installed Contents Poista kaikki asennettu sisältö - + Manage Play Time - + Edit Play Time Data - + Remove Play Time Data - - + + Dump RomFS Dumppaa RomFS - + Dump RomFS to SDMC - + Verify Integrity - + Copy Title ID to Clipboard Kopioi nimike ID leikepöydälle - + Navigate to GameDB entry Siirry GameDB merkintään - + Create Shortcut - + Add to Desktop - + Add to Applications Menu - + Configure Game - + Scan Subfolders Skannaa alakansiot - + Remove Game Directory Poista pelikansio - + ▲ Move Up ▲ Liiku ylös - + ▼ Move Down ▼ Liiku alas - + Open Directory Location Avaa hakemisto - + Clear Tyhjennä - - - Name - Nimi - - - - Compatibility - Yhteensopivuus - - - - Add-ons - Lisäosat - - - - File type - Tiedostotyyppi - - - - Size - Koko - - - - Play time - - GameListItemCompat - + Ingame - + Game starts, but crashes or major glitches prevent it from being completed. - + Perfect Täydellinen - + Game can be played without issues. - + Playable - + Game functions with minor graphical or audio glitches and is playable from start to finish. - + Intro/Menu Intro/Valikko - + Game loads, but is unable to progress past the Start Screen. - + Won't Boot Ei käynnisty - + The game crashes when attempting to startup. Peli kaatuu käynnistettäessä. - + Not Tested Ei testattu - + The game has not yet been tested. Peliä ei ole vielä testattu + + GameListModel + + + Name + + + + + Compatibility + + + + + Add-ons + + + + + File type + + + + + Size + + + + + Play time + + + GameListPlaceholder - + Double-click to add a new folder to the game list Tuplaklikkaa lisätäksesi uusi kansio pelilistaan. @@ -6271,17 +6305,17 @@ Please go to Configure -> System -> Network and make a selection. GameListSearchField - + %1 of %n result(s) - + Filter: Suodatin: - + Enter pattern to filter Syötä suodatettava tekstipätkä @@ -6775,1139 +6809,1099 @@ Debug Message: - + Game &Icon Size - + Reset Window Size to &720p - + Reset Window Size to 720p - + Reset Window Size to &900p - + Reset Window Size to 900p - + Reset Window Size to &1080p - + Reset Window Size to 1080p - + &Multiplayer - + &Tools - + Am&iibo - + Launch &Applet - + &TAS - + &Create Home Menu Shortcut - + Install &Firmware - + &Help &Apu - + &Install Files to NAND... - + L&oad File... - + Load &Folder... - + E&xit P&oistu - - + + &Pause &Pysäytä - + &Stop &Lopeta - + &Verify Installed Contents - + &About Eden - + Single &Window Mode - + Con&figure... - + Ctrl+, - + Enable Overlay Display Applet - + Show &Filter Bar - + Show &Status Bar - + Show Status Bar Näytä statuspalkki - + &Browse Public Game Lobby - + &Create Room - + &Leave Room - + &Direct Connect to Room - + &Show Current Room - + F&ullscreen - + &Restart - + Load/Remove &Amiibo... - + &Report Compatibility - + Open &Mods Page - + Open &Quickstart Guide - + &FAQ - + &Capture Screenshot - + &Album - + &Set Nickname and Owner - + &Delete Game Data - + &Restore Amiibo - + &Format Amiibo - + &Mii Editor - + &Configure TAS... - + Configure C&urrent Game... - - + + &Start &Käynnistä - + &Reset - - + + R&ecord - + Open &Controller Menu - + Install Decryption &Keys - + &Home Menu - + &Desktop - + &Application Menu - + &Root Data Folder - + &NAND Folder - + &SDMC Folder - + &Mod Folder - + &Log Folder - + From Folder - + From ZIP - + &Eden Dependencies - + &Data Manager - + &Tree View - + &Grid View - + Game Icon Size - - + + None - + Show Game &Name - + Show &Performance Overlay - + + &Carousel View + + + + Small (32x32) - + Standard (64x64) - + Large (128x128) - + Full Size (256x256) - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping - + Loading Web Applet... - - + + Disable Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. - + Unmute - + Mute - + Reset Volume - + &Clear Recent Files - + &Continue - + Warning: Outdated Game Format - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. - - + + Error while loading ROM! - + The ROM format is not supported. - + An error occurred initializing the video core. - + Eden has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-mirror.github.io/help/reference/log-files/'>How to Upload the Log File</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. - + (64-bit) - + (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit - + Closing software... - + Save Data - + Mod Data - + Error Opening %1 Folder - - + + Folder does not exist! - + Remove Installed Game Contents? - + Remove Installed Game Update? - + Remove Installed Game DLC? - + Remove Entry - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? - + Remove Cache Storage? - + Remove File - + Remove Play Time Data - + Reset play time? - - + + RomFS Extraction Failed! - + There was an error copying the RomFS files or the user cancelled the operation. - + Full - + Skeleton - + Select RomFS Dump Mode - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... - - + + Cancel - + RomFS Extraction Succeeded! - + The operation completed successfully. - + Error Opening %1 - + Select Directory - + Properties - + The game properties could not be loaded. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. - + Load File - + Open Extracted ROM Directory - + Invalid Directory Selected - + The directory you have selected does not contain a 'main' file. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files - + %n file(s) remaining - + Installing file "%1"... - - + + Install Results - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application - + System Archive - + System Application Update - + Firmware Package (Type A) - + Firmware Package (Type B) - + Game - + Game Update - + Game DLC - + Delta Title - + Select NCA Install Type... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) - + Failed to Install - + The title type you selected for the NCA is invalid. - + File not found - + File "%1" not found - + OK - + Function Disabled - + Compatibility list reporting is currently disabled. Check back later! - + Error opening URL - + Unable to open the URL "%1". - + TAS Recording - + Overwrite file of player 1? - + Invalid config detected - + Handheld controller can't be used on docked mode. Pro controller will be selected. - - + + Amiibo - - + + The current amiibo has been removed - + Error - - + + The current game is not looking for amiibos - + Amiibo File (%1);; All Files (*.*) - + Load Amiibo - + Error loading Amiibo data - + The selected file is not a valid amiibo - + The selected file is already on use - + An unknown error occurred - - - Keys not installed - - - - - - Install decryption keys and restart Eden before attempting to install firmware. - - - - - Select Dumped Firmware Source Location - - - - - Select Dumped Firmware ZIP - - - - - Zipped Archives (*.zip) - - - - - Firmware cleanup failed - - - - - Failed to clean up extracted firmware cache. -Check write permissions in the system temp directory and try again. -OS reported error: %1 - - - - + No firmware available - + Firmware Corrupted - + Unknown applet - + Applet doesn't map to a known value. - + Record not found - + Applet not found. Please reinstall firmware. - + Capture Screenshot - + PNG Image (*.png) - + TAS state: Running %1/%2 - + TAS state: Recording %1 - + TAS state: Idle %1/%2 - + TAS State: Invalid - + &Stop Running - + Stop R&ecording - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor - + Speed: %1% / %2% - + Speed: %1% - + Game: %1 FPS - + Frame: %1 ms - - - FSR - - - - + NO AA - + VOLUME: MUTE - + VOLUME: %1% Volume percentage (e.g. 50%) - + Derivation Components Missing - + Decryption keys are missing. Install them now? - + Wayland Detected! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -7915,74 +7909,74 @@ Would you like to force it for future launches? - + Use X11 - + Continue with Wayland - + Don't show again - + Restart Required - + Restart Eden to apply the X11 backend. - + Slow - + Turbo - + Unlocked - + Select RomFS Dump Target - + Please select which RomFS you would like to dump. - + Are you sure you want to close Eden? - - - + + + Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? @@ -8048,6 +8042,11 @@ Would you like to bypass this and exit anyway? ScaleForce + + + FSR + + Area @@ -8059,52 +8058,62 @@ Would you like to bypass this and exit anyway? - + + SGSR + + + + + SGSR EdgeDir + + + + Docked - + Handheld - + Fast - + Balanced - + Accurate - + Vulkan - + OpenGL GLSL - + OpenGL SPIRV - + OpenGL GLASM - + Null @@ -8137,7 +8146,7 @@ If you wish to clean up the files which were left in the old data location, you - + Data was migrated successfully. @@ -8986,47 +8995,47 @@ p, li { white-space: pre-wrap; } - + Play Time: %1 - + Never Played - + Version: %1 - + Version: 1.0.0 - + Installed SD Titles Asennetut SD-sovellukset - + Installed NAND Titles Asennetut NAND-sovellukset - + System Titles Järjestelmäsovellukset - + Add New Game Directory Lisää uusi pelikansio - + Favorites @@ -9147,253 +9156,286 @@ p, li { white-space: pre-wrap; } QtCommon::Content - + Game Requires Firmware - + The game you are trying to launch requires firmware to boot or to get past the opening menu. Please <a href='https://yuzu-mirror.github.io/help/quickstart'>dump and install firmware</a>, or press "OK" to launch anyways. - + Installing Firmware... - - - - - + + + + + Cancel - + Firmware Install Failed - + Firmware Install Succeeded - + Firmware integrity verification failed! - - + + Verification failed for the following files: %1 - - + + Verifying integrity... + + + + Integrity verification succeeded! + + - Integrity verification succeeded! - - - - - The operation completed successfully. - - + + Integrity verification failed! - + File contents may be corrupt or missing. - + Integrity verification couldn't be performed - + Firmware installation cancelled, firmware may be in a bad state or corrupted. File contents could not be checked for validity. - + Select Dumped Keys Location - + Decryption Keys install succeeded - + Decryption Keys install failed - + Orphaned Profiles Detected! - + UNEXPECTED BAD THINGS MAY HAPPEN IF YOU DON'T READ THIS!<br>Eden has detected the following save directories with no attached profile:<br>%1<br><br>The following profiles are valid:<br>%2<br><br>Click "OK" to open your save folder and fix up your profiles.<br>Hint: copy the contents of the largest or last-modified folder elsewhere, delete all orphaned profiles, and move your copied contents to the good profile.<br><br>Still confused? See the <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/Orphaned.md'>help page</a>.<br> - + Really clear data? - + Important data may be lost! - + Are you REALLY sure? - + Once deleted, your data will NOT come back! Only do this if you're 100% sure you want to delete this data. - + Clearing... - + Select Export Location - + %1.zip - - + + + Zipped Archives (*.zip) - + Exporting data. This may take a while... - + Exporting - + Exported Successfully - + Data was exported successfully. - + Export Cancelled - + Export was cancelled by the user. - + Export Failed - + Ensure you have write permissions on the targeted directory and try again. - + Select Import Location - + Import Warning - + All previous data in this directory will be deleted. Are you sure you wish to proceed? - + Importing data. This may take a while... - + Importing - + Imported Successfully - + Data was imported successfully. - + Import Cancelled - + Import was cancelled by the user. - + Import Failed - + Ensure you have read permissions on the targeted directory and try again. + + + Keys not installed + + + + + Install decryption keys and restart Eden before attempting to install firmware. + + + + + Select Dumped Firmware Source Location + + + + + Select Dumped Firmware ZIP + + + + + Firmware cleanup failed + + + + + Failed to clean up extracted firmware cache. +Check write permissions in the system temp directory and try again. +OS reported error: %1 + + QtCommon::FS @@ -9637,72 +9679,72 @@ Would you like to manually select a portable folder to use? - + Create Shortcut - + Do you want to launch the game in fullscreen? - + Shortcut Created - + Successfully created a shortcut to %1 - + Shortcut may be Volatile! - + This will create a shortcut to the current AppImage. This may not work well if you update. Continue? - + Failed to Create Shortcut - + Failed to create a shortcut to %1 - + Create Icon - + Cannot create icon file. Path "%1" does not exist and cannot be created. - + No firmware available - + Please install firmware to use the home menu. - + Home Menu Applet - + Home Menu is not available. Please reinstall firmware. @@ -9710,55 +9752,55 @@ Would you like to manually select a portable folder to use? QtCommon::Mod - + Mod Name - + What should this mod be called? - + RomFS - + ExeFS/Patch - + Cheat - + Mod Type - + Could not detect mod type automatically. Please manually specify the type of mod you downloaded. Most mods are RomFS mods, but patches (.pchtxt) are typically ExeFS mods. - - + + Mod Extract Failed - + Failed to create temporary directory %1 - + Zip file %1 is empty @@ -10442,65 +10484,65 @@ By selecting "From Eden", previous save data stored in Ryujinx will be - + New Version Location - + All Files (*.*) - - - + + + Failed to save file - + Could not open file %1 for writing. - + Downloading... - + Cancel - + Could not write to file %1. - + Could not commit to file %1. - + Failed to download file - + Could not download from %1%2 Error code: %3 - + Download Complete - + Successfully downloaded %1. Would you like to open it? diff --git a/dist/languages/fr.ts b/dist/languages/fr.ts index d53b1902d4..0119644338 100644 --- a/dist/languages/fr.ts +++ b/dist/languages/fr.ts @@ -724,8 +724,8 @@ Des options inférieures à 1X peuvent provoquer des artefacts. - Determines how sharpened the image will look using FSR's dynamic contrast. - Détermine le niveau de netteté de l’image en utilisant le contraste dynamique de FSR. + Determines how sharpened the image will look using FSR's or SGSR's dynamic contrast. + @@ -782,23 +782,23 @@ Disabling it is only intended for debugging. Le désactiver est uniquement destiné au débogage. - + Use asynchronous GPU emulation - + Uses an extra CPU thread for rendering. This option should always remain enabled. - + NVDEC emulation: Émulation NVDEC : - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. @@ -807,12 +807,12 @@ Elles peuvent être décodées soit par le CPU, soit par le GPU, ou pas du tout Dans la plupart des cas, le décodage GPU offre les meilleures performances. - + ASTC Decoding Method: Méthode de décodage ASTC : - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -824,12 +824,12 @@ GPU : Utiliser les shaders de calcul du GPU pour décoder les textures ASTC (rec CPU asynchrone : Utiliser le CPU pour décoder les textures ASTC à la demande. Élimine les saccades liées au décodage ASTC, mais peut provoquer des artefacts. - + ASTC Recompression Method: Méthode de recompression ASTC : - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. @@ -837,44 +837,55 @@ BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, BC1/BC3 : Le format intermédiaire sera recompressé en BC1 ou BC3, ce qui économise de la VRAM mais dégrade la qualité de l’image. - + Frame Pacing Mode (Vulkan only) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. - + VRAM Usage Mode: Mode d'utilisation de la VRAM : - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. Permet de choisir si l’émulateur doit privilégier la conservation de la mémoire ou utiliser au maximum la mémoire vidéo disponible pour les performances. Le mode agressif peut affecter les performances d’autres applications, comme les logiciels d’enregistrement. - + Skip CPU Inner Invalidation Ignorer l'invalidation interne du CPU - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. Ignore certaines invalidations de cache lors des mises à jour de la mémoire, réduisant l’utilisation du CPU et améliorant la latence. Cela peut provoquer des plantages légers. - + + Anti-Flicker + + + + + Forces GPU fence callbacks to wait for submitted GPU work. +Use with Fast GPU mode, to avoid flicker with lower performance impact. + + + + VSync Mode: Mode VSync : - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -885,12 +896,12 @@ Mailbox peut offrir une latence inférieure à FIFO et n’entraîne pas de déc Immediate (pas de synchronisation) affiche ce qui est disponible et peut provoquer du déchirement. - + Sync Memory Operations Synchroniser les opérations mémoire - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. @@ -899,144 +910,144 @@ Cette option corrige des problèmes dans les jeux, mais peut dégrader les perfo Les jeux Unreal Engine 4 sont souvent ceux qui bénéficient le plus de ce réglage. - + Enable asynchronous presentation (Vulkan only) Activer la présentation asynchrone (Vulkan uniquement) - + Slightly improves performance by moving presentation to a separate CPU thread. Améliore légèrement les performances en déplaçant la présentation vers un thread CPU séparé. - + Force maximum clocks (Vulkan only) Forcer la fréquence d'horloge maximale (Vulkan uniquement) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. Les exécutions fonctionnent en arrière-plan en attendant les commandes graphiques pour empêcher le GPU de réduire sa vitesse de fréquence d'horloge. - + Anisotropic Filtering: Filtrage anisotropique : - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. Contrôle la qualité du rendu des textures sous des angles obliques. Il est sûr de le régler à 16x sur la plupart des GPU. - + GPU Mode: Mode GPU : - + Controls the GPU emulation mode. Most games render fine with Fast or Balanced modes, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. - + DMA Accuracy: Précision du DMA : - + Controls the DMA precision accuracy. Safe precision fixes issues in some games but may degrade performance. Contrôle la précision des transferts DMA. Une précision plus élevée corrige certains problèmes dans certains jeux, mais peut réduire les performances. - + Enable asynchronous shader compilation Activer la compilation asynchrone des shaders - + May reduce shader stutter. Peut réduire les saccades dues aux shaders. - + Fast GPU Time Temps GPU rapide - + Overclocks the emulated GPU to increase dynamic resolution and render distance. Use 256 for maximal performance and 512 for maximal graphics fidelity. - + GPU Unswizzle - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. - + GPU Unswizzle Max Texture Size - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. - + GPU Unswizzle Stream Size - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. - + GPU Unswizzle Chunk Size - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. - + Use Vulkan pipeline cache Utiliser le cache de pipeline Vulkan - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. Active le cache de pipeline spécifique au fournisseur de GPU. Cette option peut améliorer considérablement le temps de chargement des shaders dans les cas où le pilote Vulkan ne stocke pas les fichiers de cache de pipeline en interne. - + Enable Compute Pipelines (Intel Vulkan Only) Activer les pipelines de calcul (Vulkan sur Intel uniquement) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. @@ -1045,95 +1056,95 @@ Ce réglage n’existe que pour les pilotes propriétaires Intel et peut provoqu Les pipelines de calcul sont toujours activés sur tous les autres pilotes. - + Enable Reactive Flushing Activer le Vidage Réactif - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. Utilise une purge réactive au lieu d'une purge prédictive, permettant une synchronisation de la mémoire plus précise. - + Sync to framerate of video playback Synchro la fréquence d'image de la relecture du vidéo - + Run the game at normal speed during video playback, even when the framerate is unlocked. Éxécuter le jeu à une vitesse normale pendant la relecture du vidéo, même-ci la fréquence d'image est dévérouillée. - + Barrier feedback loops Boucles de rétroaction de barrière - + Improves rendering of transparency effects in specific games. Améliore le rendu des effets de transparence dans des jeux spécifiques. - + Enable buffer history - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. - + Fix bloom effects - + Removes bloom in Burnout. - + Enable Legacy Rescale Pass - + May fix rescale issues in some games by relying on behavior from the previous implementation. Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. - + Extended Dynamic State État dynamique étendu - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. - + Vertex Input Dynamic State - + Enables vertex input dynamic state feature for better quality and performance. - + Sample Shading Échantillonnage de shading - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. Permet au shader de fragments de s’exécuter pour chaque échantillon dans un fragment multi-échantillonné, au lieu d’une seule fois par fragment. @@ -1141,86 +1152,96 @@ Améliore la qualité graphique au prix de performances réduites. Des valeurs plus élevées améliorent la qualité mais dégradent les performances. - + RNG Seed Seed RNG - + Controls the seed of the random number generator. Mainly used for speedrunning. Contrôle la graine du générateur de nombres aléatoires. Principalement utilisé pour le speedrun. - + Device Name Nom de l'appareil - + The name of the console. Nom de la console. - + + Homebrew Args + + + + + Command-line arguments passed to homebrew at launch (e.g. -noglsl). + + + + Custom RTC Date: Date RTC personnalisée : - + This option allows to change the clock of the console. Can be used to manipulate time in games. Cette option permet de modifier l’horloge de la console. Peut être utilisée pour manipuler le temps dans les jeux. - + The number of seconds from the current unix time Nombre de secondes écoulées depuis le 1er janvier 1970. - + Language: Langue : - + This option can be overridden when region setting is auto-select Cette option peut être remplacée lorsque la région est sur auto. - + Region: Région : - + The region of the console. Région de la console. - + Time Zone: Fuseau horaire : - + The time zone of the console. Fuseau horaire de la console. - + Sound Output Mode: Mode de sortie sonore : - + Console Mode: Mode console : - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. @@ -1229,1031 +1250,1041 @@ Les jeux adaptent leur résolution, leurs paramètres graphiques et les manettes Passer en mode Portable peut améliorer les performances sur les systèmes peu puissants. - + Prompt for user profile on boot Choisir l’utilisateur au démarrage. - + Useful if multiple people use the same PC. Utile si plusieurs personnes utilisent le même PC. - + Pause when not in focus Pause lorsque la fenêtre n’est pas active. - + Pauses emulation when focusing on other windows. Met l’émulation en pause dès que l’utilisateur change de fenêtre. - + Confirm before stopping emulation Confirmer avant d'arrêter l'émulation - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. Ignore les demandes de confirmation pour arrêter l’émulation. L’activer permet de contourner ces confirmations et de quitter directement l’émulation. - + Hide mouse on inactivity Cacher la souris en cas d'inactivité - + Hides the mouse after 2.5s of inactivity. Cache le curseur après 2,5 secondes d’inactivité. - + Disable controller applet Désactiver l'applet du contrôleur - + Forcibly disables the use of the controller applet in emulated programs. When a program attempts to open the controller applet, it is immediately closed. Désactive de force le menu de configuration des manettes dans les programmes émulés. Lorsqu’un programme tente d’ouvrir ce menu, il est immédiatement fermé. - + Check for updates Rechercher des mises à jours - + Whether or not to check for updates upon startup. Vérifier ou non les mises à jour au démarrage. - + Enable Gamemode Activer le mode jeu - + Force X11 as Graphics Backend Forcer X11 comme moteur graphique - + Custom frontend Interface personnalisée - + Real applet Applet réel - + Never Jamais - + On Load Au chargement - + Always Toujours - + CPU CPU - + GPU GPU - + CPU Asynchronous CPU Asynchrone - + Uncompressed (Best quality) Non compressé (Meilleure qualité) - + BC1 (Low quality) BC1 (Basse qualité) - + BC3 (Medium quality) BC3 (Qualité moyenne) - - + + Auto Auto - + 30 FPS - + 60 FPS - + 90 FPS - + 120 FPS - + Conservative Conservateur - + Aggressive Agressif - + Vulkan Vulkan - + OpenGL GLSL OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) - + Null Nul - + Fast Rapide - + Balanced Moyen - - + + Accurate Précis - - + + Default Par défaut - + Unsafe (fast) Insecure (rapide) - + Safe (stable) Sûr (stable) - + Unsafe Risqué - + Paranoid (disables most optimizations) Paranoïaque (désactive la plupart des optimisations) - + Debugging Débogage - + Dynarmic Dynamique - + NCE NCE - + Borderless Windowed Fenêtré sans bordure - + Exclusive Fullscreen Plein écran exclusif - + No Video Output Pas de sortie vidéo - + CPU Video Decoding Décodage Vidéo sur le CPU - + GPU Video Decoding (Default) Décodage Vidéo sur le GPU (par défaut) - + 0.25X (180p/270p) [EXPERIMENTAL] 0.25X (180p/270p) [EXPÉRIMENTAL] - + 0.5X (360p/540p) [EXPERIMENTAL] 0.5X (360p/540p) [EXPERIMENTAL] - + 0.75X (540p/810p) [EXPERIMENTAL] 0.75X (540p/810p) [EXPÉRIMENTAL] - + 1X (720p/1080p) 1X (720p/1080p) - + 1.25X (900p/1350p) [EXPERIMENTAL] 1,25X (900p/1350p) [EXPÉRIMENTAL] - + 1.5X (1080p/1620p) [EXPERIMENTAL] 1.5X (1080p/1620p) [EXPÉRIMENTAL] - + 2X (1440p/2160p) 2X (1440p/2160p) - + 3X (2160p/3240p) 3X (2160p/3240p) - + 4X (2880p/4320p) 4X (2880p/4320p) - + 5X (3600p/5400p) 5X (3600p/5400p) - + 6X (4320p/6480p) 6X (4320p/6480p) - + 7X (5040p/7560p) 7X (5040p/7560p) - + 8X (5760p/8640p) 8X (5760p/8640p) - + Nearest Neighbor Plus proche voisin - + Bilinear Bilinéaire - + Bicubic Bicubique - + Gaussian Gaussien - + Lanczos Lanczos - + ScaleForce ScaleForce - + AMD FidelityFX Super Resolution AMD FidelityFX Super Resolution - + Area Area (Par zone) - + MMPX MMPX - + Zero-Tangent Zero-Tangent - + B-Spline B-Spline - + Mitchell Mitchell - + Spline-1 Spline-1 - - + + Snapdragon Game Super Resolution + + + + + Snapdragon Game Super Resolution EdgeDir + + + + + None Aucun - + FXAA FXAA - + SMAA SMAA - + Default (16:9) Par défaut (16:9) - + Force 4:3 Forcer le 4:3 - + Force 21:9 Forcer le 21:9 - + Force 16:10 Forcer le 16:10 - + Stretch to Window Étirer à la fenêtre - + Automatic Automatique - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x - + 32x 32x - + 64x 64x - + Japanese (日本語) Japonais (日本語) - + American English Anglais Américain - + French (français) Français (français) - + German (Deutsch) Allemand (Deutsch) - + Italian (italiano) Italien (italiano) - + Spanish (español) Espagnol (español) - + Chinese Chinois - + Korean (한국어) Coréen (한국어) - + Dutch (Nederlands) Néerlandais (Nederlands) - + Portuguese (português) Portugais (português) - + Russian (Русский) Russe (Русский) - + Taiwanese Taïwanais - + British English Anglais Britannique - + Canadian French Français Canadien - + Latin American Spanish Espagnol d'Amérique Latine - + Simplified Chinese Chinois Simplifié - + Traditional Chinese (正體中文) Chinois Traditionnel (正體中文) - + Brazilian Portuguese (português do Brasil) Portugais Brésilien (português do Brasil) - + Polish (polska) - + Thai (แบบไทย) - - + + Japan Japon - + USA É.-U.A. - + Europe Europe - + Australia Australie - + China Chine - + Korea Corée - + Taiwan Taïwan - + Auto (%1) Auto select time zone Auto (%1) - + Default (%1) Default time zone Par défaut (%1) - + CET CET - + CST6CDT CST6CDT - + Cuba Cuba - + EET EET - + Egypt Égypte - + Eire Eire - + EST EST - + EST5EDT EST5EDT - + GB GB - + GB-Eire GB-Eire - + GMT GMT - + GMT+0 GMT+0 - + GMT-0 GMT-0 - + GMT0 GMT0 - + Greenwich Greenwich - + Hongkong Hong Kong - + HST HST - + Iceland Islande - + Iran Iran - + Israel Israël - + Jamaica Jamaïque - + Kwajalein Kwajalein - + Libya Libye - + MET MET - + MST MST - + MST7MDT MST7MDT - + Navajo Navajo - + NZ NZ - + NZ-CHAT NZ-CHAT - + Poland Pologne - + Portugal Portugal - + PRC PRC - + PST8PDT PST8PDT - + ROC ROC - + ROK ROK - + Singapore Singapour - + Turkey Turquie - + UCT UCT - + Universal Universel - + UTC UTC - + W-SU W-SU - + WET WET - + Zulu Zulu - + Mono Mono - + Stereo Stéréo - + Surround Surround - + 4GB DRAM (Default) 4 GB DRAM (Par défaut) - + 6GB DRAM (Unsafe) 6 GB DRAM (Risqué) - + 8GB DRAM 8GO DRAM - + 10GB DRAM (Unsafe) 10GO DRAM (Insecure) - + 12GB DRAM (Unsafe) 12GO DRAM (Insecure) - + Docked Mode TV - + Handheld Mode Portable - - + + Off Désactivé - + Boost (1700MHz) Boost (1700MHz) - + Fast (2000MHz) Rapide (2000MHz) - + Always ask (Default) Toujours demander (par défaut) - + Only if game specifies not to stop Uniquement si le jeu précise de ne pas s'arrêter - + Never ask Jamais demander - - + + Medium (256) Moyen (256) - - + + High (512) Élevé (512) - + Very Small (16 MB) Très petit (16Mo) - + Small (32 MB) Petit (32Mo) - + Normal (128 MB) Normal (128Mo) - + Large (256 MB) Large (256Mo) - + Very Large (512 MB) Très large (512Mo) - + Very Low (4 MB) Très faible (4 Mo) - + Low (8 MB) Faible (8 Mo) - + Normal (16 MB) Normal (16 Mo) - + Medium (32 MB) Moyen (32 Mo) - + High (64 MB) Élevé (64 Mo) - + Very Low (32) Très faible (32) - + Low (64) Faible (64) - + Normal (128) Normal (128) - + Disabled Désactivé - + ExtendedDynamicState 1 État dynamique étendu 1 - + ExtendedDynamicState 2 État dynamique étendu 2 - + ExtendedDynamicState 3 État dynamique étendu 3 - + Tree View Vue en liste - + Grid View Vue en grille @@ -3308,33 +3339,33 @@ Would you like to delete the old save data? Couleur de l’arrière plan : - + % - FSR sharpening percentage (e.g. 50%) - % + FSR/SGSR sharpening percentage (e.g. 50%) + - + Off Désactivé - + VSync Off VSync Désactivée - + Recommended Recommandé - + On Activé - + VSync On VSync Activée @@ -4463,7 +4494,7 @@ Pour inverser les axes, bougez d'abord votre joystick verticalement, puis h - + Configure Configurer @@ -4494,7 +4525,7 @@ Pour inverser les axes, bougez d'abord votre joystick verticalement, puis h - + Test Tester @@ -4509,77 +4540,77 @@ Pour inverser les axes, bougez d'abord votre joystick verticalement, puis h Retirer un serveur - + %1:%2 %1:%2 - - - - - - + + + + + + Eden Eden - + Port number has invalid characters Le numéro de port contient des caractères invalides - + Port has to be in range 0 and 65353 Le port doit être entre 0 et 65353 - + IP address is not valid L'adresse IP n'est pas valide - + This UDP server already exists Ce serveur UDP existe déjà - + Unable to add more than 8 servers Impossible d'ajouter plus de 8 serveurs - + Testing Essai - + Configuring Configuration - + Test Successful Test réussi - + Successfully received data from the server. Données reçues du serveur avec succès. - + Test Failed Test échoué - + Could not receive valid data from the server.<br>Please verify that the server is set up correctly and the address and port are correct. Impossible de recevoir des données valides du serveur.<br>Veuillez vérifier que le serveur est correctement configuré et que l'adresse et le port sont corrects. - + UDP Test or calibration configuration is in progress.<br>Please wait for them to finish. Le test UDP ou la configuration de l'étalonnage est en cours.<br>Veuillez attendre qu'ils se terminent. @@ -4764,57 +4795,57 @@ Les valeurs actuelles sont respectivement de %1% et %2%. Certains paramètres ne sont disponibles que lorsqu'un jeu n'est pas en cours d'exécution. - + Add-Ons Extensions - + System Système - + CPU CPU - + Graphics Graphiques - + Adv. Graphics Adv. Graphiques - + Ext. Graphics - + Audio Audio - + Input Profiles Profils d'entrée - + Network - + Applets - + Properties Propriétés @@ -5825,7 +5856,7 @@ Faites glisser les points pour modifier la position ou double-cliquez sur les ce Importer les données dans ce répertoire. Cela peut prendre un certain temps et supprimera TOUTES LES DONNÉES EXISTANTES ! - + Calculating... Calcul en cours… @@ -6029,50 +6060,50 @@ Veuillez aller dans Configurer -> Système -> Réseau puis en choisir une. GRenderWindow - - + + OpenGL not available! OpenGL n'est pas disponible ! - + OpenGL shared contexts are not supported. Les contextes OpenGL partagés ne sont pas pris en charge. - + Eden has not been compiled with OpenGL support. Eden n'a pas été compilé avec le support OpenGL - - - + + + Error while initializing OpenGL! Erreur lors de l'initialisation d'OpenGL ! - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. Votre GPU peut ne pas prendre en charge OpenGL, ou vous n'avez pas les derniers pilotes graphiques. - + Error while initializing OpenGL 4.6! Erreur lors de l'initialisation d'OpenGL 4.6 ! - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 Votre GPU peut ne pas prendre en charge OpenGL 4.6 ou vous ne disposez pas du dernier pilote graphique: %1 - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 Votre GPU peut ne pas prendre en charge une ou plusieurs extensions OpenGL requises. Veuillez vous assurer que vous disposez du dernier pilote graphique.<br><br>GL Renderer :<br>%1<br><br>Extensions non prises en charge :<br>%2 - + This build doesn't have OpenGL support. @@ -6080,279 +6111,282 @@ Veuillez aller dans Configurer -> Système -> Réseau puis en choisir une. GameList - + &Add New Game Directory - + Favorite Préférer - + Start Game Démarrer le jeu - + Start Game without Custom Configuration Démarrer le jeu sans configuration personnalisée - + Open Save Data Location Ouvrir l'emplacement des données de sauvegarde - + Open Mod Data Location Ouvrir l'emplacement des données des mods - + Open Transferable Pipeline Cache Ouvrir le cache de pipelines transférable - + Link to Ryujinx Lier à Ryujinx - + Remove Supprimer - + Remove Installed Update Supprimer mise à jour installée - + Remove All Installed DLC Supprimer tous les DLC installés - + Remove Custom Configuration Supprimer la configuration personnalisée - + Remove Cache Storage Supprimer le stockage du cache - + Remove OpenGL Pipeline Cache Supprimer le cache de pipelines OpenGL - + Remove Vulkan Pipeline Cache Supprimer le cache de pipelines Vulkan - + Remove All Pipeline Caches Supprimer tous les caches de pipelines - + Remove All Installed Contents Supprimer tout le contenu installé - + Manage Play Time Gérer le Temps de Jeu - + Edit Play Time Data Modifier les Données de Temps de Jeu - + Remove Play Time Data Supprimer les données de temps de jeu - - + + Dump RomFS Extraire la RomFS - + Dump RomFS to SDMC Décharger RomFS vers SDMC - + Verify Integrity Vérifier l'intégrité - + Copy Title ID to Clipboard Copier l'ID du titre dans le Presse-papiers - + Navigate to GameDB entry Accédez à l'entrée GameDB - + Create Shortcut Créer un raccourci - + Add to Desktop Ajouter au bureau - + Add to Applications Menu Ajouter au menu des applications - + Configure Game Configurer le jeux - + Scan Subfolders Scanner les sous-dossiers - + Remove Game Directory Supprimer le répertoire du jeu - + ▲ Move Up ▲ Monter - + ▼ Move Down ▼ Descendre - + Open Directory Location Ouvrir l'emplacement du répertoire - + Clear Effacer - - - Name - Nom - - - - Compatibility - Compatibilité - - - - Add-ons - Extensions - - - - File type - Type de fichier - - - - Size - Taille - - - - Play time - Temps de jeu - GameListItemCompat - + Ingame En jeu - + Game starts, but crashes or major glitches prevent it from being completed. Le jeu se lance, mais crash ou des bugs majeurs l'empêchent d'être complété. - + Perfect Parfait - + Game can be played without issues. Le jeu peut être joué sans problèmes. - + Playable Jouable - + Game functions with minor graphical or audio glitches and is playable from start to finish. Le jeu fonctionne avec des glitchs graphiques ou audio mineurs et est jouable du début à la fin. - + Intro/Menu Intro/Menu - + Game loads, but is unable to progress past the Start Screen. Le jeu charge, mais ne peut pas progresser après le menu de démarrage. - + Won't Boot Ne démarre pas - + The game crashes when attempting to startup. Le jeu crash au lancement. - + Not Tested Non testé - + The game has not yet been tested. Le jeu n'a pas encore été testé. + + GameListModel + + + Name + + + + + Compatibility + + + + + Add-ons + + + + + File type + + + + + Size + + + + + Play time + + + GameListPlaceholder - + Double-click to add a new folder to the game list Double-cliquez pour ajouter un nouveau dossier à la liste de jeux @@ -6360,17 +6394,17 @@ Veuillez aller dans Configurer -> Système -> Réseau puis en choisir une. GameListSearchField - + %1 of %n result(s) %1 sur %n résultat%1 sur %n résultats%1 sur %n résultat(s) - + Filter: Filtre : - + Enter pattern to filter Entrez un motif à filtrer @@ -6866,1139 +6900,1099 @@ Message de débogage : - + Game &Icon Size - + Reset Window Size to &720p &Réinitialiser la taille de la fenêtre à 720p - + Reset Window Size to 720p Réinitialiser la taille de la fenêtre à 720p - + Reset Window Size to &900p Réinitialiser la taille de la fenêtre à &900p - + Reset Window Size to 900p Réinitialiser la taille de la fenêtre à 900p - + Reset Window Size to &1080p Réinitialiser la taille de la fenêtre à &1080p - + Reset Window Size to 1080p Réinitialiser la taille de la fenêtre à 1080p - + &Multiplayer &Multijoueur - + &Tools &Outils - + Am&iibo Am&iibo - + Launch &Applet - + &TAS &TAS - + &Create Home Menu Shortcut &Créer un Raccourci du Menu d'Accueil - + Install &Firmware Installer le &Firmware - + &Help &Aide - + &Install Files to NAND... &Installer des fichiers sur la NAND... - + L&oad File... &Charger un fichier... - + Load &Folder... &Charger un dossier - + E&xit Q&uitter - - + + &Pause &Pause - + &Stop &Arrêter - + &Verify Installed Contents &Vérifier les contenus installés - + &About Eden &À propos d'Eden - + Single &Window Mode &Mode fenêtre unique - + Con&figure... &Configurer... - + Ctrl+, Ctrl+, - + Enable Overlay Display Applet - + Show &Filter Bar &Afficher la barre de filtre - + Show &Status Bar &Afficher la barre d'état - + Show Status Bar Afficher la barre d'état - + &Browse Public Game Lobby &Parcourir le menu des jeux publics - + &Create Room &Créer un salon - + &Leave Room &Quitter le salon - + &Direct Connect to Room &Connexion directe au salon - + &Show Current Room &Afficher le salon actuel - + F&ullscreen P&lein écran - + &Restart &Redémarrer - + Load/Remove &Amiibo... Charger/Retirer un &Amiibo… - + &Report Compatibility &Signaler la compatibilité - + Open &Mods Page Ouvrir la &page des mods - + Open &Quickstart Guide Ouvrir le &guide de démarrage rapide - + &FAQ &FAQ - + &Capture Screenshot &Capture d'écran - + &Album - + &Set Nickname and Owner &Définir le surnom et le propriétaire - + &Delete Game Data &Supprimer les données du jeu - + &Restore Amiibo &Restaurer l'amiibo - + &Format Amiibo &Formater l'amiibo - + &Mii Editor - + &Configure TAS... &Configurer TAS... - + Configure C&urrent Game... Configurer le j&eu actuel... - - + + &Start &Démarrer - + &Reset &Réinitialiser - - + + R&ecord En&registrer - + Open &Controller Menu Ouvrir le &menu des manettes - + Install Decryption &Keys Installer les &clés de déchiffrement - + &Home Menu - + &Desktop &Bureau - + &Application Menu &Menu de l'Application - + &Root Data Folder &Dossier de Données (Root) Principal. - + &NAND Folder &Dossier NAND - + &SDMC Folder &Dossier SDMC - + &Mod Folder &Dossier Mod - + &Log Folder &Dossier Log - + From Folder Depuis un dossier - + From ZIP Depuis une archive ZIP - + &Eden Dependencies Dépendances d'&Eden - + &Data Manager &Gestionnaire de données - + &Tree View - + &Grid View - + Game Icon Size - - + + None Aucun - + Show Game &Name - + Show &Performance Overlay - + + &Carousel View + + + + Small (32x32) - + Standard (64x64) - + Large (128x128) - + Full Size (256x256) - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping - + Loading Web Applet... - - + + Disable Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. - + Unmute Remettre le son - + Mute Couper le son - + Reset Volume Réinitialiser le volume - + &Clear Recent Files - + &Continue &Continuer - + Warning: Outdated Game Format - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. - - + + Error while loading ROM! - + The ROM format is not supported. - + An error occurred initializing the video core. - + Eden has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-mirror.github.io/help/reference/log-files/'>How to Upload the Log File</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. - + (64-bit) (64-bit) - + (32-bit) (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit - + Closing software... Fermeture du logiciel... - + Save Data Données de sauvegarde - + Mod Data - + Error Opening %1 Folder - - + + Folder does not exist! - + Remove Installed Game Contents? - + Remove Installed Game Update? - + Remove Installed Game DLC? - + Remove Entry - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? - + Remove Cache Storage? - + Remove File - + Remove Play Time Data - + Reset play time? Réinitialiser le temps de jeu ? - - + + RomFS Extraction Failed! L'extraction de la RomFS a échoué ! - + There was an error copying the RomFS files or the user cancelled the operation. - + Full - + Skeleton - + Select RomFS Dump Mode - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... - - + + Cancel Annuler - + RomFS Extraction Succeeded! Extraction de la RomFS réussi ! - + The operation completed successfully. - + Error Opening %1 - + Select Directory - + Properties Propriétés - + The game properties could not be loaded. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. - + Load File - + Open Extracted ROM Directory - + Invalid Directory Selected - + The directory you have selected does not contain a 'main' file. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files - + %n file(s) remaining - + Installing file "%1"... - - + + Install Results - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application - + System Archive - + System Application Update - + Firmware Package (Type A) - + Firmware Package (Type B) - + Game Jeu - + Game Update - + Game DLC - + Delta Title - + Select NCA Install Type... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) - + Failed to Install Échec de l'installation - + The title type you selected for the NCA is invalid. - + File not found Fichier non trouvé - + File "%1" not found - + OK OK - + Function Disabled - + Compatibility list reporting is currently disabled. Check back later! - + Error opening URL - + Unable to open the URL "%1". - + TAS Recording - + Overwrite file of player 1? - + Invalid config detected - + Handheld controller can't be used on docked mode. Pro controller will be selected. - - + + Amiibo Amiibo - - + + The current amiibo has been removed - + Error Erreur - - + + The current game is not looking for amiibos - + Amiibo File (%1);; All Files (*.*) - + Load Amiibo Charger un Amiibo - + Error loading Amiibo data - + The selected file is not a valid amiibo - + The selected file is already on use - + An unknown error occurred - - - Keys not installed - - - - - - Install decryption keys and restart Eden before attempting to install firmware. - - - - - Select Dumped Firmware Source Location - - - - - Select Dumped Firmware ZIP - - - - - Zipped Archives (*.zip) - - - - - Firmware cleanup failed - - - - - Failed to clean up extracted firmware cache. -Check write permissions in the system temp directory and try again. -OS reported error: %1 - - - - + No firmware available - + Firmware Corrupted Firmware corrompu - + Unknown applet - + Applet doesn't map to a known value. - + Record not found - + Applet not found. Please reinstall firmware. - + Capture Screenshot - + PNG Image (*.png) - + TAS state: Running %1/%2 - + TAS state: Recording %1 - + TAS state: Idle %1/%2 - + TAS State: Invalid - + &Stop Running - + Stop R&ecording - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor - + Speed: %1% / %2% - + Speed: %1% - + Game: %1 FPS - + Frame: %1 ms - - - FSR - FSR - - - + NO AA - + VOLUME: MUTE VOLUME : MUET - + VOLUME: %1% Volume percentage (e.g. 50%) - + Derivation Components Missing - + Decryption keys are missing. Install them now? - + Wayland Detected! Wayland détecté ! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -8006,74 +8000,74 @@ Would you like to force it for future launches? - + Use X11 - + Continue with Wayland - + Don't show again - + Restart Required - + Restart Eden to apply the X11 backend. - + Slow - + Turbo - + Unlocked - + Select RomFS Dump Target - + Please select which RomFS you would like to dump. - + Are you sure you want to close Eden? - - - + + + Eden Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? @@ -8139,6 +8133,11 @@ Would you like to bypass this and exit anyway? ScaleForce ScaleForce + + + FSR + FSR + Area @@ -8150,52 +8149,62 @@ Would you like to bypass this and exit anyway? - + + SGSR + + + + + SGSR EdgeDir + + + + Docked Mode TV - + Handheld Mode Portable - + Fast - + Balanced - + Accurate - + Vulkan Vulkan - + OpenGL GLSL - + OpenGL SPIRV - + OpenGL GLASM - + Null @@ -8238,7 +8247,7 @@ Si vous souhaitez supprimer les fichiers qui ont été laissés dans l'anci %1 - + Data was migrated successfully. Les données ont été migré avec succès @@ -9096,47 +9105,47 @@ p, li { white-space: pre-wrap; } %1 joue à %2 - + Play Time: %1 - + Never Played - + Version: %1 - + Version: 1.0.0 - + Installed SD Titles Titres installés sur la SD - + Installed NAND Titles Titres installés sur la NAND - + System Titles Titres Système - + Add New Game Directory Ajouter un nouveau répertoire de jeu - + Favorites Favoris @@ -9257,47 +9266,47 @@ p, li { white-space: pre-wrap; } QtCommon::Content - + Game Requires Firmware Le jeu nécessite un firmware - + The game you are trying to launch requires firmware to boot or to get past the opening menu. Please <a href='https://yuzu-mirror.github.io/help/quickstart'>dump and install firmware</a>, or press "OK" to launch anyways. Le jeu que vous essayez de lancer nécessite un firmware pour démarrer ou pour passer le menu d’ouverture. Veuillez <a href='https://yuzu-mirror.github.io/help/quickstart'>dumper et installer le firmware</a>, ou appuyez sur « OK » pour lancer quand même. - + Installing Firmware... Installation du firmware... - - - - - + + + + + Cancel Annuler - + Firmware Install Failed Installation du firmware échoué - + Firmware Install Succeeded Installation du firmware réussi - + Firmware integrity verification failed! Échec de la vérification de l'intégrité du firmware ! - - + + Verification failed for the following files: %1 @@ -9306,207 +9315,240 @@ p, li { white-space: pre-wrap; } %1 - - + + Verifying integrity... Vérification de l'intégrité... - - + + Integrity verification succeeded! La vérification de l'intégrité réussi ! - - + + The operation completed successfully. L'opération s'est déroulée avec succès. - - + + Integrity verification failed! La vérification de l'intégrité a échoué ! - + File contents may be corrupt or missing. Le contenu d'un fichier peut être corrompu or manquant. - + Integrity verification couldn't be performed La vérification de l'intégrité n'a pas pu être effectuée - + Firmware installation cancelled, firmware may be in a bad state or corrupted. File contents could not be checked for validity. Installation du firmware annulée, le firmware est peut-être en mauvais état ou corrompu. Impossible de vérifier la validité du contenu du fichier. - + Select Dumped Keys Location Sélectionner Emplacement Clés Extraites - + Decryption Keys install succeeded Installation des clés de décryptage avec succès - + Decryption Keys install failed Installation des clés de décryptage échoué - + Orphaned Profiles Detected! Profils orphelins détectés ! - + UNEXPECTED BAD THINGS MAY HAPPEN IF YOU DON'T READ THIS!<br>Eden has detected the following save directories with no attached profile:<br>%1<br><br>The following profiles are valid:<br>%2<br><br>Click "OK" to open your save folder and fix up your profiles.<br>Hint: copy the contents of the largest or last-modified folder elsewhere, delete all orphaned profiles, and move your copied contents to the good profile.<br><br>Still confused? See the <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/Orphaned.md'>help page</a>.<br> DES CHOSES GRAVES INATTENDUES PEUVENT SURVENIR SI VOUS NE LISEZ PAS CECI !<br>Eden a détecté les répertoires de sauvegarde suivants sans profil associé :<br>%1<br><br>Les profils suivants sont valides :<br>%2<br><br>Cliquez sur « OK » pour ouvrir votre dossier de sauvegarde et corriger vos profils.<br>Astuce : copiez le contenu du dossier le plus volumineux ou le plus récemment modifié ailleurs, supprimez tous les profils orphelins et déplacez le contenu copié vers le profil correct.<br><br>Toujours confus ? Consultez la <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/Orphaned.md'>page d’aide</a>.<br> - + Really clear data? Vraiment effacer les données ? - + Important data may be lost! Des données importantes peuvent être perdues ! - + Are you REALLY sure? Êtes-vous VRAIMENT sûr ? - + Once deleted, your data will NOT come back! Only do this if you're 100% sure you want to delete this data. Une fois supprimées, vos données NE POURRONT PAS être récupérées ! Ne faites cela que si vous êtes sûr à 100 % de vouloir supprimer ces données. - + Clearing... Suppression en cours… - + Select Export Location Sélectionner l’emplacement d’exportation - + %1.zip %1.zip - - + + + Zipped Archives (*.zip) Archives compressées (*.zip) - + Exporting data. This may take a while... Exportation des données en cours. Cela peut prendre un certain temps… - + Exporting Exportation en cours… - + Exported Successfully Exportation réussie - + Data was exported successfully. Les données ont été exportées avec succès. - + Export Cancelled Exportation annulée - + Export was cancelled by the user. L’exportation a été annulée par l’utilisateur. - + Export Failed Échec de l’exportation - + Ensure you have write permissions on the targeted directory and try again. Assurez-vous d’avoir les permissions d’écriture sur le répertoire ciblé et réessayez. - + Select Import Location Sélectionner l’emplacement d’importation - + Import Warning Avertissement d’importation - + All previous data in this directory will be deleted. Are you sure you wish to proceed? Toutes les données précédentes de ce répertoire seront supprimées. Êtes-vous sûr de vouloir continuer ? - + Importing data. This may take a while... Importation des données en cours. Cela peut prendre un certain temps… - + Importing Importation en cours… - + Imported Successfully Importation réussie - + Data was imported successfully. Les données ont été importées avec succès. - + Import Cancelled Importation annulée - + Import was cancelled by the user. L’importation a été annulée par l’utilisateur. - + Import Failed Échec de l’importation - + Ensure you have read permissions on the targeted directory and try again. Assurez-vous d’avoir les permissions de lecture sur le répertoire ciblé et réessayez. + + + Keys not installed + + + + + Install decryption keys and restart Eden before attempting to install firmware. + + + + + Select Dumped Firmware Source Location + + + + + Select Dumped Firmware ZIP + + + + + Firmware cleanup failed + + + + + Failed to clean up extracted firmware cache. +Check write permissions in the system temp directory and try again. +OS reported error: %1 + + QtCommon::FS @@ -9753,72 +9795,72 @@ Would you like to manually select a portable folder to use? Le cache des métadonnées n'a pas pu être supprimé. Il est peut-être en cours d'utilisation ou inexistant. - + Create Shortcut Créer un raccourci - + Do you want to launch the game in fullscreen? Voulez-vous lancer le jeu en plein écran ? - + Shortcut Created Raccourcis crée - + Successfully created a shortcut to %1 Création d'un raccourci vers %1 réussi avec succès - + Shortcut may be Volatile! Les raccourcis peuvent être instables ! - + This will create a shortcut to the current AppImage. This may not work well if you update. Continue? Cela créera un raccourci vers l'AppImage actuel. Cela peut ne pas fonctionner correctement si vous effectuez une mise à jour. Continuer ? - + Failed to Create Shortcut Échec de la création du raccourci - + Failed to create a shortcut to %1 Échec de la création d'un raccourci vers %1 - + Create Icon Créer une icône - + Cannot create icon file. Path "%1" does not exist and cannot be created. Impossible de créer le fichier icône. Le chemin "%1" n'existe pas et ne peut être créé. - + No firmware available Pas de firmware disponible - + Please install firmware to use the home menu. Veuillez installer un firmware pour utiliser le menu d'accueil - + Home Menu Applet Applet Menu d'accueil - + Home Menu is not available. Please reinstall firmware. Le menu d'accueil n'est pas disponible. Veuillez réinstaller le firmware @@ -9826,55 +9868,55 @@ Would you like to manually select a portable folder to use? QtCommon::Mod - + Mod Name - + What should this mod be called? - + RomFS - + ExeFS/Patch - + Cheat - + Mod Type - + Could not detect mod type automatically. Please manually specify the type of mod you downloaded. Most mods are RomFS mods, but patches (.pchtxt) are typically ExeFS mods. - - + + Mod Extract Failed - + Failed to create temporary directory %1 - + Zip file %1 is empty @@ -10575,65 +10617,65 @@ En sélectionnant « Depuis Eden », les données de sauvegarde précédemment s - + New Version Location - + All Files (*.*) - - - + + + Failed to save file - + Could not open file %1 for writing. - + Downloading... - + Cancel - + Could not write to file %1. - + Could not commit to file %1. - + Failed to download file - + Could not download from %1%2 Error code: %3 - + Download Complete - + Successfully downloaded %1. Would you like to open it? diff --git a/dist/languages/hu.ts b/dist/languages/hu.ts index a9067946f0..0d84ab2bee 100644 --- a/dist/languages/hu.ts +++ b/dist/languages/hu.ts @@ -705,7 +705,7 @@ Options lower than 1X can cause artifacts. - Determines how sharpened the image will look using FSR's dynamic contrast. + Determines how sharpened the image will look using FSR's or SGSR's dynamic contrast. @@ -759,23 +759,23 @@ Disabling it is only intended for debugging. Kikapcsolása csak hibakeresésre szolgál. - + Use asynchronous GPU emulation - + Uses an extra CPU thread for rendering. This option should always remain enabled. - + NVDEC emulation: NVDEC emuláció: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. @@ -784,12 +784,12 @@ A dekódoláshoz használhatja a CPU-t vagy a GPU-t, vagy egyáltalán nem vége A legtöbb esetben a GPU dekódolás nyújtja a legjobb teljesítményt. - + ASTC Decoding Method: ASTC dekódoló módszer: - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -798,55 +798,66 @@ stuttering but may present artifacts. - + ASTC Recompression Method: ASTC újraszűrési módszer: - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. - + Frame Pacing Mode (Vulkan only) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. - + VRAM Usage Mode: VRAM használati mód: - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. - + Skip CPU Inner Invalidation - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. - + + Anti-Flicker + + + + + Forces GPU fence callbacks to wait for submitted GPU work. +Use with Fast GPU mode, to avoid flicker with lower performance impact. + + + + VSync Mode: VSync mód: - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -854,1361 +865,1381 @@ Immediate (no synchronization) presents whatever is available and can exhibit te - + Sync Memory Operations - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. - + Enable asynchronous presentation (Vulkan only) Aszinkron prezentálás engedélyezése (csak Vulkan) - + Slightly improves performance by moving presentation to a separate CPU thread. Kicsit javítja a teljesítményt azáltal, hogy a megjelenítést külön CPU szálra helyezi át. - + Force maximum clocks (Vulkan only) Maximális órajelek kényszerítése (csak Vulkan) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. A háttérben fut, miközben várja a grafikai parancsokat, hogy a GPU ne csökkentse az órajelét. - + Anisotropic Filtering: Anizotropikus szűrés: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. - + GPU Mode: - + Controls the GPU emulation mode. Most games render fine with Fast or Balanced modes, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. - + DMA Accuracy: - + Controls the DMA precision accuracy. Safe precision fixes issues in some games but may degrade performance. - + Enable asynchronous shader compilation - + May reduce shader stutter. - + Fast GPU Time - + Overclocks the emulated GPU to increase dynamic resolution and render distance. Use 256 for maximal performance and 512 for maximal graphics fidelity. - + GPU Unswizzle - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. - + GPU Unswizzle Max Texture Size - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. - + GPU Unswizzle Stream Size - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. - + GPU Unswizzle Chunk Size - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. - + Use Vulkan pipeline cache Vulkan pipeline gyorsítótár használata. - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. - + Enable Compute Pipelines (Intel Vulkan Only) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. - + Enable Reactive Flushing Reaktív ürítés használata - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. Reaktív ürítést használ a prediktív ürítés helyett, ami pontosabb memóriaszinkronizálást tesz lehetővé. - + Sync to framerate of video playback Szinkronizálás a videolejátszás képkockasebességéhez - + Run the game at normal speed during video playback, even when the framerate is unlocked. A játék futtatása normál sebességgel videolejátszás közben, még akkor is, ha a képkockasebesség fel van oldva. - + Barrier feedback loops - + Improves rendering of transparency effects in specific games. Javítja az átlátszósági effektek megjelenítését bizonyos játékokban. - + Enable buffer history - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. - + Fix bloom effects - + Removes bloom in Burnout. - + Enable Legacy Rescale Pass - + May fix rescale issues in some games by relying on behavior from the previous implementation. Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. - + Extended Dynamic State - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. - + Vertex Input Dynamic State - + Enables vertex input dynamic state feature for better quality and performance. - + Sample Shading - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. - + RNG Seed - + Controls the seed of the random number generator. Mainly used for speedrunning. - + Device Name Eszköznév - + The name of the console. - + + Homebrew Args + + + + + Command-line arguments passed to homebrew at launch (e.g. -noglsl). + + + + Custom RTC Date: Egyéni RTC dátum: - + This option allows to change the clock of the console. Can be used to manipulate time in games. - + The number of seconds from the current unix time - + Language: Nyelv: - + This option can be overridden when region setting is auto-select - + Region: Régió: - + The region of the console. - + Time Zone: Időzóna: - + The time zone of the console. - + Sound Output Mode: Hangkimeneti mód: - + Console Mode: Konzol mód: - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. - + Prompt for user profile on boot - + Useful if multiple people use the same PC. - + Pause when not in focus - + Pauses emulation when focusing on other windows. - + Confirm before stopping emulation Emuláció leállításának megerősítése - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. - + Hide mouse on inactivity Egér elrejtése inaktivitáskor - + Hides the mouse after 2.5s of inactivity. - + Disable controller applet Vezérlő applet letiltása - + Forcibly disables the use of the controller applet in emulated programs. When a program attempts to open the controller applet, it is immediately closed. - + Check for updates - + Whether or not to check for updates upon startup. - + Enable Gamemode Játékmód engedélyezése - + Force X11 as Graphics Backend - + Custom frontend Egyéni frontend - + Real applet Valódi applet - + Never - + On Load - + Always - + CPU CPU - + GPU GPU - + CPU Asynchronous CPU aszinkron - + Uncompressed (Best quality) Tömörítetlen (legjobb minőség) - + BC1 (Low quality) BC1 (alacsony minőség) - + BC3 (Medium quality) BC3 (közepes minőség) - - + + Auto Automatikus - + 30 FPS - + 60 FPS - + 90 FPS - + 120 FPS - + Conservative Takarékos - + Aggressive Aggresszív - + Vulkan Vulkan - + OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) - + Null Null - + Fast - + Balanced - - + + Accurate Pontos - - + + Default Alapértelmezett - + Unsafe (fast) - + Safe (stable) - + Unsafe Nem biztonságos - + Paranoid (disables most optimizations) Paranoid (a legtöbb optimalizálást letiltja) - + Debugging - + Dynarmic Dinamikus - + NCE NCE - + Borderless Windowed Szegély nélküli ablak - + Exclusive Fullscreen Exkluzív teljes képernyő - + No Video Output Nincs videokimenet - + CPU Video Decoding CPU videódekódolás - + GPU Video Decoding (Default) GPU videódekódolás (alapértelmezett) - + 0.25X (180p/270p) [EXPERIMENTAL] - + 0.5X (360p/540p) [EXPERIMENTAL] 0.5X (360p/540p) [KÍSÉRLETI] - + 0.75X (540p/810p) [EXPERIMENTAL] 0.75X (540p/810p) [KÍSÉRLETI] - + 1X (720p/1080p) 1X (720p/1080p) - + 1.25X (900p/1350p) [EXPERIMENTAL] - + 1.5X (1080p/1620p) [EXPERIMENTAL] 1.5X (1080p/1620p) [KÍSÉRLETI] - + 2X (1440p/2160p) 2X (1440p/2160p) - + 3X (2160p/3240p) 3X (2160p/3240p) - + 4X (2880p/4320p) 4X (2880p/4320p) - + 5X (3600p/5400p) 5X (3600p/5400p) - + 6X (4320p/6480p) 6X (4320p/6480p) - + 7X (5040p/7560p) 7X (5040p/7560p) - + 8X (5760p/8640p) 8X (5760p/8640p) - + Nearest Neighbor Legközelebbi szomszéd - + Bilinear Bilineáris - + Bicubic Bikubikus - + Gaussian Gauss-féle - + Lanczos - + ScaleForce ScaleForce - + AMD FidelityFX Super Resolution - + Area - + MMPX - + Zero-Tangent - + B-Spline - + Mitchell - + Spline-1 - - + + Snapdragon Game Super Resolution + + + + + Snapdragon Game Super Resolution EdgeDir + + + + + None Nincs - + FXAA FXAA - + SMAA SMAA - + Default (16:9) Alapértelmezett (16:9) - + Force 4:3 4:3 kényszerítése - + Force 21:9 21:9 kényszerítése - + Force 16:10 16:10 kényszerítése - + Stretch to Window Ablakhoz nyújtás - + Automatic Automatikus - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x - + 32x - + 64x - + Japanese (日本語) Japán (日本語) - + American English Amerikai angol - + French (français) Francia (français) - + German (Deutsch) Német (Deutsch) - + Italian (italiano) Olasz (italiano) - + Spanish (español) Spanyol (español) - + Chinese Kínai - + Korean (한국어) Koreai (한국어) - + Dutch (Nederlands) Holland (Nederlands) - + Portuguese (português) Portugál (português) - + Russian (Русский) Orosz (Русский) - + Taiwanese Tajvani - + British English Brit Angol - + Canadian French Kanadai francia - + Latin American Spanish Latin-amerikai spanyol - + Simplified Chinese Egyszerűsített kínai - + Traditional Chinese (正體中文) Hagyományos kínai (正體中文) - + Brazilian Portuguese (português do Brasil) Brazíliai portugál (português do Brasil) - + Polish (polska) - + Thai (แบบไทย) - - + + Japan Japán - + USA USA - + Europe Európa - + Australia Ausztrália - + China Kína - + Korea Korea - + Taiwan Tajvan - + Auto (%1) Auto select time zone Automatikus (%1) - + Default (%1) Default time zone Alapértelmezett (%1) - + CET CET - + CST6CDT CST6CDT - + Cuba Kuba - + EET EET - + Egypt Egyiptom - + Eire Eire - + EST EST - + EST5EDT EST5EDT - + GB GB - + GB-Eire GB-Eire - + GMT GMT - + GMT+0 GMT+0 - + GMT-0 GMT-0 - + GMT0 GMT0 - + Greenwich Greenwich - + Hongkong Hongkong - + HST HST - + Iceland Izland - + Iran Irán - + Israel Izrael - + Jamaica Jamaika - + Kwajalein Kwajalein - + Libya Líbia - + MET MET - + MST MST - + MST7MDT MST7MDT - + Navajo Navahó - + NZ NZ - + NZ-CHAT NZ-CHAT - + Poland Lengyelország - + Portugal Portugália - + PRC PRC - + PST8PDT PST8PDT - + ROC ROC - + ROK ROK - + Singapore Szingapúr - + Turkey Törökország - + UCT UCT - + Universal Univerzális - + UTC UTC - + W-SU W-SU - + WET WET - + Zulu Zulu - + Mono Mono - + Stereo Sztereó - + Surround Térhangzás - + 4GB DRAM (Default) 4GB DRAM (Alapértelmezett) - + 6GB DRAM (Unsafe) 6GB DRAM (Nem biztonságos) - + 8GB DRAM - + 10GB DRAM (Unsafe) - + 12GB DRAM (Unsafe) - + Docked Dokkolt - + Handheld Kézi - - + + Off - + Boost (1700MHz) - + Fast (2000MHz) - + Always ask (Default) Mindig kérdezz rá (alapértelmezett) - + Only if game specifies not to stop Csak akkor, ha a játék kifejezetten kéri a folytatást. - + Never ask Soha ne kérdezz rá - - + + Medium (256) - - + + High (512) - + Very Small (16 MB) - + Small (32 MB) - + Normal (128 MB) - + Large (256 MB) - + Very Large (512 MB) - + Very Low (4 MB) - + Low (8 MB) - + Normal (16 MB) - + Medium (32 MB) - + High (64 MB) - + Very Low (32) - + Low (64) - + Normal (128) - + Disabled - + ExtendedDynamicState 1 - + ExtendedDynamicState 2 - + ExtendedDynamicState 3 - + Tree View - + Grid View @@ -3236,33 +3267,33 @@ Would you like to delete the old save data? Háttérszín: - + % - FSR sharpening percentage (e.g. 50%) - % + FSR/SGSR sharpening percentage (e.g. 50%) + - + Off Ki - + VSync Off VSync Ki - + Recommended Ajánlott - + On Be - + VSync On VSync Be @@ -4391,7 +4422,7 @@ A tengely megfordításához mozgasd a kart először függőlegesen, majd vízs - + Configure Konfigurálás @@ -4422,7 +4453,7 @@ A tengely megfordításához mozgasd a kart először függőlegesen, majd vízs - + Test Teszt @@ -4437,77 +4468,77 @@ A tengely megfordításához mozgasd a kart először függőlegesen, majd vízs Szerver eltávolítása - + %1:%2 %1:%2 - - - - - - + + + + + + Eden - + Port number has invalid characters A port érvénytelen karaktereket tartalmaz - + Port has to be in range 0 and 65353 A portnak 0 és 65353 közötti tartományban kell lennie. - + IP address is not valid Érvénytelen IP-cím - + This UDP server already exists Ez az UDP szerver már létezik - + Unable to add more than 8 servers 8-nál több kiszolgálót nem lehet hozzáadni - + Testing Tesztelés - + Configuring Konfigurálás - + Test Successful Sikeres teszt - + Successfully received data from the server. Az adatok sikeresen beérkeztek a kiszolgálótól. - + Test Failed Sikertelen teszt - + Could not receive valid data from the server.<br>Please verify that the server is set up correctly and the address and port are correct. Nem lehetett érvényes adatot fogadni a szervertől. <br>Ellenőrizd, hogy a szerver megfelelően van-e beállítva, valamint a cím és a port helyes. - + UDP Test or calibration configuration is in progress.<br>Please wait for them to finish. UDP tesztelés vagy a kalibrálás konfigurálása folyamatban van.<br>Kérjük, várj, amíg befejeződik. @@ -4692,57 +4723,57 @@ A jelenlegi érték %1% és %2%. Néhány beállítás csak akkor érhető el, amikor nem fut játék. - + Add-Ons Kiegészítők - + System Rendszer - + CPU CPU - + Graphics Grafika - + Adv. Graphics Haladó graf. - + Ext. Graphics - + Audio Hang - + Input Profiles Beviteli profilok - + Network - + Applets - + Properties Tulajdonságok @@ -5753,7 +5784,7 @@ Húzd a pontokat a pozíció megváltoztatásához, vagy kattints duplán a táb - + Calculating... @@ -5955,50 +5986,50 @@ Please go to Configure -> System -> Network and make a selection. GRenderWindow - - + + OpenGL not available! OpenGL nem elérhető! - + OpenGL shared contexts are not supported. - + Eden has not been compiled with OpenGL support. - - - + + + Error while initializing OpenGL! Hiba történt az OpenGL inicializálása során! - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. Lehetséges, hogy a GPU-d nem támogatja az OpenGL-t, vagy nem a legfrissebb grafikus illesztőprogram van telepítve. - + Error while initializing OpenGL 4.6! Hiba történt az OpenGL 4.6 inicializálása során! - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 Lehetséges, hogy a GPU-d nem támogatja az OpenGL 4.6-ot, vagy nem a legfrissebb grafikus illesztőprogram van telepítve.<br><br>GL Renderer:<br>%1 - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 Előfordulhat, hogy a GPU-d nem támogat egy vagy több szükséges OpenGL kiterjesztést. Győződj meg róla, hogy a legújabb videokártya-illesztőprogramot használod.<br><br>GL Renderer:<br>%1<br><br>Nem támogatott kiterjesztések:<br>%2 - + This build doesn't have OpenGL support. @@ -6006,279 +6037,282 @@ Please go to Configure -> System -> Network and make a selection. GameList - + &Add New Game Directory - + Favorite Kedvenc - + Start Game Játék indítása - + Start Game without Custom Configuration Játék indítása egyéni konfiguráció nélkül - + Open Save Data Location Mentett adatok helyének megnyitása - + Open Mod Data Location Modadatok helyének megnyitása - + Open Transferable Pipeline Cache Áthelyezhető pipeline gyorsítótár megnyitása - + Link to Ryujinx - + Remove Eltávolítás - + Remove Installed Update Telepített frissítés eltávolítása - + Remove All Installed DLC Összes telepített DLC eltávolítása - + Remove Custom Configuration Egyéni konfiguráció eltávolítása - + Remove Cache Storage Gyorsítótár ürítése - + Remove OpenGL Pipeline Cache OpenGL Pipeline gyorsítótár eltávolítása - + Remove Vulkan Pipeline Cache Vulkan pipeline gyorsítótár eltávolítása - + Remove All Pipeline Caches Az összes Pipeline gyorsítótár törlése - + Remove All Installed Contents Összes telepített tartalom törlése - + Manage Play Time - + Edit Play Time Data - + Remove Play Time Data Játékidő törlése - - + + Dump RomFS RomFS kimentése - + Dump RomFS to SDMC RomFS kimentése SDMC-re - + Verify Integrity Integritás ellenőrzése - + Copy Title ID to Clipboard Játék címének vágólapra másolása - + Navigate to GameDB entry GameDB bejegyzéshez navigálás - + Create Shortcut Parancsikon létrehozása - + Add to Desktop Asztalhoz adás - + Add to Applications Menu Alkalmazások menühöz adás - + Configure Game - + Scan Subfolders Almappák szkennelése - + Remove Game Directory Játékkönyvtár eltávolítása - + ▲ Move Up ▲ Feljebb mozgatás - + ▼ Move Down ▼ Lejjebb mozgatás - + Open Directory Location Könyvtár helyének megnyitása - + Clear Törlés - - - Name - Név - - - - Compatibility - Kompatibilitás - - - - Add-ons - Kiegészítők - - - - File type - Fájltípus - - - - Size - Méret - - - - Play time - Játékidő - GameListItemCompat - + Ingame Játékban - + Game starts, but crashes or major glitches prevent it from being completed. A játék elindul, de összeomlik, vagy súlyos hibák miatt nem fejezhető be. - + Perfect Tökéletes - + Game can be played without issues. A játék problémamentesen játszható. - + Playable Játszható - + Game functions with minor graphical or audio glitches and is playable from start to finish. A játék kisebb grafikai- és hanghibákkal végigjátszható. - + Intro/Menu Bevezető/Menü - + Game loads, but is unable to progress past the Start Screen. A játék betölt, de nem jut tovább a Kezdőképernyőn. - + Won't Boot Nem indul - + The game crashes when attempting to startup. A játék összeomlik indításkor. - + Not Tested Nem tesztelt - + The game has not yet been tested. Ez a játék még nem lett tesztelve. + + GameListModel + + + Name + + + + + Compatibility + + + + + Add-ons + + + + + File type + + + + + Size + + + + + Play time + + + GameListPlaceholder - + Double-click to add a new folder to the game list Dupla kattintással új mappát adhatsz hozzá a játéklistához. @@ -6286,17 +6320,17 @@ Please go to Configure -> System -> Network and make a selection. GameListSearchField - + %1 of %n result(s) %1 a(z) %n találatból%1 a(z) %n találatból - + Filter: Szűrés: - + Enter pattern to filter Adj meg egy mintát a szűréshez @@ -6791,1139 +6825,1099 @@ Debug Message: - + Game &Icon Size - + Reset Window Size to &720p Ablakfelbontás visszaállítása erre: &720p - + Reset Window Size to 720p Ablakfelbontás visszaállítása 720p-re - + Reset Window Size to &900p Ablakfelbontás visszaállítása erre: &900p - + Reset Window Size to 900p Ablakfelbontás visszaállítása 900p-re - + Reset Window Size to &1080p Ablakfelbontás visszaállítása erre: &1080p - + Reset Window Size to 1080p Ablakfelbontás visszaállítása 1080p-re - + &Multiplayer &Multiplayer - + &Tools &Eszközök - + Am&iibo - + Launch &Applet - + &TAS &TAS - + &Create Home Menu Shortcut - + Install &Firmware - + &Help &Segítség - + &Install Files to NAND... &Fájlok telepítése a NAND-ra... - + L&oad File... F&ájl betöltése... - + Load &Folder... &Mappa betöltése... - + E&xit K&ilépés - - + + &Pause &Szünet - + &Stop &Leállítás - + &Verify Installed Contents &Telepített tartalom ellenőrzése - + &About Eden - + Single &Window Mode &Egyablakos mód - + Con&figure... Kon&figurálás... - + Ctrl+, - + Enable Overlay Display Applet - + Show &Filter Bar &Szűrősáv mutatása - + Show &Status Bar &Állapotsáv mutatása - + Show Status Bar Állapotsáv mutatása - + &Browse Public Game Lobby &Nyilvános játéklobbi böngészése - + &Create Room &Szoba létrehozása - + &Leave Room &Szoba elhagyása - + &Direct Connect to Room &Közvetlen csatlakozás szobához - + &Show Current Room &Jelenlegi szoba megjelenítése - + F&ullscreen T&eljes képernyő - + &Restart &Újraindítás - + Load/Remove &Amiibo... &Amiibo betöltése/törlése... - + &Report Compatibility &Kompatibilitás jelentése - + Open &Mods Page &Modok oldal megnyitása - + Open &Quickstart Guide &Gyorstájékoztató megnyitása - + &FAQ &GYIK - + &Capture Screenshot &Képernyőkép készítése - + &Album - + &Set Nickname and Owner &Becenév és tulajdonos beállítása - + &Delete Game Data &Játékadatok törlése - + &Restore Amiibo &Amiibo helyreállítása - + &Format Amiibo &Amiibo formázása - + &Mii Editor - + &Configure TAS... &TAS konfigurálása... - + Configure C&urrent Game... J&elenlegi játék konfigurálása... - - + + &Start &Indítás - + &Reset &Visszaállítás - - + + R&ecord F&elvétel - + Open &Controller Menu &Vezérlő menü megnyitása - + Install Decryption &Keys - + &Home Menu - + &Desktop - + &Application Menu - + &Root Data Folder - + &NAND Folder - + &SDMC Folder - + &Mod Folder - + &Log Folder - + From Folder - + From ZIP - + &Eden Dependencies - + &Data Manager - + &Tree View - + &Grid View - + Game Icon Size - - + + None - + Show Game &Name - + Show &Performance Overlay - + + &Carousel View + + + + Small (32x32) - + Standard (64x64) - + Large (128x128) - + Full Size (256x256) - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping - + Loading Web Applet... - - + + Disable Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. - + Unmute - + Mute - + Reset Volume - + &Clear Recent Files - + &Continue - + Warning: Outdated Game Format - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. - - + + Error while loading ROM! - + The ROM format is not supported. - + An error occurred initializing the video core. - + Eden has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-mirror.github.io/help/reference/log-files/'>How to Upload the Log File</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. - + (64-bit) - + (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit - + Closing software... - + Save Data - + Mod Data - + Error Opening %1 Folder - - + + Folder does not exist! - + Remove Installed Game Contents? - + Remove Installed Game Update? - + Remove Installed Game DLC? - + Remove Entry - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? - + Remove Cache Storage? - + Remove File - + Remove Play Time Data - + Reset play time? - - + + RomFS Extraction Failed! - + There was an error copying the RomFS files or the user cancelled the operation. - + Full - + Skeleton - + Select RomFS Dump Mode - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... - - + + Cancel - + RomFS Extraction Succeeded! - + The operation completed successfully. - + Error Opening %1 - + Select Directory - + Properties - + The game properties could not be loaded. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. - + Load File - + Open Extracted ROM Directory - + Invalid Directory Selected - + The directory you have selected does not contain a 'main' file. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files - + %n file(s) remaining - + Installing file "%1"... - - + + Install Results - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application - + System Archive - + System Application Update - + Firmware Package (Type A) - + Firmware Package (Type B) - + Game - + Game Update - + Game DLC - + Delta Title - + Select NCA Install Type... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) - + Failed to Install - + The title type you selected for the NCA is invalid. - + File not found - + File "%1" not found - + OK - + Function Disabled - + Compatibility list reporting is currently disabled. Check back later! - + Error opening URL - + Unable to open the URL "%1". - + TAS Recording - + Overwrite file of player 1? - + Invalid config detected - + Handheld controller can't be used on docked mode. Pro controller will be selected. - - + + Amiibo - - + + The current amiibo has been removed - + Error - - + + The current game is not looking for amiibos - + Amiibo File (%1);; All Files (*.*) - + Load Amiibo - + Error loading Amiibo data - + The selected file is not a valid amiibo - + The selected file is already on use - + An unknown error occurred - - - Keys not installed - - - - - - Install decryption keys and restart Eden before attempting to install firmware. - - - - - Select Dumped Firmware Source Location - - - - - Select Dumped Firmware ZIP - - - - - Zipped Archives (*.zip) - - - - - Firmware cleanup failed - - - - - Failed to clean up extracted firmware cache. -Check write permissions in the system temp directory and try again. -OS reported error: %1 - - - - + No firmware available - + Firmware Corrupted - + Unknown applet - + Applet doesn't map to a known value. - + Record not found - + Applet not found. Please reinstall firmware. - + Capture Screenshot - + PNG Image (*.png) - + TAS state: Running %1/%2 - + TAS state: Recording %1 - + TAS state: Idle %1/%2 - + TAS State: Invalid - + &Stop Running - + Stop R&ecording - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor - + Speed: %1% / %2% - + Speed: %1% - + Game: %1 FPS - + Frame: %1 ms - - - FSR - - - - + NO AA - + VOLUME: MUTE - + VOLUME: %1% Volume percentage (e.g. 50%) - + Derivation Components Missing - + Decryption keys are missing. Install them now? - + Wayland Detected! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -7931,74 +7925,74 @@ Would you like to force it for future launches? - + Use X11 - + Continue with Wayland - + Don't show again - + Restart Required - + Restart Eden to apply the X11 backend. - + Slow - + Turbo - + Unlocked - + Select RomFS Dump Target - + Please select which RomFS you would like to dump. - + Are you sure you want to close Eden? - - - + + + Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? @@ -8064,6 +8058,11 @@ Would you like to bypass this and exit anyway? ScaleForce + + + FSR + + Area @@ -8075,52 +8074,62 @@ Would you like to bypass this and exit anyway? - + + SGSR + + + + + SGSR EdgeDir + + + + Docked - + Handheld - + Fast - + Balanced - + Accurate - + Vulkan - + OpenGL GLSL - + OpenGL SPIRV - + OpenGL GLASM - + Null @@ -8153,7 +8162,7 @@ If you wish to clean up the files which were left in the old data location, you - + Data was migrated successfully. @@ -9008,47 +9017,47 @@ p, li { white-space: pre-wrap; } %1 ezzel játszik: %2 - + Play Time: %1 - + Never Played - + Version: %1 - + Version: 1.0.0 - + Installed SD Titles Telepített SD játékok - + Installed NAND Titles Telepített NAND játékok - + System Titles Rendszercímek - + Add New Game Directory Új játékkönyvtár hozzáadása - + Favorites Kedvencek @@ -9169,253 +9178,286 @@ p, li { white-space: pre-wrap; } QtCommon::Content - + Game Requires Firmware - + The game you are trying to launch requires firmware to boot or to get past the opening menu. Please <a href='https://yuzu-mirror.github.io/help/quickstart'>dump and install firmware</a>, or press "OK" to launch anyways. - + Installing Firmware... - - - - - + + + + + Cancel - + Firmware Install Failed - + Firmware Install Succeeded - + Firmware integrity verification failed! - - + + Verification failed for the following files: %1 - - + + Verifying integrity... + + + + Integrity verification succeeded! + + - Integrity verification succeeded! - - - - - The operation completed successfully. - - + + Integrity verification failed! - + File contents may be corrupt or missing. - + Integrity verification couldn't be performed - + Firmware installation cancelled, firmware may be in a bad state or corrupted. File contents could not be checked for validity. - + Select Dumped Keys Location - + Decryption Keys install succeeded - + Decryption Keys install failed - + Orphaned Profiles Detected! - + UNEXPECTED BAD THINGS MAY HAPPEN IF YOU DON'T READ THIS!<br>Eden has detected the following save directories with no attached profile:<br>%1<br><br>The following profiles are valid:<br>%2<br><br>Click "OK" to open your save folder and fix up your profiles.<br>Hint: copy the contents of the largest or last-modified folder elsewhere, delete all orphaned profiles, and move your copied contents to the good profile.<br><br>Still confused? See the <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/Orphaned.md'>help page</a>.<br> - + Really clear data? - + Important data may be lost! - + Are you REALLY sure? - + Once deleted, your data will NOT come back! Only do this if you're 100% sure you want to delete this data. - + Clearing... - + Select Export Location - + %1.zip - - + + + Zipped Archives (*.zip) - + Exporting data. This may take a while... - + Exporting - + Exported Successfully - + Data was exported successfully. - + Export Cancelled - + Export was cancelled by the user. - + Export Failed - + Ensure you have write permissions on the targeted directory and try again. - + Select Import Location - + Import Warning - + All previous data in this directory will be deleted. Are you sure you wish to proceed? - + Importing data. This may take a while... - + Importing - + Imported Successfully - + Data was imported successfully. - + Import Cancelled - + Import was cancelled by the user. - + Import Failed - + Ensure you have read permissions on the targeted directory and try again. + + + Keys not installed + + + + + Install decryption keys and restart Eden before attempting to install firmware. + + + + + Select Dumped Firmware Source Location + + + + + Select Dumped Firmware ZIP + + + + + Firmware cleanup failed + + + + + Failed to clean up extracted firmware cache. +Check write permissions in the system temp directory and try again. +OS reported error: %1 + + QtCommon::FS @@ -9659,72 +9701,72 @@ Would you like to manually select a portable folder to use? - + Create Shortcut - + Do you want to launch the game in fullscreen? - + Shortcut Created - + Successfully created a shortcut to %1 - + Shortcut may be Volatile! - + This will create a shortcut to the current AppImage. This may not work well if you update. Continue? - + Failed to Create Shortcut - + Failed to create a shortcut to %1 - + Create Icon - + Cannot create icon file. Path "%1" does not exist and cannot be created. - + No firmware available - + Please install firmware to use the home menu. - + Home Menu Applet - + Home Menu is not available. Please reinstall firmware. @@ -9732,55 +9774,55 @@ Would you like to manually select a portable folder to use? QtCommon::Mod - + Mod Name - + What should this mod be called? - + RomFS - + ExeFS/Patch - + Cheat - + Mod Type - + Could not detect mod type automatically. Please manually specify the type of mod you downloaded. Most mods are RomFS mods, but patches (.pchtxt) are typically ExeFS mods. - - + + Mod Extract Failed - + Failed to create temporary directory %1 - + Zip file %1 is empty @@ -10474,65 +10516,65 @@ By selecting "From Eden", previous save data stored in Ryujinx will be - + New Version Location - + All Files (*.*) - - - + + + Failed to save file - + Could not open file %1 for writing. - + Downloading... - + Cancel - + Could not write to file %1. - + Could not commit to file %1. - + Failed to download file - + Could not download from %1%2 Error code: %3 - + Download Complete - + Successfully downloaded %1. Would you like to open it? diff --git a/dist/languages/id.ts b/dist/languages/id.ts index d759c99d70..a3a08928a6 100644 --- a/dist/languages/id.ts +++ b/dist/languages/id.ts @@ -717,7 +717,7 @@ Options lower than 1X can cause artifacts. - Determines how sharpened the image will look using FSR's dynamic contrast. + Determines how sharpened the image will look using FSR's or SGSR's dynamic contrast. @@ -771,23 +771,23 @@ Disabling it is only intended for debugging. Menonaktifkannya hanya dimaksudkan untuk debugging. - + Use asynchronous GPU emulation - + Uses an extra CPU thread for rendering. This option should always remain enabled. - + NVDEC emulation: Emulasi NVDEC: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. @@ -796,12 +796,12 @@ Ini dapat menggunakan CPU atau GPU untuk dekode, atau tidak melakukan dekode sam Dalam kebanyakan kasus, dekode GPU memberikan kinerja terbaik. - + ASTC Decoding Method: ASTC Metode Dekoding: - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -810,55 +810,66 @@ stuttering but may present artifacts. - + ASTC Recompression Method: ASTC Metode Pemampatan Ulang: - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. - + Frame Pacing Mode (Vulkan only) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. - + VRAM Usage Mode: Mode Penggunaan VRAM: - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. - + Skip CPU Inner Invalidation Abaikan Invalidasi Internal CPU - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. - + + Anti-Flicker + + + + + Forces GPU fence callbacks to wait for submitted GPU work. +Use with Fast GPU mode, to avoid flicker with lower performance impact. + + + + VSync Mode: Mode Sinkronisasi Vertikal - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -866,1362 +877,1382 @@ Immediate (no synchronization) presents whatever is available and can exhibit te - + Sync Memory Operations Sinkronisasi Operasi Memori - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. - + Enable asynchronous presentation (Vulkan only) Aktifkan presentasi asinkron (hanya Vulkan) - + Slightly improves performance by moving presentation to a separate CPU thread. Meningkatkan kinerja sedikit dengan memindahkan presentasi ke thread CPU terpisah. - + Force maximum clocks (Vulkan only) Paksa jam maximum (Vulkan only) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. Berjalan bekerja di latar belakang sambil menunggu perintah grafis untuk mencegah GPU agar tidak menurunkan kecepatan jamnya. - + Anisotropic Filtering: Anisotropic Filtering: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. - + GPU Mode: Mode GPU: - + Controls the GPU emulation mode. Most games render fine with Fast or Balanced modes, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. - + DMA Accuracy: Akurasi DMA - + Controls the DMA precision accuracy. Safe precision fixes issues in some games but may degrade performance. - + Enable asynchronous shader compilation Gunakan kompilasi shader asinkron - + May reduce shader stutter. - + Fast GPU Time Percepatan Waktu CPU - + Overclocks the emulated GPU to increase dynamic resolution and render distance. Use 256 for maximal performance and 512 for maximal graphics fidelity. - + GPU Unswizzle - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. - + GPU Unswizzle Max Texture Size - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. - + GPU Unswizzle Stream Size - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. - + GPU Unswizzle Chunk Size - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. - + Use Vulkan pipeline cache Gunakan pipeline cache Vulkan - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. Memungkinkan cache pipeline spesifik vendor GPU. Opsi ini dapat meningkatkan waktu pemuatan shader secara signifikan dalam kasus di mana driver Vulkan tidak menyimpan file cache pipeline secara internal. - + Enable Compute Pipelines (Intel Vulkan Only) Aktifkan Pipa Komputasi (Hanya Intel Vulkan) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. - + Enable Reactive Flushing Aktifkan Reactive Flushing - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. Menggunakan pemadatan reaktif alih-alih pemadatan prediktif, memungkinkan sinkronisasi memori yang lebih akurat. - + Sync to framerate of video playback Sinkronkan dengan kecepatan pemutaran video - + Run the game at normal speed during video playback, even when the framerate is unlocked. Jalankan permainan dengan kecepatan normal selama pemutaran video, bahkan ketika framerate tidak terkunci. - + Barrier feedback loops Loop umpan balik penghalang - + Improves rendering of transparency effects in specific games. Meningkatkan rendering efek transparansi dalam game tertentu. - + Enable buffer history - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. - + Fix bloom effects Perbaiki efek bloom - + Removes bloom in Burnout. - + Enable Legacy Rescale Pass - + May fix rescale issues in some games by relying on behavior from the previous implementation. Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. - + Extended Dynamic State - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. - + Vertex Input Dynamic State - + Enables vertex input dynamic state feature for better quality and performance. - + Sample Shading Pencahayaan Sampel - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. - + RNG Seed Benih RNG - + Controls the seed of the random number generator. Mainly used for speedrunning. - + Device Name Nama Perangkat - + The name of the console. - + + Homebrew Args + + + + + Command-line arguments passed to homebrew at launch (e.g. -noglsl). + + + + Custom RTC Date: Tanggal RTC Kustom: - + This option allows to change the clock of the console. Can be used to manipulate time in games. - + The number of seconds from the current unix time - + Language: Bahasa - + This option can be overridden when region setting is auto-select - + Region: Wilayah: - + The region of the console. - + Time Zone: Zona Waktu: - + The time zone of the console. - + Sound Output Mode: Mode keluaran suara. - + Console Mode: Mode Konsol - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. - + Prompt for user profile on boot - + Useful if multiple people use the same PC. - + Pause when not in focus - + Pauses emulation when focusing on other windows. - + Confirm before stopping emulation Konfirmasi sebelum menghentikan emulasi - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. - + Hide mouse on inactivity Sembunyikan mouse saat tidak aktif - + Hides the mouse after 2.5s of inactivity. - + Disable controller applet Nonaktifkan aplikasi pengontrol - + Forcibly disables the use of the controller applet in emulated programs. When a program attempts to open the controller applet, it is immediately closed. - + Check for updates Cek Pembaruan - + Whether or not to check for updates upon startup. - + Enable Gamemode Aktifkan Mode Permainan - + Force X11 as Graphics Backend - + Custom frontend Tampilan depan kustom - + Real applet Aplikasi nyata - + Never Tidak Pernah - + On Load - + Always Selalu - + CPU CPU - + GPU GPU - + CPU Asynchronous CPU sinkron - + Uncompressed (Best quality) Tidak terkompresi (Kualitas Terbaik) - + BC1 (Low quality) BC1 (Kualitas rendah) - + BC3 (Medium quality) BC3 (Kualitas sedang) - - + + Auto Otomatis - + 30 FPS - + 60 FPS - + 90 FPS - + 120 FPS - + Conservative Konservatif - + Aggressive Agresif - + Vulkan Vulkan - + OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) - + Null Null - + Fast Cepat - + Balanced Seimbang - - + + Accurate Akurat - - + + Default Bawaan - + Unsafe (fast) - + Safe (stable) - + Unsafe Berbahaya - + Paranoid (disables most optimizations) Paranoid (menonaktifkan sebagian besar optimasi) - + Debugging - + Dynarmic Dynarmic - + NCE NCE - + Borderless Windowed Layar Tanpa Batas - + Exclusive Fullscreen Layar Penuh Eksklusif - + No Video Output Tidak ada Keluaran Suara - + CPU Video Decoding Penguraian Video menggunakan CPU - + GPU Video Decoding (Default) Penguraian Video menggunakan GPU (Bawaan) - + 0.25X (180p/270p) [EXPERIMENTAL] - + 0.5X (360p/540p) [EXPERIMENTAL] 0.5X (360p/540p) [EKSPERIMENTAL] - + 0.75X (540p/810p) [EXPERIMENTAL] 0.75X (540p/810p) [EKSPERIMENTAL] - + 1X (720p/1080p) 1X (720p/1080p) - + 1.25X (900p/1350p) [EXPERIMENTAL] - + 1.5X (1080p/1620p) [EXPERIMENTAL] 1.5X (1080p/1620p) [EKSPERIMENTAL] - + 2X (1440p/2160p) 2X (1440p/2160p) - + 3X (2160p/3240p) 3X (2160p/3240p) - + 4X (2880p/4320p) 4X (2880p/4320p) - + 5X (3600p/5400p) 5X (3600p/5400p) - + 6X (4320p/6480p) 6X (4320p/6480p) - + 7X (5040p/7560p) 7X (5040p/7560p) - + 8X (5760p/8640p) 8X (5760p/8640p) - + Nearest Neighbor Nearest Neighbor - + Bilinear Biliner - + Bicubic Bikubik - + Gaussian Gaussian - + Lanczos - + ScaleForce ScaleForce - + AMD FidelityFX Super Resolution - + Area - + MMPX - + Zero-Tangent - + B-Spline - + Mitchell - + Spline-1 - - + + Snapdragon Game Super Resolution + + + + + Snapdragon Game Super Resolution EdgeDir + + + + + None Tak ada - + FXAA FXAA - + SMAA SMAA - + Default (16:9) Bawaan (16:9) - + Force 4:3 Paksa 4:3 - + Force 21:9 Paksa 21:9 - + Force 16:10 Paksa 16:10 - + Stretch to Window Regangkan ke Layar - + Automatic Otomatis - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x - + 32x - + 64x - + Japanese (日本語) Jepang (日本語) - + American English Bahasa Inggris Amerika - + French (français) Prancis (français) - + German (Deutsch) Jerman (Deutsch) - + Italian (italiano) Italia (italiano) - + Spanish (español) Spanyol (español) - + Chinese Cina - + Korean (한국어) Korea (한국어) - + Dutch (Nederlands) Belanda (Nederlands) - + Portuguese (português) Portugis (português) - + Russian (Русский) Rusia (Русский) - + Taiwanese Taiwan - + British English Inggris Britania - + Canadian French Prancis Kanada - + Latin American Spanish Spanyol Amerika Latin - + Simplified Chinese Cina Sederhana - + Traditional Chinese (正體中文) Cina Tradisional (正體中文) - + Brazilian Portuguese (português do Brasil) Portugis Brazil (português do Brasil) - + Polish (polska) - + Thai (แบบไทย) - - + + Japan Jepang - + USA USA - + Europe Eropa - + Australia Australia - + China Tiongkok - + Korea Korea - + Taiwan Taiwan - + Auto (%1) Auto select time zone Auto (%1) - + Default (%1) Default time zone Bawaan (%1) - + CET CET - + CST6CDT CST6CDT - + Cuba Kuba - + EET EET - + Egypt Mesir - + Eire Éire - + EST EST - + EST5EDT EST5EDT - + GB GB - + GB-Eire GB-Éire - + GMT GMT - + GMT+0 GMT+0 - + GMT-0 GMT-0 - + GMT0 GMT0 - + Greenwich Greenwich - + Hongkong Hongkong - + HST HST - + Iceland Islandia - + Iran Iran - + Israel Israel - + Jamaica Jamaika - + Kwajalein Kwajalein - + Libya Libya - + MET MET - + MST MST - + MST7MDT MST7MDT - + Navajo Navajo - + NZ NZ - + NZ-CHAT NZ-CHAT - + Poland Polandia - + Portugal Portugal - + PRC PRC - + PST8PDT PST8PDT - + ROC ROC - + ROK ROK - + Singapore Singapura - + Turkey Turki - + UCT UCT - + Universal Universal - + UTC UTC - + W-SU W-SU - + WET WET - + Zulu Zulu - + Mono Mono - + Stereo Stereo - + Surround Surround - + 4GB DRAM (Default) 4GB DRAM (Bawaan) - + 6GB DRAM (Unsafe) 6GB DRAM (Tidak Aman) - + 8GB DRAM - + 10GB DRAM (Unsafe) 10GB DRAM (Tidak Aman) - + 12GB DRAM (Unsafe) 12GB DRAM (Tidak Aman) - + Docked Terpasang - + Handheld Jinjing - - + + Off - + Boost (1700MHz) - + Fast (2000MHz) - + Always ask (Default) Selalu tanyakan (Bawaan) - + Only if game specifies not to stop Hanya jika permainan menentukan untuk tidak berhenti - + Never ask Jangan pernah bertanya - - + + Medium (256) Sedang (256) - - + + High (512) Tinggi (512) - + Very Small (16 MB) Sangat Kecil (16 MB) - + Small (32 MB) Kecil (32 MB) - + Normal (128 MB) - + Large (256 MB) - + Very Large (512 MB) - + Very Low (4 MB) - + Low (8 MB) - + Normal (16 MB) - + Medium (32 MB) - + High (64 MB) - + Very Low (32) - + Low (64) - + Normal (128) - + Disabled - + ExtendedDynamicState 1 - + ExtendedDynamicState 2 - + ExtendedDynamicState 3 - + Tree View - + Grid View @@ -3275,33 +3306,33 @@ Would you like to delete the old save data? Warna Latar: - + % - FSR sharpening percentage (e.g. 50%) - % + FSR/SGSR sharpening percentage (e.g. 50%) + - + Off Mati - + VSync Off VSync Mati - + Recommended Direkomendasikan - + On Nyala - + VSync On VSync Aktif @@ -4430,7 +4461,7 @@ Untuk membalikkan sumbu, pertama gerakkan joystik secara tegak lurus, lalu menda - + Configure Konfigurasi @@ -4461,7 +4492,7 @@ Untuk membalikkan sumbu, pertama gerakkan joystik secara tegak lurus, lalu menda - + Test Uji coba @@ -4476,77 +4507,77 @@ Untuk membalikkan sumbu, pertama gerakkan joystik secara tegak lurus, lalu menda Hapus Server - + %1:%2 %1:%2 - - - - - - + + + + + + Eden - + Port number has invalid characters Terdapat karakter tidak sah di angka port - + Port has to be in range 0 and 65353 Port harus berada dalam jangkauan 0 dan 65353 - + IP address is not valid Alamat IP tidak sah - + This UDP server already exists Server UDP ini sudah ada - + Unable to add more than 8 servers Tidak dapat menambah lebih dari 8 server - + Testing Menguji - + Configuring Mengkonfigur - + Test Successful Tes Berhasil - + Successfully received data from the server. Berhasil menerima data dari server. - + Test Failed Uji coba Gagal - + Could not receive valid data from the server.<br>Please verify that the server is set up correctly and the address and port are correct. Tidak dapat menerima data yang sah dari server.<br>Mohon periksa bahwa server telah diatur dengan benar dan alamat dan port sudah sesuai. - + UDP Test or calibration configuration is in progress.<br>Please wait for them to finish. Uji coba UDP atau kalibrasi konfigurasi sedang berjalan.<br>Mohon tunggu hingga selesai. @@ -4730,57 +4761,57 @@ Current values are %1% and %2% respectively. Beberapa pengaturan hanya tersedia ketika permainan tidak sedang berjalan. - + Add-Ons Pengaya (Add-On) - + System Sistem - + CPU CPU - + Graphics Grafis - + Adv. Graphics Ljtan. Grafik - + Ext. Graphics - + Audio Audio - + Input Profiles Profil Masukan - + Network - + Applets - + Properties Properti @@ -5789,7 +5820,7 @@ Drag points to change position, or double-click table cells to edit values. - + Calculating... @@ -5991,50 +6022,50 @@ Please go to Configure -> System -> Network and make a selection. GRenderWindow - - + + OpenGL not available! OpenGL tidak tersedia! - + OpenGL shared contexts are not supported. - + Eden has not been compiled with OpenGL support. - - - + + + Error while initializing OpenGL! Terjadi kesalahan menginisialisasi OpenGL! - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. VGA anda mungkin tidak mendukung OpenGL, atau anda tidak memiliki pemacu piranti (driver) grafis terbaharu. - + Error while initializing OpenGL 4.6! Terjadi kesalahan menginisialisasi OpenGL 4.6! - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 VGA anda mungkin tidak mendukung OpenGL 4.6, atau anda tidak memiliki pemacu piranti (driver) grafis terbaharu.<br><br>Pemuat GL:<br>%1 - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 VGA anda mungkin tidak mendukung satu atau lebih ekstensi OpenGL. Mohon pastikan bahwa anda memiliki pemacu piranti (driver) grafis terbaharu.<br><br>Pemuat GL:<br>%1<br><br>Ekstensi yang tidak didukung:<br>%2 - + This build doesn't have OpenGL support. @@ -6042,279 +6073,282 @@ Please go to Configure -> System -> Network and make a selection. GameList - + &Add New Game Directory - + Favorite Favorit - + Start Game Mulai permainan - + Start Game without Custom Configuration - + Open Save Data Location Buka Lokasi Data Penyimpanan - + Open Mod Data Location Buka Lokasi Data Mod - + Open Transferable Pipeline Cache - + Link to Ryujinx - + Remove Singkirkan - + Remove Installed Update - + Remove All Installed DLC - + Remove Custom Configuration - + Remove Cache Storage - + Remove OpenGL Pipeline Cache - + Remove Vulkan Pipeline Cache - + Remove All Pipeline Caches - + Remove All Installed Contents Hapus semua konten terinstall. - + Manage Play Time Kelola waktu bermain - + Edit Play Time Data Edit data waktu bermain - + Remove Play Time Data Hapus data waktu bermain - - + + Dump RomFS Dump RomFS - + Dump RomFS to SDMC - + Verify Integrity Verifikasi Integritas - + Copy Title ID to Clipboard Salin Judul ID ke Clipboard. - + Navigate to GameDB entry Pindah ke tampilan GameDB - + Create Shortcut Buat pintasan - + Add to Desktop Menambahkan ke Desktop - + Add to Applications Menu - + Configure Game - + Scan Subfolders Memindai subfolder - + Remove Game Directory - + ▲ Move Up - + ▼ Move Down - + Open Directory Location Buka Lokasi Direktori - + Clear Bersihkan - - - Name - Nama - - - - Compatibility - Kompatibilitas - - - - Add-ons - Pengaya (Add-On) - - - - File type - Tipe berkas - - - - Size - Ukuran - - - - Play time - Waktu bermain - GameListItemCompat - + Ingame - + Game starts, but crashes or major glitches prevent it from being completed. - + Perfect Sempurna - + Game can be played without issues. Permainan dapat dimainkan tanpa kendala. - + Playable - + Game functions with minor graphical or audio glitches and is playable from start to finish. - + Intro/Menu Awal/Menu - + Game loads, but is unable to progress past the Start Screen. - + Won't Boot Tidak Akan Berjalan - + The game crashes when attempting to startup. Gim rusak saat mencoba untuk memulai. - + Not Tested Belum dites - + The game has not yet been tested. Gim belum pernah dites. + + GameListModel + + + Name + + + + + Compatibility + + + + + Add-ons + + + + + File type + + + + + Size + + + + + Play time + + + GameListPlaceholder - + Double-click to add a new folder to the game list Klik dua kali untuk menambahkan folder sebagai daftar permainan. @@ -6322,17 +6356,17 @@ Please go to Configure -> System -> Network and make a selection. GameListSearchField - + %1 of %n result(s) - + Filter: - + Enter pattern to filter Masukkan pola untuk memfilter @@ -6826,1139 +6860,1099 @@ Debug Message: - + Game &Icon Size - + Reset Window Size to &720p Atur ulang ukuran bingkai ke &720p - + Reset Window Size to 720p Atur ulang ukuran bingkai ke 720p - + Reset Window Size to &900p Atur ulang ukuran bingkai ke &900p - + Reset Window Size to 900p Atur ulang ukuran bingkai ke 900p - + Reset Window Size to &1080p - + Reset Window Size to 1080p - + &Multiplayer - + &Tools - + Am&iibo - + Launch &Applet - + &TAS - + &Create Home Menu Shortcut - + Install &Firmware - + &Help - + &Install Files to NAND... - + L&oad File... - + Load &Folder... - + E&xit - - + + &Pause &Jeda - + &Stop - + &Verify Installed Contents - + &About Eden - + Single &Window Mode - + Con&figure... - + Ctrl+, - + Enable Overlay Display Applet - + Show &Filter Bar - + Show &Status Bar - + Show Status Bar Munculkan Status Bar - + &Browse Public Game Lobby - + &Create Room - + &Leave Room - + &Direct Connect to Room - + &Show Current Room - + F&ullscreen - + &Restart - + Load/Remove &Amiibo... - + &Report Compatibility - + Open &Mods Page - + Open &Quickstart Guide Buka %Panduan cepat - + &FAQ - + &Capture Screenshot - + &Album - + &Set Nickname and Owner - + &Delete Game Data - + &Restore Amiibo - + &Format Amiibo - + &Mii Editor - + &Configure TAS... - + Configure C&urrent Game... - - + + &Start &Mulai - + &Reset - - + + R&ecord R&ekam - + Open &Controller Menu - + Install Decryption &Keys - + &Home Menu - + &Desktop - + &Application Menu - + &Root Data Folder - + &NAND Folder - + &SDMC Folder - + &Mod Folder - + &Log Folder - + From Folder - + From ZIP - + &Eden Dependencies - + &Data Manager - + &Tree View - + &Grid View - + Game Icon Size - - + + None - + Show Game &Name - + Show &Performance Overlay - + + &Carousel View + + + + Small (32x32) - + Standard (64x64) - + Large (128x128) - + Full Size (256x256) - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping - + Loading Web Applet... - - + + Disable Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. - + Unmute - + Mute - + Reset Volume - + &Clear Recent Files - + &Continue - + Warning: Outdated Game Format - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. - - + + Error while loading ROM! - + The ROM format is not supported. - + An error occurred initializing the video core. - + Eden has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-mirror.github.io/help/reference/log-files/'>How to Upload the Log File</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. - + (64-bit) - + (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit - + Closing software... - + Save Data - + Mod Data - + Error Opening %1 Folder - - + + Folder does not exist! - + Remove Installed Game Contents? - + Remove Installed Game Update? - + Remove Installed Game DLC? - + Remove Entry - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? - + Remove Cache Storage? - + Remove File - + Remove Play Time Data - + Reset play time? - - + + RomFS Extraction Failed! - + There was an error copying the RomFS files or the user cancelled the operation. - + Full - + Skeleton - + Select RomFS Dump Mode - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... - - + + Cancel - + RomFS Extraction Succeeded! - + The operation completed successfully. - + Error Opening %1 - + Select Directory - + Properties - + The game properties could not be loaded. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. - + Load File - + Open Extracted ROM Directory - + Invalid Directory Selected - + The directory you have selected does not contain a 'main' file. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files - + %n file(s) remaining - + Installing file "%1"... - - + + Install Results - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application - + System Archive - + System Application Update - + Firmware Package (Type A) - + Firmware Package (Type B) - + Game - + Game Update - + Game DLC - + Delta Title - + Select NCA Install Type... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) - + Failed to Install - + The title type you selected for the NCA is invalid. - + File not found - + File "%1" not found - + OK - + Function Disabled - + Compatibility list reporting is currently disabled. Check back later! - + Error opening URL - + Unable to open the URL "%1". - + TAS Recording - + Overwrite file of player 1? - + Invalid config detected - + Handheld controller can't be used on docked mode. Pro controller will be selected. - - + + Amiibo - - + + The current amiibo has been removed - + Error - - + + The current game is not looking for amiibos - + Amiibo File (%1);; All Files (*.*) - + Load Amiibo - + Error loading Amiibo data - + The selected file is not a valid amiibo - + The selected file is already on use - + An unknown error occurred - - - Keys not installed - - - - - - Install decryption keys and restart Eden before attempting to install firmware. - - - - - Select Dumped Firmware Source Location - - - - - Select Dumped Firmware ZIP - - - - - Zipped Archives (*.zip) - - - - - Firmware cleanup failed - - - - - Failed to clean up extracted firmware cache. -Check write permissions in the system temp directory and try again. -OS reported error: %1 - - - - + No firmware available - + Firmware Corrupted - + Unknown applet - + Applet doesn't map to a known value. - + Record not found - + Applet not found. Please reinstall firmware. - + Capture Screenshot - + PNG Image (*.png) - + TAS state: Running %1/%2 - + TAS state: Recording %1 - + TAS state: Idle %1/%2 - + TAS State: Invalid - + &Stop Running - + Stop R&ecording - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor - + Speed: %1% / %2% - + Speed: %1% - + Game: %1 FPS - + Frame: %1 ms - - - FSR - - - - + NO AA - + VOLUME: MUTE - + VOLUME: %1% Volume percentage (e.g. 50%) - + Derivation Components Missing - + Decryption keys are missing. Install them now? - + Wayland Detected! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -7966,74 +7960,74 @@ Would you like to force it for future launches? - + Use X11 - + Continue with Wayland - + Don't show again - + Restart Required - + Restart Eden to apply the X11 backend. - + Slow - + Turbo - + Unlocked - + Select RomFS Dump Target - + Please select which RomFS you would like to dump. - + Are you sure you want to close Eden? - - - + + + Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? @@ -8099,6 +8093,11 @@ Would you like to bypass this and exit anyway? ScaleForce + + + FSR + + Area @@ -8110,52 +8109,62 @@ Would you like to bypass this and exit anyway? - + + SGSR + + + + + SGSR EdgeDir + + + + Docked - + Handheld - + Fast - + Balanced - + Accurate - + Vulkan - + OpenGL GLSL - + OpenGL SPIRV - + OpenGL GLASM - + Null @@ -8188,7 +8197,7 @@ If you wish to clean up the files which were left in the old data location, you - + Data was migrated successfully. @@ -9037,47 +9046,47 @@ p, li { white-space: pre-wrap; } - + Play Time: %1 - + Never Played - + Version: %1 - + Version: 1.0.0 - + Installed SD Titles - + Installed NAND Titles - + System Titles - + Add New Game Directory Tambahkan direktori permainan - + Favorites @@ -9198,253 +9207,286 @@ p, li { white-space: pre-wrap; } QtCommon::Content - + Game Requires Firmware - + The game you are trying to launch requires firmware to boot or to get past the opening menu. Please <a href='https://yuzu-mirror.github.io/help/quickstart'>dump and install firmware</a>, or press "OK" to launch anyways. - + Installing Firmware... - - - - - + + + + + Cancel - + Firmware Install Failed - + Firmware Install Succeeded - + Firmware integrity verification failed! - - + + Verification failed for the following files: %1 - - + + Verifying integrity... + + + + Integrity verification succeeded! + + - Integrity verification succeeded! - - - - - The operation completed successfully. - - + + Integrity verification failed! - + File contents may be corrupt or missing. - + Integrity verification couldn't be performed - + Firmware installation cancelled, firmware may be in a bad state or corrupted. File contents could not be checked for validity. - + Select Dumped Keys Location - + Decryption Keys install succeeded - + Decryption Keys install failed - + Orphaned Profiles Detected! - + UNEXPECTED BAD THINGS MAY HAPPEN IF YOU DON'T READ THIS!<br>Eden has detected the following save directories with no attached profile:<br>%1<br><br>The following profiles are valid:<br>%2<br><br>Click "OK" to open your save folder and fix up your profiles.<br>Hint: copy the contents of the largest or last-modified folder elsewhere, delete all orphaned profiles, and move your copied contents to the good profile.<br><br>Still confused? See the <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/Orphaned.md'>help page</a>.<br> - + Really clear data? - + Important data may be lost! - + Are you REALLY sure? - + Once deleted, your data will NOT come back! Only do this if you're 100% sure you want to delete this data. - + Clearing... - + Select Export Location - + %1.zip - - + + + Zipped Archives (*.zip) - + Exporting data. This may take a while... - + Exporting - + Exported Successfully - + Data was exported successfully. - + Export Cancelled - + Export was cancelled by the user. - + Export Failed - + Ensure you have write permissions on the targeted directory and try again. - + Select Import Location - + Import Warning - + All previous data in this directory will be deleted. Are you sure you wish to proceed? - + Importing data. This may take a while... - + Importing - + Imported Successfully - + Data was imported successfully. - + Import Cancelled - + Import was cancelled by the user. - + Import Failed - + Ensure you have read permissions on the targeted directory and try again. + + + Keys not installed + + + + + Install decryption keys and restart Eden before attempting to install firmware. + + + + + Select Dumped Firmware Source Location + + + + + Select Dumped Firmware ZIP + + + + + Firmware cleanup failed + + + + + Failed to clean up extracted firmware cache. +Check write permissions in the system temp directory and try again. +OS reported error: %1 + + QtCommon::FS @@ -9688,72 +9730,72 @@ Would you like to manually select a portable folder to use? - + Create Shortcut - + Do you want to launch the game in fullscreen? - + Shortcut Created - + Successfully created a shortcut to %1 - + Shortcut may be Volatile! - + This will create a shortcut to the current AppImage. This may not work well if you update. Continue? - + Failed to Create Shortcut - + Failed to create a shortcut to %1 - + Create Icon - + Cannot create icon file. Path "%1" does not exist and cannot be created. - + No firmware available - + Please install firmware to use the home menu. - + Home Menu Applet - + Home Menu is not available. Please reinstall firmware. @@ -9761,55 +9803,55 @@ Would you like to manually select a portable folder to use? QtCommon::Mod - + Mod Name Nama Mod - + What should this mod be called? - + RomFS RomFS - + ExeFS/Patch - + Cheat Cheat - + Mod Type Tipe Mod - + Could not detect mod type automatically. Please manually specify the type of mod you downloaded. Most mods are RomFS mods, but patches (.pchtxt) are typically ExeFS mods. - - + + Mod Extract Failed - + Failed to create temporary directory %1 - + Zip file %1 is empty @@ -10493,65 +10535,65 @@ By selecting "From Eden", previous save data stored in Ryujinx will be - + New Version Location - + All Files (*.*) - - - + + + Failed to save file - + Could not open file %1 for writing. - + Downloading... - + Cancel - + Could not write to file %1. - + Could not commit to file %1. - + Failed to download file - + Could not download from %1%2 Error code: %3 - + Download Complete - + Successfully downloaded %1. Would you like to open it? diff --git a/dist/languages/it.ts b/dist/languages/it.ts index d85159bdc2..36b676040d 100644 --- a/dist/languages/it.ts +++ b/dist/languages/it.ts @@ -724,8 +724,8 @@ Opzioni inferiori a 1X possono causare artefatti. - Determines how sharpened the image will look using FSR's dynamic contrast. - Determina quanto sarà nitida l'immagine utilizzando il contrasto dinamico di FSR. + Determines how sharpened the image will look using FSR's or SGSR's dynamic contrast. + @@ -782,24 +782,24 @@ Disabling it is only intended for debugging. Disabilitarla ha senso solo quando si effettua il debug. - + Use asynchronous GPU emulation Abilita l'emulazione asincrona della GPU - + Uses an extra CPU thread for rendering. This option should always remain enabled. Utilizza un thread aggiuntivo della CPU per il rendering. Quest'opzione dovrebbe sempre rimanere abilitata. - + NVDEC emulation: Emulazione NVDEC: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. @@ -808,12 +808,12 @@ In most cases, GPU decoding provides the best performance. Nella maggior parte dei casi, la decodifica tramite GPU fornisce prestazioni migliori. - + ASTC Decoding Method: Metodo di decodifica ASTC: - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -825,12 +825,12 @@ GPU: Usa i compute shader della GPU per decodificare le texture ASTC (consigliat CPU (Asincrono): Usa la CPU per decodificare le texture ASTC se richiesto. Elimina lo stuttering causato dalla decodifica ma potrebbe generare artefatti visivi. - + ASTC Recompression Method: Metodo di ricompressione ASTC: - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. @@ -838,44 +838,55 @@ BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, BC1/BC3: Il formato intermedio sarà ricompresso nel formato BC1 o BC3, risparmiando VRAM ma peggiorando la qualità visiva. - + Frame Pacing Mode (Vulkan only) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. - + VRAM Usage Mode: Modalità di utilizzo della VRAM: - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. Determina se l'emulatore dovrebbe risparmiare memoria o utilizzarne il più possibile per massimizzare le prestazioni. La modalità aggressiva potrebbe impattare sulle prestazioni di altre applicazioni, come i programmi di registrazione. - + Skip CPU Inner Invalidation Salta invalidamento interno CPU - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. Salta alcuni invalidamenti della cache durante gli aggiornamenti della memoria, riducendo l'uso della CPU e migliorando la latenza. Questo potrebbe causare soft-crash. - + + Anti-Flicker + + + + + Forces GPU fence callbacks to wait for submitted GPU work. +Use with Fast GPU mode, to avoid flicker with lower performance impact. + + + + VSync Mode: Modalità VSync: - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -886,12 +897,12 @@ Mailbox può avere una latenza minore di FIFO e non ha tearing ma potrebbe perde Immediate (no sincronizzazione) mostra tutto il disponibile, quindi anche tearing. - + Sync Memory Operations Sincronizza operazioni di memoria - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. @@ -900,44 +911,44 @@ Questa opzione dovrebbe risolvere problemi in alcuni giochi, ma potrebbe ridurre I giochi con Unreal Engine 4 sembrano essere i più colpiti. - + Enable asynchronous presentation (Vulkan only) Abilita la presentazione asincrona (solo Vulkan) - + Slightly improves performance by moving presentation to a separate CPU thread. Migliora di poco le prestazioni spostando la presentazione su un thread della CPU separato. - + Force maximum clocks (Vulkan only) Forza clock massimi (solo Vulkan) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. Esegue del lavoro in background durante l'attesa dei comandi grafici per evitare che la GPU diminuisca la sua velocità di clock. - + Anisotropic Filtering: Filtro anisotropico: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. Controlla la qualità del rendering delle texture negli angoli obliqui. Si può utilizzare in modo sicuro al 16x su quasi tutte le GPU. - + GPU Mode: Modalità GPU: - + Controls the GPU emulation mode. Most games render fine with Fast or Balanced modes, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. @@ -946,101 +957,101 @@ La maggior parte dei giochi viene renderizzata senza problemi con le modalità & I particellari tendono a essere renderizzati correttamente solo in modalità "Accurata". - + DMA Accuracy: Precisione DMA: - + Controls the DMA precision accuracy. Safe precision fixes issues in some games but may degrade performance. Controlla la precisione del DMA. La precisione "Sicura" risolve dei problemi in alcuni giochi, ma può ridurre le prestazioni. - + Enable asynchronous shader compilation Abilita la compilazione asincrona degli shader - + May reduce shader stutter. Può ridurre i fenomeni di stuttering (scatti) causati dagli shader. - + Fast GPU Time Tempo GPU veloce - + Overclocks the emulated GPU to increase dynamic resolution and render distance. Use 256 for maximal performance and 512 for maximal graphics fidelity. Aumenta il clock della GPU emulata per aumentare la risoluzione dinamica e la distanza di rendering. Usa 256 per massimizzare le prestazioni e 512 per massimizzare la qualità visiva. - + GPU Unswizzle - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. - + GPU Unswizzle Max Texture Size - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. - + GPU Unswizzle Stream Size - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. - + GPU Unswizzle Chunk Size - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. - + Use Vulkan pipeline cache Usa la cache delle pipeline di Vulkan - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. Abilita la cache delle pipeline specifica del produttore della GPU. Quest'opzione può ridurre di molto i tempi di caricamento degli shader nei casi in cui il driver Vulkan non memorizza la cache delle pipeline internamente. - + Enable Compute Pipelines (Intel Vulkan Only) Abilita le compute pipeline (solo per Vulkan su Intel) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. @@ -1049,182 +1060,192 @@ Quest'impostazione esiste solo per i driver proprietari Intel e potrebbe fa Le compute pipelines sono sempre attivate su tutti gli altri drivers. - + Enable Reactive Flushing Abilita il flushing reattivo - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. Utilizza il flushing reattivo invece di quello predittivo, al fine di ottenere una sincronizzazione della memoria più accurata. - + Sync to framerate of video playback Sincronizza il framerate a quello del video - + Run the game at normal speed during video playback, even when the framerate is unlocked. Esegue il gioco a velocità normale durante le cutscene, anche quando il framerate è sbloccato. - + Barrier feedback loops Barrier feedback loops - + Improves rendering of transparency effects in specific games. Migliora il rendering degli effetti di trasparenza in alcuni giochi. - + Enable buffer history - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. - + Fix bloom effects - + Removes bloom in Burnout. - + Enable Legacy Rescale Pass - + May fix rescale issues in some games by relying on behavior from the previous implementation. Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. - + Extended Dynamic State Stato dinamico esteso - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. Controlla il numero di funzionalità che possono essere usate con lo stato dinamico esteso. Gli stati più alti consentono più funzionalità e possono migliorare le prestazioni, ma possono causare ulteriori problemi grafici. - + Vertex Input Dynamic State - + Enables vertex input dynamic state feature for better quality and performance. - + Sample Shading Sample shading - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. Permette al fragment shader di eseguire per campione in un frammento multi-campione invece che una volta per frammento. Migliora la qualità grafica a scapito delle prestazioni. Alti valori migliorano la qualità ma peggiorano le prestazioni. - + RNG Seed Seed RNG - + Controls the seed of the random number generator. Mainly used for speedrunning. Controlla il seme del generatore di numeri casuali. Principalmente utilizzato per le speedrun. - + Device Name Nome del dispositivo - + The name of the console. Il nome della console. - + + Homebrew Args + + + + + Command-line arguments passed to homebrew at launch (e.g. -noglsl). + + + + Custom RTC Date: Data RTC personalizzata: - + This option allows to change the clock of the console. Can be used to manipulate time in games. Quest'opzione permette di modificare l'orologio della console. Può essere usato per manipolare il tempo nei giochi. - + The number of seconds from the current unix time Il numero di secondi dal tempo Unix attuale - + Language: Lingua: - + This option can be overridden when region setting is auto-select Può essere rimpiazzato se il fuso orario della Regione è impostato su Auto - + Region: Regione: - + The region of the console. La regione della console. - + Time Zone: Fuso orario: - + The time zone of the console. Il fuso orario della console. - + Sound Output Mode: Modalità di output del suono: - + Console Mode: Modalità console: - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. @@ -1233,1031 +1254,1041 @@ I giochi ne terranno conto e modificheranno la risoluzione, i dettagli e i contr Impostare l'opzione su "Portatile" può aiutare a migliorare le prestazioni sui sistemi meno potenti. - + Prompt for user profile on boot Scegli il profilo utente all'avvio - + Useful if multiple people use the same PC. Utile se più persone utilizzano lo stesso PC. - + Pause when not in focus Metti in pausa quando la finestra non è in primo piano - + Pauses emulation when focusing on other windows. Mette in pausa l'emulazione quando altre finestre sono in primo piano. - + Confirm before stopping emulation Chiedi conferma prima di arrestare l'emulazione - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. Sovrascrive le richieste di conferma per fermare l'emulazione. Abilitarla bypassa queste richieste e l'emulazione cesserà immediatamente. - + Hide mouse on inactivity Nascondi il puntatore del mouse se inattivo - + Hides the mouse after 2.5s of inactivity. Nasconde il mouse dopo 2,5 secondi di inattività. - + Disable controller applet Disabilita l'applet controller - + Forcibly disables the use of the controller applet in emulated programs. When a program attempts to open the controller applet, it is immediately closed. Disabilita forzatamente l'uso dell'applet del controller nei programmi emulati. Quando un programma proverà ad aprire l'applet del controller, quest'ultimo verrà immediatamente chiuso. - + Check for updates Controlla la presenza di aggiornamenti - + Whether or not to check for updates upon startup. Determina se controllare o meno la presenza di aggiornamenti all'avvio. - + Enable Gamemode Abilita Gamemode - + Force X11 as Graphics Backend Forza l'uso del back-end grafico X11 - + Custom frontend Frontend personalizzato - + Real applet Applet reale - + Never Mai - + On Load Al caricamento - + Always Sempre - + CPU CPU - + GPU GPU - + CPU Asynchronous CPU (Asincrono) - + Uncompressed (Best quality) Nessuna compressione (qualità migliore) - + BC1 (Low quality) BC1 (qualità bassa) - + BC3 (Medium quality) BC3 (qualità media) - - + + Auto Automatico - + 30 FPS 30 FPS - + 60 FPS 60 FPS - + 90 FPS 90 FPS - + 120 FPS 120 FPS - + Conservative Conservativa - + Aggressive Aggressiva - + Vulkan Vulkan - + OpenGL GLSL OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) OpenGL GLASM (shader assembly, solo NVIDIA) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) OpenGL SPIR-V (sperimentale, solo AMD/MESA) - + Null Nullo - + Fast Veloce - + Balanced Bilanciata - - + + Accurate Accurata - - + + Default Predefinito - + Unsafe (fast) Non sicuro (veloce) - + Safe (stable) Sicuro (stabile) - + Unsafe Non sicura - + Paranoid (disables most optimizations) Paranoica (disabilita la maggior parte delle ottimizzazioni) - + Debugging Debug - + Dynarmic Dynarmic - + NCE NCE - + Borderless Windowed Finestra senza bordi - + Exclusive Fullscreen Schermo intero esclusivo - + No Video Output Nessun output video - + CPU Video Decoding Decodifica video CPU - + GPU Video Decoding (Default) Decodifica video GPU (predefinita) - + 0.25X (180p/270p) [EXPERIMENTAL] 0.25X (180p/270p) [SPERIMENTALE] - + 0.5X (360p/540p) [EXPERIMENTAL] 0.5X (360p/540p) [SPERIMENTALE] - + 0.75X (540p/810p) [EXPERIMENTAL] 0.75X (540p/810p) [SPERIMENTALE] - + 1X (720p/1080p) 1X (720p/1080p) - + 1.25X (900p/1350p) [EXPERIMENTAL] 1.25X (900p/1350p) [SPERIMENTALE] - + 1.5X (1080p/1620p) [EXPERIMENTAL] 1.5X (1080p/1620p) [SPERIMENTALE] - + 2X (1440p/2160p) 2X (1440p/2160p) - + 3X (2160p/3240p) 3X (2160p/3240p) - + 4X (2880p/4320p) 4X (2880p/4320p) - + 5X (3600p/5400p) 5X (3600p/5400p) - + 6X (4320p/6480p) 6X (4320p/6480p) - + 7X (5040p/7560p) 7X (5040p/7560p) - + 8X (5760p/8640p) 8X (5760p/8640p) - + Nearest Neighbor Nearest neighbor - + Bilinear Bilineare - + Bicubic Bicubico - + Gaussian Gaussiano - + Lanczos Lanczos - + ScaleForce ScaleForce - + AMD FidelityFX Super Resolution AMD FidelityFX Super Resolution - + Area Area - + MMPX MMPX - + Zero-Tangent Zero-Tangent - + B-Spline B-Spline - + Mitchell Mitchell - + Spline-1 Spline-1 - - + + Snapdragon Game Super Resolution + + + + + Snapdragon Game Super Resolution EdgeDir + + + + + None Nessuna - + FXAA FXAA - + SMAA SMAA - + Default (16:9) Predefinito (16:9) - + Force 4:3 Forza 4:3 - + Force 21:9 Forza 21:9 - + Force 16:10 Forza 16:10 - + Stretch to Window Allunga a finestra - + Automatic Automatico - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x - + 32x 32x - + 64x 64x - + Japanese (日本語) Giapponese (日本語) - + American English Inglese americano - + French (français) Francese (français) - + German (Deutsch) Tedesco (Deutsch) - + Italian (italiano) Italiano - + Spanish (español) Spagnolo (español) - + Chinese Cinese - + Korean (한국어) Coreano (한국어) - + Dutch (Nederlands) Olandese (Nederlands) - + Portuguese (português) Portoghese (português) - + Russian (Русский) Russo (Русский) - + Taiwanese Taiwanese - + British English Inglese britannico - + Canadian French Francese canadese - + Latin American Spanish Spagnolo latino-americano - + Simplified Chinese Cinese semplificato - + Traditional Chinese (正體中文) Cinese tradizionale (正體中文) - + Brazilian Portuguese (português do Brasil) Portoghese brasiliano (português do Brasil) - + Polish (polska) Polacco (polska) - + Thai (แบบไทย) Thailandese (แบบไทย) - - + + Japan Giappone - + USA USA - + Europe Europa - + Australia Australia - + China Cina - + Korea Corea - + Taiwan Taiwan - + Auto (%1) Auto select time zone Automatico (%1) - + Default (%1) Default time zone Predefinito (%1) - + CET CET - + CST6CDT CST6CDT - + Cuba Cuba - + EET EET - + Egypt Egitto - + Eire Eire - + EST EST - + EST5EDT EST5EDT - + GB GB - + GB-Eire GB-Eire - + GMT GMT - + GMT+0 GMT+0 - + GMT-0 GMT-0 - + GMT0 GMT0 - + Greenwich Greenwich - + Hongkong Hongkong - + HST HST - + Iceland Islanda - + Iran Iran - + Israel Israele - + Jamaica Giamaica - + Kwajalein Kwajalein - + Libya Libia - + MET MET - + MST MST - + MST7MDT MST7MDT - + Navajo Navajo - + NZ NZ - + NZ-CHAT NZ-CHAT - + Poland Polonia - + Portugal Portogallo - + PRC PRC - + PST8PDT PST8PDT - + ROC ROC - + ROK ROK - + Singapore Singapore - + Turkey Turchia - + UCT UCT - + Universal Universale - + UTC UTC - + W-SU W-SU - + WET WET - + Zulu Zulu - + Mono Mono - + Stereo Stereo - + Surround Surround - + 4GB DRAM (Default) 4GB DRAM (Predefinito) - + 6GB DRAM (Unsafe) 6GB DRAM (Non sicuro) - + 8GB DRAM 8GB DRAM - + 10GB DRAM (Unsafe) 10GB DRAM (Non sicuro) - + 12GB DRAM (Unsafe) 12GB DRAM (Non sicuro) - + Docked Dock - + Handheld Portatile - - + + Off Disattivato - + Boost (1700MHz) Boost (1700MHz) - + Fast (2000MHz) Veloce (2000MHz) - + Always ask (Default) Chiedi sempre (Predefinito) - + Only if game specifies not to stop Solo se il gioco richiede di non essere arrestato - + Never ask Non chiedere mai - - + + Medium (256) Medio (256) - - + + High (512) Alto (512) - + Very Small (16 MB) Molto piccola (16 MB) - + Small (32 MB) Piccola (32 MB) - + Normal (128 MB) Normale (128 MB) - + Large (256 MB) Grande (256 MB) - + Very Large (512 MB) Molto grande (512 MB) - + Very Low (4 MB) Molto bassa (4 MB) - + Low (8 MB) Bassa (8 MB) - + Normal (16 MB) Normale (16 MB) - + Medium (32 MB) Media (32 MB) - + High (64 MB) Alta (64 MB) - + Very Low (32) Molto basso (32) - + Low (64) Basso (64) - + Normal (128) Normale (128) - + Disabled Disabilitato - + ExtendedDynamicState 1 Stato dinamico esteso 1 - + ExtendedDynamicState 2 Stato dinamico esteso 2 - + ExtendedDynamicState 3 Stato dinamico esteso 3 - + Tree View Vista ad albero - + Grid View Vista a griglia @@ -3322,33 +3353,33 @@ Vuoi cancellare i dati precedenti? Colore dello sfondo: - + % - FSR sharpening percentage (e.g. 50%) - % + FSR/SGSR sharpening percentage (e.g. 50%) + - + Off Disattivato - + VSync Off VSync disattivato - + Recommended Consigliata - + On Attivato - + VSync On VSync attivato @@ -4477,7 +4508,7 @@ Per invertire gli assi, prima muovi la levetta verticalmente, e poi orizzontalme - + Configure Configura @@ -4508,7 +4539,7 @@ Per invertire gli assi, prima muovi la levetta verticalmente, e poi orizzontalme - + Test Test @@ -4523,77 +4554,77 @@ Per invertire gli assi, prima muovi la levetta verticalmente, e poi orizzontalme Rimuovi un server - + %1:%2 %1:%2 - - - - - - + + + + + + Eden Eden - + Port number has invalid characters Il numero di porta contiene caratteri non validi - + Port has to be in range 0 and 65353 La valore della porta deve essere compreso tra 0 e 65353 inclusi - + IP address is not valid Indirizzo IP non valido - + This UDP server already exists Questo server UDP esiste già - + Unable to add more than 8 servers Impossibile aggiungere più di 8 server - + Testing Testando - + Configuring Configurando - + Test Successful Test riuscito - + Successfully received data from the server. Ricevuti con successo dati dal server. - + Test Failed Test fallito - + Could not receive valid data from the server.<br>Please verify that the server is set up correctly and the address and port are correct. Impossibile ricevere dati validi dal server.<br> Verificare che il server sia impostato correttamente e che indirizzo e porta siano corretti. - + UDP Test or calibration configuration is in progress.<br>Please wait for them to finish. È in corso il test UDP o la configurazione della calibrazione,<br> attendere che finiscano. @@ -4782,57 +4813,57 @@ Per attivarlo, disattiva il mouse emulato. Alcune impostazioni sono disponibili soltanto quando un gioco non è in esecuzione. - + Add-Ons Add-on - + System Sistema - + CPU CPU - + Graphics Grafica - + Adv. Graphics Grafica (Avanzate) - + Ext. Graphics Grafica (Estensioni) - + Audio Audio - + Input Profiles Profili di input - + Network Rete - + Applets Applet - + Properties Proprietà @@ -5848,7 +5879,7 @@ Trascina i punti per cambiare posizione, oppure clicca due volte la cella in tab Importa dati in questa cartella. Potrebbe impiegare un po' di tempo, e TUTTI I DATI ESISTENTI verranno cancellati! - + Calculating... Calcolo in corso... @@ -6052,50 +6083,50 @@ Vai su Configura -> Sistema -> Rete e selezionane una. GRenderWindow - - + + OpenGL not available! OpenGL non disponibile! - + OpenGL shared contexts are not supported. Gli shared context di OpenGL non sono supportati. - + Eden has not been compiled with OpenGL support. Eden non è stato compilato con il supporto a OpenGL. - - - + + + Error while initializing OpenGL! Errore durante l'inizializzazione di OpenGL! - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. La tua GPU potrebbe non supportare OpenGL, o non hai installato l'ultima versione dei driver video. - + Error while initializing OpenGL 4.6! Errore durante l'inizializzazione di OpenGL 4.6! - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 La tua GPU potrebbe non supportare OpenGL 4.6, o non hai installato l'ultima versione dei driver video.<br><br>Renderer GL:<br>%1 - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 La tua GPU potrebbe non supportare una o più estensioni OpenGL richieste. Assicurati di aver installato i driver video più recenti.<br><br>Renderer GL:<br>%1<br><br>Estensioni non supportate:<br>%2 - + This build doesn't have OpenGL support. @@ -6103,279 +6134,282 @@ Vai su Configura -> Sistema -> Rete e selezionane una. GameList - + &Add New Game Directory &Aggiungi nuova cartella dei giochi - + Favorite Preferito - + Start Game Avvia gioco - + Start Game without Custom Configuration Avvia gioco senza la configurazione personalizzata - + Open Save Data Location Apri la cartella dei dati di salvataggio - + Open Mod Data Location Apri la cartella delle mod - + Open Transferable Pipeline Cache Apri la cartella della cache trasferibile delle pipeline - + Link to Ryujinx Collega con Ryujinx - + Remove Rimuovi - + Remove Installed Update Rimuovi l'aggiornamento installato - + Remove All Installed DLC Rimuovi tutti i DLC installati - + Remove Custom Configuration Rimuovi la configurazione personalizzata - + Remove Cache Storage Rimuovi la cache del gioco - + Remove OpenGL Pipeline Cache Rimuovi la cache delle pipeline OpenGL - + Remove Vulkan Pipeline Cache Rimuovi la cache delle pipeline Vulkan - + Remove All Pipeline Caches Rimuovi tutte le cache delle pipeline - + Remove All Installed Contents Rimuovi tutti i contenuti installati - + Manage Play Time Gestisci il tempo di gioco - + Edit Play Time Data Modifica il tempo di gioco - + Remove Play Time Data Reimposta il tempo di gioco - - + + Dump RomFS Estrai RomFS - + Dump RomFS to SDMC Estrai RomFS su SDMC - + Verify Integrity Verifica integrità - + Copy Title ID to Clipboard Copia il Title ID negli appunti - + Navigate to GameDB entry Vai alla pagina di GameDB - + Create Shortcut Crea scorciatoia - + Add to Desktop Aggiungi al desktop - + Add to Applications Menu Aggiungi al menù delle applicazioni - + Configure Game Configura gioco - + Scan Subfolders Scansiona le sottocartelle - + Remove Game Directory Rimuovi cartella dei giochi - + ▲ Move Up ▲ Sposta in alto - + ▼ Move Down ▼ Sposta in basso - + Open Directory Location Apri cartella - + Clear Cancella - - - Name - Nome - - - - Compatibility - Compatibilità - - - - Add-ons - Add-on - - - - File type - Tipo di file - - - - Size - Dimensione - - - - Play time - Tempo di gioco - GameListItemCompat - + Ingame In-game - + Game starts, but crashes or major glitches prevent it from being completed. Il gioco parte, ma non può essere completato a causa di arresti anomali o di glitch importanti. - + Perfect Perfetto - + Game can be played without issues. Il gioco funziona senza problemi. - + Playable Giocabile - + Game functions with minor graphical or audio glitches and is playable from start to finish. Il gioco presenta alcuni glitch audio o video minori ed è possibile giocare dall'inizio alla fine. - + Intro/Menu Intro/Menù - + Game loads, but is unable to progress past the Start Screen. Il gioco si avvia, ma è impossibile proseguire oltre la schermata iniziale. - + Won't Boot Non si avvia - + The game crashes when attempting to startup. Il gioco si blocca quando viene avviato. - + Not Tested Non testato - + The game has not yet been tested. Il gioco non è ancora stato testato. + + GameListModel + + + Name + + + + + Compatibility + + + + + Add-ons + + + + + File type + + + + + Size + + + + + Play time + + + GameListPlaceholder - + Double-click to add a new folder to the game list Clicca due volte per aggiungere una nuova cartella alla lista dei giochi @@ -6383,17 +6417,17 @@ Vai su Configura -> Sistema -> Rete e selezionane una. GameListSearchField - + %1 of %n result(s) %1 di %n risultato%1 di %n di risultati%1 di %n risultati - + Filter: Filtro: - + Enter pattern to filter Inserisci pattern per filtrare @@ -6889,772 +6923,777 @@ Messaggio debug: &Modalità della lista dei giochi - + Game &Icon Size Dimensione delle &icone dei giochi - + Reset Window Size to &720p Ripristina le dimensioni della finestra a &720p - + Reset Window Size to 720p Ripristina le dimensioni della finestra a 720p - + Reset Window Size to &900p Ripristina le dimensioni della finestra a &900p - + Reset Window Size to 900p Ripristina le dimensioni della finestra a 900p - + Reset Window Size to &1080p Ripristina le dimensioni della finestra a &1080p - + Reset Window Size to 1080p Ripristina le dimensioni della finestra a 1080p - + &Multiplayer &Multigiocatore - + &Tools &Strumenti - + Am&iibo Am&iibo - + Launch &Applet Avvia &applet - + &TAS &TAS - + &Create Home Menu Shortcut &Crea scorciatoia per il menù Home - + Install &Firmware Installa &firmware - + &Help &Aiuto - + &Install Files to NAND... &Installa file su NAND... - + L&oad File... Carica &file... - + Load &Folder... Carica &cartella... - + E&xit &Esci - - + + &Pause &Pausa - + &Stop Arre&sta - + &Verify Installed Contents &Verifica i contenuti installati - + &About Eden &Informazioni su Eden - + Single &Window Mode &Modalità finestra singola - + Con&figure... Configura... - + Ctrl+, Ctrl+, - + Enable Overlay Display Applet - + Show &Filter Bar Mostra barra del &filtro - + Show &Status Bar Mostra barra di &stato - + Show Status Bar Mostra barra di stato - + &Browse Public Game Lobby &Sfoglia lobby di gioco pubblica - + &Create Room &Crea stanza - + &Leave Room &Esci dalla stanza - + &Direct Connect to Room Collegamento &diretto a una stanza - + &Show Current Room &Mostra stanza attuale - + F&ullscreen Schermo intero - + &Restart &Riavvia - + Load/Remove &Amiibo... Carica/Rimuovi &Amiibo... - + &Report Compatibility &Segnala la compatibilità - + Open &Mods Page Apri la pagina delle &mod - + Open &Quickstart Guide Apri la &guida introduttiva - + &FAQ &Domande frequenti - + &Capture Screenshot Cattura schermo - + &Album &Album - + &Set Nickname and Owner &Imposta nickname e proprietario - + &Delete Game Data &Rimuovi i dati di gioco - + &Restore Amiibo &Ripristina gli Amiibo - + &Format Amiibo &Formatta gli Amiibo - + &Mii Editor Editor &Mii - + &Configure TAS... &Configura TAS... - + Configure C&urrent Game... Configura il gioco in uso... - - + + &Start &Avvia - + &Reset &Reimposta - - + + R&ecord R&egistra - + Open &Controller Menu Apri il menù dei &controller - + Install Decryption &Keys Installa le &chiavi di crittografia - + &Home Menu Menù &Home - + &Desktop &Desktop - + &Application Menu &Menù delle applicazioni - + &Root Data Folder Cartella &principale dei dati - + &NAND Folder Cartella &NAND - + &SDMC Folder Cartella &SDMC - + &Mod Folder Cartella delle &mod - + &Log Folder Cartella dei &log - + From Folder Da una cartella - + From ZIP Da un file ZIP - + &Eden Dependencies &Dipendenze di Eden - + &Data Manager Gestione &dati - + &Tree View Vista ad &albero - + &Grid View Vista a &griglia - + Game Icon Size Dimensione delle icone dei giochi - - + + None Nessuno - + Show Game &Name Mostra i nomi dei giochi - + Show &Performance Overlay Mostra &prestazioni in sovrimpressione - + + &Carousel View + + + + Small (32x32) Piccola (32x32) - + Standard (64x64) Normale (64x64) - + Large (128x128) Grande (128x128) - + Full Size (256x256) Intera (256x256) - + Broken Vulkan Installation Detected Rilevata installazione di Vulkan non funzionante - + Vulkan initialization failed during boot. L'inizializzazione di Vulkan è fallita durante l'avvio. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping Gioco in esecuzione - + Loading Web Applet... Caricamento dell'applet web... - - + + Disable Web Applet Disabilita l'applet web - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) Disabilitare l'applet web potrebbe causare dei comportamenti indesiderati e andrebbe fatto solo con Super Mario 3D All-Stars. Sei sicuro di voler procedere? (Puoi riabilitarlo quando vuoi nelle impostazioni di debug.) - + The amount of shaders currently being built Il numero di shader in fase di compilazione - + The current selected resolution scaling multiplier. Il moltiplicatore attuale della risoluzione. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. Velocità attuale dell'emulazione. Valori più alti o più bassi di 100% indicano che l'emulazione sta funzionando più velocemente o lentamente rispetto a una Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Il numero di fotogrammi al secondo che il gioco sta attualmente renderizzando. Può variare in base al gioco e alla situazione. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Tempo necessario per emulare un fotogramma della Switch, senza tenere conto del limite al framerate o del V-Sync. Per un'emulazione alla massima velocità, il valore non dovrebbe essere superiore a 16.67 ms. - + Unmute Riattiva - + Mute Silenzia - + Reset Volume Reimposta volume - + &Clear Recent Files &Cancella i file recenti - + &Continue &Continua - + Warning: Outdated Game Format Attenzione: Formato del gioco obsoleto - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. Stai usando una cartella contenente una ROM decostruita per avviare questo gioco, che è un formato obsoleto e sostituito da NCA, NAX, XCI o NSP. Le ROM decostruite non hanno icone né metadati e non supportano gli aggiornamenti.<br>Per una spiegazione sui vari formati della console Switch supportati da Eden, consulta il manuale utente. Non riceverai di nuovo questo avviso. - - + + Error while loading ROM! Errore nel caricamento della ROM! - + The ROM format is not supported. Il formato della ROM non è supportato. - + An error occurred initializing the video core. Errore durante l'inizializzazione del componente video di base - + Eden has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-mirror.github.io/help/reference/log-files/'>How to Upload the Log File</a>. Eden ha riscontrato un errore durante l'esecuzione del componente video di base. Di solito questo errore è causato da driver GPU obsoleti, compresi quelli integrati. Consulta il log per maggiori dettagli. Per informazioni su come accedere al log, visita <a href='https://yuzu-mirror.github.io/help/reference/log-files/'>questa pagina</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. Errore nel caricamento della ROM! %1 - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. %1<br>Esegui un nuovo dump dei tuoi file o chiedi aiuto su Discord/Stoat. - + An unknown error occurred. Please see the log for more details. Si è verificato un errore sconosciuto. Consulta il log per maggiori dettagli. - + (64-bit) (64 bit) - + (32-bit) (32 bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Closing software... Chiusura del software in corso... - + Save Data Dati di salvataggio - + Mod Data Dati delle mod - + Error Opening %1 Folder Impossibile aprire la cartella %1 - - + + Folder does not exist! La cartella non esiste! - + Remove Installed Game Contents? Rimuovere il contenuto del gioco installato? - + Remove Installed Game Update? Rimuovere l'aggiornamento installato? - + Remove Installed Game DLC? Rimuovere il DLC installato? - + Remove Entry Rimuovi voce - + Delete OpenGL Transferable Shader Cache? Vuoi rimuovere la cache trasferibile degli shader OpenGL? - + Delete Vulkan Transferable Shader Cache? Vuoi rimuovere la cache trasferibile degli shader Vulkan? - + Delete All Transferable Shader Caches? Vuoi rimuovere tutte le cache trasferibili degli shader? - + Remove Custom Game Configuration? Rimuovere la configurazione personalizzata del gioco? - + Remove Cache Storage? Rimuovere la cache del gioco? - + Remove File Rimuovi file - + Remove Play Time Data Reimposta il tempo di gioco - + Reset play time? Vuoi reimpostare il tempo di gioco? - - + + RomFS Extraction Failed! Estrazione RomFS fallita! - + There was an error copying the RomFS files or the user cancelled the operation. Si è verificato un errore durante la copia dei file del RomFS o l'operazione è stata annullata dall'utente. - + Full Completa - + Skeleton Cartelle - + Select RomFS Dump Mode Seleziona la modalità di estrazione del RomFS - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. Seleziona come vorresti estrarre il RomFS. <br>La modalità Completa copierà tutti i file in una nuova cartella mentre<br>la modalità Cartelle creerà solamente la struttura delle cartelle. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root Non c'è abbastanza spazio disponibile in %1 per estrarre il RomFS. Libera lo spazio o seleziona una cartella di estrazione diversa in Emulazione > Configura > Sistema > File system > Cartella di estrazione - + Extracting RomFS... Estrazione RomFS in corso... - - + + Cancel Annulla - + RomFS Extraction Succeeded! Estrazione RomFS riuscita! - + The operation completed successfully. L'operazione è stata completata con successo. - + Error Opening %1 Impossibile aprire %1 - + Select Directory Seleziona cartella - + Properties Proprietà - + The game properties could not be loaded. Non è stato possibile caricare le proprietà del gioco. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Eseguibile Switch (%1);;Tutti i file (*.*) - + Load File Carica file - + Open Extracted ROM Directory Apri la cartella della ROM estratta - + Invalid Directory Selected Cartella selezionata non valida - + The directory you have selected does not contain a 'main' file. La cartella che hai selezionato non contiene un file "main". - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) File Switch installabili (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files Installa file - + %n file(s) remaining %n file rimanente%n di file rimanenti%n file rimanenti - + Installing file "%1"... Installazione del file "%1"... - - + + Install Results Risultati dell'installazione - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. Per evitare possibli conflitti, sconsigliamo di installare i giochi base su NAND. Usa questa funzione solo per installare aggiornamenti e DLC. - + %n file(s) were newly installed %n nuovo file è stato installato @@ -7663,7 +7702,7 @@ Usa questa funzione solo per installare aggiornamenti e DLC. - + %n file(s) were overwritten %n file è stato sovrascritto @@ -7672,7 +7711,7 @@ Usa questa funzione solo per installare aggiornamenti e DLC. - + %n file(s) failed to install %n file non è stato installato a causa di errori @@ -7681,359 +7720,314 @@ Usa questa funzione solo per installare aggiornamenti e DLC. - + System Application Applicazione di sistema - + System Archive Archivio di sistema - + System Application Update Aggiornamento di un'applicazione di sistema - + Firmware Package (Type A) Pacchetto firmware (tipo A) - + Firmware Package (Type B) Pacchetto firmware (tipo B) - + Game Gioco - + Game Update Aggiornamento di gioco - + Game DLC DLC - + Delta Title Titolo delta - + Select NCA Install Type... Seleziona il tipo di installazione NCA... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) Seleziona il tipo del file NCA da installare: (Nella maggior parte dei casi, il valore predefinito "Gioco" va bene) - + Failed to Install Installazione fallita - + The title type you selected for the NCA is invalid. Il tipo che hai selezionato per il file NCA non è valido. - + File not found File non trovato - + File "%1" not found File "%1" non trovato - + OK OK - + Function Disabled Funzionalità disabilitata - + Compatibility list reporting is currently disabled. Check back later! La segnalazione della compatibilità è al momento disabilitata. Torna a controllare più avanti! - + Error opening URL Impossibile aprire l'URL - + Unable to open the URL "%1". Non è stato possibile aprire l'URL "%1". - + TAS Recording Registrazione TAS - + Overwrite file of player 1? Vuoi sovrascrivere il file del giocatore 1? - + Invalid config detected Rilevata configurazione non valida - + Handheld controller can't be used on docked mode. Pro controller will be selected. Il controller portatile non può essere utilizzato in modalità dock. Verrà selezionato il controller Pro. - - + + Amiibo Amiibo - - + + The current amiibo has been removed L'Amiibo corrente è stato rimosso - + Error Errore - - + + The current game is not looking for amiibos Il gioco in uso non è alla ricerca di Amiibo - + Amiibo File (%1);; All Files (*.*) File Amiibo (%1);; Tutti i file (*.*) - + Load Amiibo Carica Amiibo - + Error loading Amiibo data Impossibile caricare i dati dell'Amiibo - + The selected file is not a valid amiibo Il file selezionato non è un Amiibo valido - + The selected file is already on use Il file selezionato è già in uso - + An unknown error occurred Si è verificato un errore sconosciuto - - - Keys not installed - Chiavi non installate - - - - - Install decryption keys and restart Eden before attempting to install firmware. - Installa le chiavi di crittografia e riavvia Eden prima di installare il firmware. - - - - Select Dumped Firmware Source Location - Seleziona il percorso del firmware estratto - - - - Select Dumped Firmware ZIP - Seleziona il file ZIP del firmware estratto - - - - Zipped Archives (*.zip) - Archivi compressi (*.zip) - - - - Firmware cleanup failed - Pulizia del firmware fallita - - - - Failed to clean up extracted firmware cache. -Check write permissions in the system temp directory and try again. -OS reported error: %1 - - - - + No firmware available Nessun firmware disponibile - + Firmware Corrupted Firmware corrotto - + Unknown applet Applet sconosciuto - + Applet doesn't map to a known value. L'applet non è associato a un valore noto. - + Record not found Non trovato - + Applet not found. Please reinstall firmware. Applet non trovato. Reinstalla il firmware. - + Capture Screenshot Cattura screenshot - + PNG Image (*.png) Immagine PNG (*.png) - + TAS state: Running %1/%2 Stato TAS: In esecuzione (%1/%2) - + TAS state: Recording %1 Stato TAS: Registrazione in corso (%1) - + TAS state: Idle %1/%2 Stato TAS: In attesa (%1/%2) - + TAS State: Invalid Stato TAS: Non valido - + &Stop Running &Interrompi esecuzione - + Stop R&ecording Interrompi r&egistrazione - + Building: %n shader(s) Compilazione di %n shaderCompilazione di %n shaderCompilazione di %n shader - + Scale: %1x %1 is the resolution scaling factor Risoluzione: %1x - + Speed: %1% / %2% Velocità: %1% / %2% - + Speed: %1% Velocità: %1% - + Game: %1 FPS Gioco: %1 FPS - + Frame: %1 ms Frame: %1 ms - - - FSR - FSR - - - + NO AA NO AA - + VOLUME: MUTE VOLUME: MUTO - + VOLUME: %1% Volume percentage (e.g. 50%) VOLUME: %1% - + Derivation Components Missing Componenti di derivazione mancanti - + Decryption keys are missing. Install them now? Chiavi di crittografia mancanti. Vuoi installarle ora? - + Wayland Detected! Wayland rilevato! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -8044,74 +8038,74 @@ Si consiglia invece di usare X11. Vuoi forzare l'uso di quest'ultimo per i prossimi avvii? - + Use X11 Usa X11 - + Continue with Wayland Continua con Wayland - + Don't show again Non mostrare di nuovo - + Restart Required Riavvio richiesto - + Restart Eden to apply the X11 backend. Riavvia Eden per usare il back-end X11. - + Slow Lento - + Turbo Turbo - + Unlocked Sbloccato - + Select RomFS Dump Target Seleziona RomFS da estrarre - + Please select which RomFS you would like to dump. Seleziona quale RomFS vorresti estrarre. - + Are you sure you want to close Eden? Sei sicuro di voler uscire da Eden? - - - + + + Eden Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. Sei sicuro di voler arrestare l'emulazione? Tutti i progressi non salvati verranno perduti. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? @@ -8178,6 +8172,11 @@ Vuoi uscire comunque? ScaleForce ScaleForce + + + FSR + FSR + Area @@ -8189,52 +8188,62 @@ Vuoi uscire comunque? MMPX - + + SGSR + + + + + SGSR EdgeDir + + + + Docked Dock - + Handheld Portatile - + Fast Veloce - + Balanced Bilanciata - + Accurate Accurata - + Vulkan Vulkan - + OpenGL GLSL OpenGL GLSL - + OpenGL SPIRV OpenGL SPIR-V - + OpenGL GLASM OpenGL GLASM - + Null Nullo @@ -8267,7 +8276,7 @@ If you wish to clean up the files which were left in the old data location, you - + Data was migrated successfully. I dati sono stati trasferiti con successo. @@ -9124,47 +9133,47 @@ p, li { white-space: pre-wrap; } %1 sta giocando a %2 - + Play Time: %1 Tempo di gioco: %1 - + Never Played Mai giocato - + Version: %1 Versione: %1 - + Version: 1.0.0 Versione: 1.0.0 - + Installed SD Titles Titoli SD installati - + Installed NAND Titles Titoli NAND installati - + System Titles Titoli di sistema - + Add New Game Directory Aggiungi nuova cartella dei giochi - + Favorites Preferiti @@ -9285,47 +9294,47 @@ p, li { white-space: pre-wrap; } QtCommon::Content - + Game Requires Firmware Firmware richiesto - + The game you are trying to launch requires firmware to boot or to get past the opening menu. Please <a href='https://yuzu-mirror.github.io/help/quickstart'>dump and install firmware</a>, or press "OK" to launch anyways. Il gioco che stai cercando di avviare richiede il firmware per poter partire o per superare il menù iniziale. <a href='https://yuzu-mirror.github.io/help/quickstart'>Esegui il dump del firmware e installalo</a>, o premi "OK" per continuare lo stesso. - + Installing Firmware... Installazione del firmware in corso... - - - - - + + + + + Cancel Annulla - + Firmware Install Failed Installazione del firmware fallita - + Firmware Install Succeeded Installazione del firmware riuscita - + Firmware integrity verification failed! Verifica dell'integrità del firmware fallita! - - + + Verification failed for the following files: %1 @@ -9334,207 +9343,240 @@ p, li { white-space: pre-wrap; } %1 - - + + Verifying integrity... Verifica dell'integrità in corso... - - + + Integrity verification succeeded! Verifica dell'integrità riuscita! - - + + The operation completed successfully. L'operazione è stata completata con successo. - - + + Integrity verification failed! Verifica dell'integrità fallita! - + File contents may be corrupt or missing. I contenuti dei file potrebbero essere corrotti o mancanti. - + Integrity verification couldn't be performed Impossibile effettuare la verifica dell'integrità - + Firmware installation cancelled, firmware may be in a bad state or corrupted. File contents could not be checked for validity. Installazione del firmware annullata, il firmware potrebbe essere corrotto o in cattivo stato. Non è stato possibile controllare la validità dei contenuti dei file. - + Select Dumped Keys Location Seleziona il percorso delle chiavi estratte - + Decryption Keys install succeeded Installazione delle chiavi di crittografia riuscita - + Decryption Keys install failed Installazione delle chiavi di crittografia fallita - + Orphaned Profiles Detected! Rilevati profili scollegati! - + UNEXPECTED BAD THINGS MAY HAPPEN IF YOU DON'T READ THIS!<br>Eden has detected the following save directories with no attached profile:<br>%1<br><br>The following profiles are valid:<br>%2<br><br>Click "OK" to open your save folder and fix up your profiles.<br>Hint: copy the contents of the largest or last-modified folder elsewhere, delete all orphaned profiles, and move your copied contents to the good profile.<br><br>Still confused? See the <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/Orphaned.md'>help page</a>.<br> - + Really clear data? Eliminare i dati? - + Important data may be lost! Potresti perdere dei dati importanti! - + Are you REALLY sure? Sei DAVVERO sicuro? - + Once deleted, your data will NOT come back! Only do this if you're 100% sure you want to delete this data. Una volta eliminati, i tuoi dati NON potranno essere ripristinati! Procedi solo se sei assolutamente sicuro di voler eliminare questi dati. - + Clearing... Eliminazione in corso... - + Select Export Location Scegli dove esportare i dati - + %1.zip %1.zip - - + + + Zipped Archives (*.zip) Archivi compressi (*.zip) - + Exporting data. This may take a while... Esportazione dei dati in corso. Potrebbe richiedere un po' di tempo... - + Exporting Esportazione in corso - + Exported Successfully Esportazione completata - + Data was exported successfully. I dati sono stati esportati con successo. - + Export Cancelled Esportazione annullata - + Export was cancelled by the user. L'esportazione è stata annullata dall'utente. - + Export Failed Esportazione fallita - + Ensure you have write permissions on the targeted directory and try again. Assicurati di disporre dei permessi di scrittura nella cartella selezionata e poi riprova. - + Select Import Location Seleziona il file da importare - + Import Warning Attenzione - + All previous data in this directory will be deleted. Are you sure you wish to proceed? Tutti i dati già presenti in questa cartella verranno eliminati. Sei sicuro di voler procedere? - + Importing data. This may take a while... Importazione dei dati in corso. Potrebbe richiedere un po' di tempo... - + Importing Importazione in corso - + Imported Successfully Importazione completata - + Data was imported successfully. I dati sono stati importati con successo. - + Import Cancelled Importazione annullata - + Import was cancelled by the user. L'importazione è stata annullata dall'utente. - + Import Failed Importazione fallita - + Ensure you have read permissions on the targeted directory and try again. Assicurati di disporre dei permessi di lettura nella cartella selezionata e poi riprova. + + + Keys not installed + + + + + Install decryption keys and restart Eden before attempting to install firmware. + + + + + Select Dumped Firmware Source Location + + + + + Select Dumped Firmware ZIP + + + + + Firmware cleanup failed + + + + + Failed to clean up extracted firmware cache. +Check write permissions in the system temp directory and try again. +OS reported error: %1 + + QtCommon::FS @@ -9783,72 +9825,72 @@ Vuoi selezionare manualmente la cartella dell'installazione portatile da us Impossibile eliminare la cache dei metadati. Potrebbe essere in uso o inesistente. - + Create Shortcut Crea scorciatoia - + Do you want to launch the game in fullscreen? Vuoi avviare il gioco a schermo intero? - + Shortcut Created Scorciatoia creata - + Successfully created a shortcut to %1 Scorciatoia creata con successo per %1 - + Shortcut may be Volatile! Scorciatoia potenzialmente instabile! - + This will create a shortcut to the current AppImage. This may not work well if you update. Continue? Verrà creata una scorciatoia all'AppImage attuale. Potrebbe non funzionare correttamente se effettui un aggiornamento. Vuoi continuare? - + Failed to Create Shortcut Impossibile creare la scorciatoia - + Failed to create a shortcut to %1 Si è verificato un errore nel creare la scorciatoia per %1 - + Create Icon Crea icona - + Cannot create icon file. Path "%1" does not exist and cannot be created. Impossibile creare il file dell'icona. Il percorso "%1" non esiste e non può essere creato. - + No firmware available Nessun firmware disponibile - + Please install firmware to use the home menu. Installa il firmware per usare il menù Home. - + Home Menu Applet Applet menù Home - + Home Menu is not available. Please reinstall firmware. Il menù Home non è disponibile. Reinstalla il firmware. @@ -9856,37 +9898,37 @@ Vuoi selezionare manualmente la cartella dell'installazione portatile da us QtCommon::Mod - + Mod Name Nome mod - + What should this mod be called? Qual è il nome di questa mod? - + RomFS RomFS - + ExeFS/Patch ExeFS/Patch - + Cheat Trucco - + Mod Type Tipologia mod - + Could not detect mod type automatically. Please manually specify the type of mod you downloaded. Most mods are RomFS mods, but patches (.pchtxt) are typically ExeFS mods. @@ -9895,18 +9937,18 @@ Most mods are RomFS mods, but patches (.pchtxt) are typically ExeFS mods. - - + + Mod Extract Failed Estrazione mod fallita - + Failed to create temporary directory %1 Impossibile creare la cartella temporanea %1 - + Zip file %1 is empty Il file ZIP %1 è vuoto @@ -10608,65 +10650,65 @@ Selezionando "Da Eden", i dati di salvataggio pre-esistenti in Ryujinx - + New Version Location - + All Files (*.*) - - - + + + Failed to save file - + Could not open file %1 for writing. - + Downloading... - + Cancel - + Could not write to file %1. - + Could not commit to file %1. - + Failed to download file - + Could not download from %1%2 Error code: %3 - + Download Complete - + Successfully downloaded %1. Would you like to open it? diff --git a/dist/languages/ja_JP.ts b/dist/languages/ja_JP.ts index c1ab881dde..480e8faa28 100644 --- a/dist/languages/ja_JP.ts +++ b/dist/languages/ja_JP.ts @@ -704,7 +704,7 @@ Options lower than 1X can cause artifacts. - Determines how sharpened the image will look using FSR's dynamic contrast. + Determines how sharpened the image will look using FSR's or SGSR's dynamic contrast. @@ -755,35 +755,35 @@ Disabling it is only intended for debugging. - + Use asynchronous GPU emulation - + Uses an extra CPU thread for rendering. This option should always remain enabled. - + NVDEC emulation: NVDEC エミュレーション: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. - + ASTC Decoding Method: ASTC デコード方式: - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -792,55 +792,66 @@ stuttering but may present artifacts. - + ASTC Recompression Method: ASTC 再圧縮方式: - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. - + Frame Pacing Mode (Vulkan only) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. - + VRAM Usage Mode: - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. - + Skip CPU Inner Invalidation - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. - + + Anti-Flicker + + + + + Forces GPU fence callbacks to wait for submitted GPU work. +Use with Fast GPU mode, to avoid flicker with lower performance impact. + + + + VSync Mode: 垂直同期: - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -851,1361 +862,1381 @@ Mailbox は FIFO よりも遅延が小さくティアリングがありません Immediate (no synchronization) は表示可能なものをすべて表示し, ティアリング発生の可能性があります. - + Sync Memory Operations メモリ操作の同期 - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. - + Enable asynchronous presentation (Vulkan only) 非同期プレゼンテーション (Vulkan のみ) - + Slightly improves performance by moving presentation to a separate CPU thread. - + Force maximum clocks (Vulkan only) 最大クロック強制 (Vulkan のみ) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. GPUのクロックスピードを下げないように、グラフィックコマンドを待っている間、バックグラウンドで作業を実行させます。 - + Anisotropic Filtering: 異方性フィルタリング: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. - + GPU Mode: GPUモード: - + Controls the GPU emulation mode. Most games render fine with Fast or Balanced modes, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. - + DMA Accuracy: DMA精度: - + Controls the DMA precision accuracy. Safe precision fixes issues in some games but may degrade performance. - + Enable asynchronous shader compilation - + May reduce shader stutter. - + Fast GPU Time - + Overclocks the emulated GPU to increase dynamic resolution and render distance. Use 256 for maximal performance and 512 for maximal graphics fidelity. - + GPU Unswizzle - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. - + GPU Unswizzle Max Texture Size - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. - + GPU Unswizzle Stream Size - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. - + GPU Unswizzle Chunk Size - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. - + Use Vulkan pipeline cache Vulkan パイプラインキャッシュを使用 - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. - + Enable Compute Pipelines (Intel Vulkan Only) コンピュート・パイプラインの有効化(インテル Vulkan のみ) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. - + Enable Reactive Flushing - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. - + Sync to framerate of video playback ビデオ再生のフレームレートに同期する - + Run the game at normal speed during video playback, even when the framerate is unlocked. - + Barrier feedback loops - + Improves rendering of transparency effects in specific games. 特定のゲームにおける透明エフェクトのレンダリングを改善します。 - + Enable buffer history - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. - + Fix bloom effects - + Removes bloom in Burnout. - + Enable Legacy Rescale Pass - + May fix rescale issues in some games by relying on behavior from the previous implementation. Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. - + Extended Dynamic State - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. - + Vertex Input Dynamic State - + Enables vertex input dynamic state feature for better quality and performance. - + Sample Shading - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. - + RNG Seed 乱数シード値の変更 - + Controls the seed of the random number generator. Mainly used for speedrunning. - + Device Name デバイス名 - + The name of the console. - + + Homebrew Args + + + + + Command-line arguments passed to homebrew at launch (e.g. -noglsl). + + + + Custom RTC Date: - + This option allows to change the clock of the console. Can be used to manipulate time in games. - + The number of seconds from the current unix time - + Language: 言語: - + This option can be overridden when region setting is auto-select - + Region: 地域: - + The region of the console. - + Time Zone: タイムゾーン: - + The time zone of the console. - + Sound Output Mode: 音声出力モード: - + Console Mode: コンソールモード: - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. - + Prompt for user profile on boot - + Useful if multiple people use the same PC. - + Pause when not in focus - + Pauses emulation when focusing on other windows. - + Confirm before stopping emulation エミュレーションを停止する前に確認する - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. - + Hide mouse on inactivity 非アクティブ時にマウスカーソルを隠す - + Hides the mouse after 2.5s of inactivity. - + Disable controller applet コントローラーアプレットの無効化 - + Forcibly disables the use of the controller applet in emulated programs. When a program attempts to open the controller applet, it is immediately closed. - + Check for updates - + Whether or not to check for updates upon startup. - + Enable Gamemode - + Force X11 as Graphics Backend - + Custom frontend - + Real applet - + Never - + On Load - + Always - + CPU CPU - + GPU GPU - + CPU Asynchronous CPU 非同期 - + Uncompressed (Best quality) 圧縮しない (最高品質) - + BC1 (Low quality) BC1 (低品質) - + BC3 (Medium quality) BC3 (中品質) - - + + Auto 自動 - + 30 FPS - + 60 FPS - + 90 FPS - + 120 FPS - + Conservative - + Aggressive - + Vulkan Vulkan - + OpenGL GLSL OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) OpenGL GLASM (アセンブリシェーダー、NVIDIA のみ) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) OpenGL SPIR-V(実験的、AMD/Mesaのみ) - + Null - + Fast - + Balanced - - + + Accurate 正確 - - + + Default デフォルト - + Unsafe (fast) - + Safe (stable) - + Unsafe 不安定 - + Paranoid (disables most optimizations) パラノイド (ほとんどの最適化を無効化) - + Debugging - + Dynarmic Dynarmic - + NCE NCE - + Borderless Windowed ボーダーレスウィンドウ - + Exclusive Fullscreen 排他的フルスクリーン - + No Video Output ビデオ出力しない - + CPU Video Decoding ビデオをCPUでデコード - + GPU Video Decoding (Default) ビデオをGPUでデコード (デフォルト) - + 0.25X (180p/270p) [EXPERIMENTAL] 0.25X (180p/270p) [EXPERIMENTAL] - + 0.5X (360p/540p) [EXPERIMENTAL] 0.5X (360p/540p) [実験的] - + 0.75X (540p/810p) [EXPERIMENTAL] 0.75X (540p/810p) [実験的] - + 1X (720p/1080p) 1X (720p/1080p) - + 1.25X (900p/1350p) [EXPERIMENTAL] 1.25X (900p/1350p) [EXPERIMENTAL] - + 1.5X (1080p/1620p) [EXPERIMENTAL] 1.5X (1080p/1620p) [実験的] - + 2X (1440p/2160p) 2X (1440p/2160p) - + 3X (2160p/3240p) 3X (2160p/3240p) - + 4X (2880p/4320p) 4X (2880p/4320p) - + 5X (3600p/5400p) 5X (3600p/5400p) - + 6X (4320p/6480p) 6X (4320p/6480p) - + 7X (5040p/7560p) 7X (5040p/7560p) - + 8X (5760p/8640p) 8X (5760p/8640p) - + Nearest Neighbor Nearest Neighbor - + Bilinear Bilinear - + Bicubic Bicubic - + Gaussian Gaussian - + Lanczos Lanczos - + ScaleForce ScaleForce - + AMD FidelityFX Super Resolution AMD FidelityFX Super Resolution - + Area - + MMPX MMPX - + Zero-Tangent - + B-Spline - + Mitchell - + Spline-1 - - + + Snapdragon Game Super Resolution + + + + + Snapdragon Game Super Resolution EdgeDir + + + + + None なし - + FXAA FXAA - + SMAA SMAA - + Default (16:9) デフォルト (16:9) - + Force 4:3 強制 4:3 - + Force 21:9 強制 21:9 - + Force 16:10 強制 16:10 - + Stretch to Window ウィンドウに合わせる - + Automatic 自動 - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x - + 32x 32x - + 64x 64x - + Japanese (日本語) 日本語 - + American English アメリカ英語 - + French (français) フランス語 (français) - + German (Deutsch) ドイツ語 (Deutsch) - + Italian (italiano) イタリア語 (italiano) - + Spanish (español) スペイン語 (español) - + Chinese 中国語 - + Korean (한국어) 韓国語 (한국어) - + Dutch (Nederlands) オランダ語 (Nederlands) - + Portuguese (português) ポルトガル語 (português) - + Russian (Русский) ロシア語 (Русский) - + Taiwanese 台湾語 - + British English イギリス英語 - + Canadian French カナダフランス語 - + Latin American Spanish ラテンアメリカスペイン語 - + Simplified Chinese 簡体字中国語 - + Traditional Chinese (正體中文) 繁体字中国語 (正體中文) - + Brazilian Portuguese (português do Brasil) ブラジルポルトガル語 (português do Brasil) - + Polish (polska) - + Thai (แบบไทย) - - + + Japan 日本 - + USA アメリカ - + Europe ヨーロッパ - + Australia オーストラリア - + China 中国 - + Korea 韓国 - + Taiwan 台湾 - + Auto (%1) Auto select time zone 自動 (%1) - + Default (%1) Default time zone 既定 (%1) - + CET 中央ヨーロッパ時間 - + CST6CDT CST6CDT - + Cuba キューバ - + EET 東ヨーロッパ標準時 - + Egypt エジプト - + Eire アイルランド - + EST アメリカ東部標準時 - + EST5EDT EST5EDT - + GB GB - + GB-Eire イギリス-アイルランド - + GMT GMT - + GMT+0 GMT+0 - + GMT-0 GMT-0 - + GMT0 GMT0 - + Greenwich グリニッジ - + Hongkong 香港 - + HST ハワイ標準時 - + Iceland アイスランド - + Iran イラン - + Israel イスラエル - + Jamaica ジャマイカ - + Kwajalein クェゼリン - + Libya リビア - + MET 中東時間 - + MST MST - + MST7MDT MST7MDT - + Navajo ナバホ - + NZ NZ - + NZ-CHAT NZ-CHAT - + Poland ポーランド - + Portugal ポルトガル - + PRC PRC - + PST8PDT PST8PDT - + ROC ROC - + ROK ROK - + Singapore シンガポール - + Turkey トルコ - + UCT UCT - + Universal ユニバーサル - + UTC UTC - + W-SU W-SU - + WET WET - + Zulu ズールー - + Mono モノラル - + Stereo ステレオ - + Surround サラウンド - + 4GB DRAM (Default) 4GB DRAM (デフォルト) - + 6GB DRAM (Unsafe) 6GB DRAM (不安定) - + 8GB DRAM 8GB DRAM - + 10GB DRAM (Unsafe) - + 12GB DRAM (Unsafe) - + Docked Docked - + Handheld 携帯モード - - + + Off - + Boost (1700MHz) Boost (1700MHz) - + Fast (2000MHz) Fast (2000MHz) - + Always ask (Default) 常に確認する (デフォルト) - + Only if game specifies not to stop ゲームが停止しないように指定しているときのみ - + Never ask 確認しない - - + + Medium (256) - - + + High (512) - + Very Small (16 MB) - + Small (32 MB) - + Normal (128 MB) - + Large (256 MB) - + Very Large (512 MB) - + Very Low (4 MB) - + Low (8 MB) - + Normal (16 MB) - + Medium (32 MB) - + High (64 MB) - + Very Low (32) - + Low (64) - + Normal (128) - + Disabled 無効 - + ExtendedDynamicState 1 - + ExtendedDynamicState 2 - + ExtendedDynamicState 3 - + Tree View - + Grid View @@ -3260,33 +3291,33 @@ Would you like to delete the old save data? 背景色: - + % - FSR sharpening percentage (e.g. 50%) - % + FSR/SGSR sharpening percentage (e.g. 50%) + - + Off オフ - + VSync Off VSync オフ - + Recommended 推奨 - + On オン - + VSync On VSync オン @@ -4415,7 +4446,7 @@ To invert the axes, first move your joystick vertically, and then horizontally.< - + Configure 設定 @@ -4446,7 +4477,7 @@ To invert the axes, first move your joystick vertically, and then horizontally.< - + Test テスト @@ -4461,77 +4492,77 @@ To invert the axes, first move your joystick vertically, and then horizontally.< サーバーを削除 - + %1:%2 %1:%2 - - - - - - + + + + + + Eden Eden - + Port number has invalid characters ポート番号に無効な文字が含まれています - + Port has to be in range 0 and 65353 ポート番号は0から65353の間で設定してください - + IP address is not valid IPアドレスが無効です - + This UDP server already exists このUDPサーバーはすでに存在してます - + Unable to add more than 8 servers 8個以上のサーバーを追加することはできません - + Testing テスト中 - + Configuring 設定中 - + Test Successful テスト成功 - + Successfully received data from the server. サーバーからのデータ受信に成功しました。 - + Test Failed テスト失敗 - + Could not receive valid data from the server.<br>Please verify that the server is set up correctly and the address and port are correct. 有効なデータを受信できませんでした。<br>サーバーが正しくセットアップされ、アドレスとポートが正しいことを確認してください。 - + UDP Test or calibration configuration is in progress.<br>Please wait for them to finish. UDPテストまたはキャリブレーション実行中です。<br>完了までお待ちください。 @@ -4715,57 +4746,57 @@ Current values are %1% and %2% respectively. いくつかの設定はゲームが実行中でないときのみ設定できます - + Add-Ons アドオン - + System システム - + CPU CPU - + Graphics グラフィック - + Adv. Graphics 高度なグラフィック - + Ext. Graphics - + Audio サウンド - + Input Profiles 入力プロファイル - + Network ネットワーク - + Applets - + Properties プロパティ @@ -5776,7 +5807,7 @@ Drag points to change position, or double-click table cells to edit values. - + Calculating... @@ -5978,50 +6009,50 @@ Please go to Configure -> System -> Network and make a selection. GRenderWindow - - + + OpenGL not available! OpenGLは使用できません! - + OpenGL shared contexts are not supported. - + Eden has not been compiled with OpenGL support. - - - + + + Error while initializing OpenGL! OpenGL初期化エラー - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. GPUがOpenGLをサポートしていないか、グラフィックスドライバーが最新ではありません。 - + Error while initializing OpenGL 4.6! OpenGL4.6初期化エラー! - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 GPUがOpenGL4.6をサポートしていないか、グラフィックスドライバーが最新ではありません。<br><br>GL レンダラ:<br>%1 - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 GPUが1つ以上の必要なOpenGL拡張機能をサポートしていない可能性があります。最新のグラフィックドライバを使用していることを確認してください。<br><br>GL レンダラ:<br>%1<br><br>サポートされていない拡張機能:<br>%2 - + This build doesn't have OpenGL support. @@ -6029,279 +6060,282 @@ Please go to Configure -> System -> Network and make a selection. GameList - + &Add New Game Directory - + Favorite お気に入り - + Start Game ゲームを開始 - + Start Game without Custom Configuration カスタム設定なしでゲームを開始 - + Open Save Data Location セーブデータディレクトリを開く - + Open Mod Data Location Modデータディレクトリを開く - + Open Transferable Pipeline Cache パイプラインキャッシュを開く - + Link to Ryujinx - + Remove 削除 - + Remove Installed Update インストールされているアップデートを削除 - + Remove All Installed DLC 全てのインストールされているDLCを削除 - + Remove Custom Configuration カスタム設定を削除 - + Remove Cache Storage キャッシュストレージを削除 - + Remove OpenGL Pipeline Cache OpenGLパイプラインキャッシュを削除 - + Remove Vulkan Pipeline Cache Vulkanパイプラインキャッシュを削除 - + Remove All Pipeline Caches すべてのパイプラインキャッシュを削除 - + Remove All Installed Contents 全てのインストールされているコンテンツを削除 - + Manage Play Time - + Edit Play Time Data - + Remove Play Time Data プレイ時間情報を削除 - - + + Dump RomFS RomFSをダンプ - + Dump RomFS to SDMC RomFSをSDMCにダンプ - + Verify Integrity 整合性を確認 - + Copy Title ID to Clipboard タイトルIDをクリップボードへコピー - + Navigate to GameDB entry GameDBエントリを表示 - + Create Shortcut ショートカットを作成 - + Add to Desktop デスクトップに追加 - + Add to Applications Menu アプリケーションメニューに追加 - + Configure Game - + Scan Subfolders サブフォルダをスキャンする - + Remove Game Directory ゲームディレクトリを削除する - + ▲ Move Up ▲ 上へ移動 - + ▼ Move Down ▼ 下へ移動 - + Open Directory Location ディレクトリの場所を開く - + Clear クリア - - - Name - ゲーム名 - - - - Compatibility - 互換性 - - - - Add-ons - アドオン - - - - File type - ファイル種別 - - - - Size - ファイルサイズ - - - - Play time - プレイ時間 - GameListItemCompat - + Ingame - + Game starts, but crashes or major glitches prevent it from being completed. ゲームは始まるが、クラッシュや大きな不具合でクリアできない。 - + Perfect カンペキ - + Game can be played without issues. ゲームは問題なくプレイできる。 - + Playable プレイ可 - + Game functions with minor graphical or audio glitches and is playable from start to finish. ゲームは、グラフィックやオーディオに小さな不具合はあるが、最初から最後までプレイできる。 - + Intro/Menu イントロ - + Game loads, but is unable to progress past the Start Screen. ゲームはロードされるが、スタート画面から先に進めない。 - + Won't Boot 起動不可 - + The game crashes when attempting to startup. ゲームは起動時にクラッシュしました。 - + Not Tested 未テスト - + The game has not yet been tested. このゲームはまだテストされていません。 + + GameListModel + + + Name + + + + + Compatibility + + + + + Add-ons + + + + + File type + + + + + Size + + + + + Play time + + + GameListPlaceholder - + Double-click to add a new folder to the game list 新しいゲームリストフォルダを追加するにはダブルクリックしてください。 @@ -6309,17 +6343,17 @@ Please go to Configure -> System -> Network and make a selection. GameListSearchField - + %1 of %n result(s) - + Filter: フィルター: - + Enter pattern to filter フィルターパターンを入力 @@ -6814,1139 +6848,1099 @@ Debug Message: - + Game &Icon Size - + Reset Window Size to &720p &720P - + Reset Window Size to 720p ウィンドウサイズを720Pにリセット - + Reset Window Size to &900p &900P - + Reset Window Size to 900p ウィンドウサイズを900Pにリセット - + Reset Window Size to &1080p &1080P - + Reset Window Size to 1080p ウィンドウサイズを1080Pにリセット - + &Multiplayer マルチプレイヤー (&M) - + &Tools ツール(&T) - + Am&iibo - + Launch &Applet - + &TAS &TAS - + &Create Home Menu Shortcut - + Install &Firmware - + &Help ヘルプ(&H) - + &Install Files to NAND... ファイルをNANDにインストール...(&I) - + L&oad File... ファイルをロード...(&L) - + Load &Folder... フォルダをロード...(&F) - + E&xit 終了(&E) - - + + &Pause 中断(&P) - + &Stop 停止(&S) - + &Verify Installed Contents インストールされたコンテンツを確認(&V) - + &About Eden - + Single &Window Mode シングルウィンドウモード(&W) - + Con&figure... 設定...(&F) - + Ctrl+, - + Enable Overlay Display Applet - + Show &Filter Bar フィルターバーを表示 (&F) - + Show &Status Bar ステータスバー(&S) - + Show Status Bar ステータスバーの表示 - + &Browse Public Game Lobby 公開ゲームロビーを参照 (&B) - + &Create Room ルームを作成 (&C) - + &Leave Room ルームを退出 (&L) - + &Direct Connect to Room ルームに直接接続 (&D) - + &Show Current Room 現在のルームを表示 (&S) - + F&ullscreen 全画面表示(&F) - + &Restart 再実行(&R) - + Load/Remove &Amiibo... &Amiibo をロード/削除... - + &Report Compatibility 互換性を報告(&R) - + Open &Mods Page &Modページを開く - + Open &Quickstart Guide クイックスタートガイドを開く(&Q) - + &FAQ &FAQ - + &Capture Screenshot スクリーンショットをキャプチャ(&C) - + &Album - + &Set Nickname and Owner オーナーとニックネームを設定 (&S) - + &Delete Game Data ゲームデータの消去 (&D) - + &Restore Amiibo Amiibo を復旧 (&R) - + &Format Amiibo Amiibo を初期化(&F) - + &Mii Editor - + &Configure TAS... TASを設定... (&C) - + Configure C&urrent Game... 現在のゲームを設定...(&U) - - + + &Start 実行(&S) - + &Reset リセット(&R) - - + + R&ecord 記録(&R) - + Open &Controller Menu コントローラーメニューを開く (&C) - + Install Decryption &Keys - + &Home Menu - + &Desktop - + &Application Menu - + &Root Data Folder - + &NAND Folder - + &SDMC Folder - + &Mod Folder - + &Log Folder - + From Folder - + From ZIP - + &Eden Dependencies - + &Data Manager - + &Tree View - + &Grid View - + Game Icon Size - - + + None - + Show Game &Name - + Show &Performance Overlay - + + &Carousel View + + + + Small (32x32) - + Standard (64x64) - + Large (128x128) - + Full Size (256x256) - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping - + Loading Web Applet... - - + + Disable Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. - + Unmute - + Mute - + Reset Volume - + &Clear Recent Files - + &Continue - + Warning: Outdated Game Format - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. - - + + Error while loading ROM! - + The ROM format is not supported. - + An error occurred initializing the video core. - + Eden has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-mirror.github.io/help/reference/log-files/'>How to Upload the Log File</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. - + (64-bit) - + (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit - + Closing software... - + Save Data - + Mod Data - + Error Opening %1 Folder - - + + Folder does not exist! - + Remove Installed Game Contents? - + Remove Installed Game Update? - + Remove Installed Game DLC? - + Remove Entry - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? - + Remove Cache Storage? - + Remove File - + Remove Play Time Data - + Reset play time? - - + + RomFS Extraction Failed! - + There was an error copying the RomFS files or the user cancelled the operation. - + Full - + Skeleton - + Select RomFS Dump Mode - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... - - + + Cancel - + RomFS Extraction Succeeded! - + The operation completed successfully. - + Error Opening %1 - + Select Directory - + Properties - + The game properties could not be loaded. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. - + Load File - + Open Extracted ROM Directory - + Invalid Directory Selected - + The directory you have selected does not contain a 'main' file. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files - + %n file(s) remaining - + Installing file "%1"... - - + + Install Results - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application - + System Archive - + System Application Update - + Firmware Package (Type A) - + Firmware Package (Type B) - + Game - + Game Update - + Game DLC - + Delta Title - + Select NCA Install Type... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) - + Failed to Install - + The title type you selected for the NCA is invalid. - + File not found - + File "%1" not found - + OK - + Function Disabled - + Compatibility list reporting is currently disabled. Check back later! - + Error opening URL - + Unable to open the URL "%1". - + TAS Recording - + Overwrite file of player 1? - + Invalid config detected - + Handheld controller can't be used on docked mode. Pro controller will be selected. - - + + Amiibo - - + + The current amiibo has been removed - + Error - - + + The current game is not looking for amiibos - + Amiibo File (%1);; All Files (*.*) - + Load Amiibo - + Error loading Amiibo data - + The selected file is not a valid amiibo - + The selected file is already on use - + An unknown error occurred - - - Keys not installed - - - - - - Install decryption keys and restart Eden before attempting to install firmware. - - - - - Select Dumped Firmware Source Location - - - - - Select Dumped Firmware ZIP - - - - - Zipped Archives (*.zip) - - - - - Firmware cleanup failed - - - - - Failed to clean up extracted firmware cache. -Check write permissions in the system temp directory and try again. -OS reported error: %1 - - - - + No firmware available - + Firmware Corrupted - + Unknown applet - + Applet doesn't map to a known value. - + Record not found - + Applet not found. Please reinstall firmware. - + Capture Screenshot - + PNG Image (*.png) - + TAS state: Running %1/%2 - + TAS state: Recording %1 - + TAS state: Idle %1/%2 - + TAS State: Invalid - + &Stop Running - + Stop R&ecording - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor - + Speed: %1% / %2% - + Speed: %1% - + Game: %1 FPS - + Frame: %1 ms - - - FSR - - - - + NO AA - + VOLUME: MUTE - + VOLUME: %1% Volume percentage (e.g. 50%) - + Derivation Components Missing - + Decryption keys are missing. Install them now? - + Wayland Detected! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -7954,74 +7948,74 @@ Would you like to force it for future launches? - + Use X11 - + Continue with Wayland - + Don't show again - + Restart Required - + Restart Eden to apply the X11 backend. - + Slow - + Turbo - + Unlocked - + Select RomFS Dump Target - + Please select which RomFS you would like to dump. - + Are you sure you want to close Eden? - - - + + + Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? @@ -8087,6 +8081,11 @@ Would you like to bypass this and exit anyway? ScaleForce + + + FSR + + Area @@ -8098,52 +8097,62 @@ Would you like to bypass this and exit anyway? - + + SGSR + + + + + SGSR EdgeDir + + + + Docked - + Handheld - + Fast - + Balanced - + Accurate - + Vulkan - + OpenGL GLSL - + OpenGL SPIRV - + OpenGL GLASM - + Null @@ -8176,7 +8185,7 @@ If you wish to clean up the files which were left in the old data location, you - + Data was migrated successfully. @@ -9031,47 +9040,47 @@ p, li { white-space: pre-wrap; } %1は%2をプレイ中です - + Play Time: %1 - + Never Played - + Version: %1 - + Version: 1.0.0 - + Installed SD Titles インストール済みSDタイトル - + Installed NAND Titles インストール済みNANDタイトル - + System Titles システムタイトル - + Add New Game Directory 新しいゲームディレクトリを追加する - + Favorites お気に入り @@ -9192,253 +9201,286 @@ p, li { white-space: pre-wrap; } QtCommon::Content - + Game Requires Firmware - + The game you are trying to launch requires firmware to boot or to get past the opening menu. Please <a href='https://yuzu-mirror.github.io/help/quickstart'>dump and install firmware</a>, or press "OK" to launch anyways. - + Installing Firmware... - - - - - + + + + + Cancel - + Firmware Install Failed - + Firmware Install Succeeded - + Firmware integrity verification failed! - - + + Verification failed for the following files: %1 - - + + Verifying integrity... + + + + Integrity verification succeeded! + + - Integrity verification succeeded! - - - - - The operation completed successfully. - - + + Integrity verification failed! - + File contents may be corrupt or missing. - + Integrity verification couldn't be performed - + Firmware installation cancelled, firmware may be in a bad state or corrupted. File contents could not be checked for validity. - + Select Dumped Keys Location - + Decryption Keys install succeeded - + Decryption Keys install failed - + Orphaned Profiles Detected! - + UNEXPECTED BAD THINGS MAY HAPPEN IF YOU DON'T READ THIS!<br>Eden has detected the following save directories with no attached profile:<br>%1<br><br>The following profiles are valid:<br>%2<br><br>Click "OK" to open your save folder and fix up your profiles.<br>Hint: copy the contents of the largest or last-modified folder elsewhere, delete all orphaned profiles, and move your copied contents to the good profile.<br><br>Still confused? See the <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/Orphaned.md'>help page</a>.<br> - + Really clear data? - + Important data may be lost! - + Are you REALLY sure? - + Once deleted, your data will NOT come back! Only do this if you're 100% sure you want to delete this data. - + Clearing... - + Select Export Location - + %1.zip - - + + + Zipped Archives (*.zip) - + Exporting data. This may take a while... - + Exporting - + Exported Successfully - + Data was exported successfully. - + Export Cancelled - + Export was cancelled by the user. - + Export Failed - + Ensure you have write permissions on the targeted directory and try again. - + Select Import Location - + Import Warning - + All previous data in this directory will be deleted. Are you sure you wish to proceed? - + Importing data. This may take a while... - + Importing - + Imported Successfully - + Data was imported successfully. - + Import Cancelled - + Import was cancelled by the user. - + Import Failed - + Ensure you have read permissions on the targeted directory and try again. + + + Keys not installed + + + + + Install decryption keys and restart Eden before attempting to install firmware. + + + + + Select Dumped Firmware Source Location + + + + + Select Dumped Firmware ZIP + + + + + Firmware cleanup failed + + + + + Failed to clean up extracted firmware cache. +Check write permissions in the system temp directory and try again. +OS reported error: %1 + + QtCommon::FS @@ -9682,72 +9724,72 @@ Would you like to manually select a portable folder to use? - + Create Shortcut - + Do you want to launch the game in fullscreen? - + Shortcut Created - + Successfully created a shortcut to %1 - + Shortcut may be Volatile! - + This will create a shortcut to the current AppImage. This may not work well if you update. Continue? - + Failed to Create Shortcut - + Failed to create a shortcut to %1 - + Create Icon - + Cannot create icon file. Path "%1" does not exist and cannot be created. - + No firmware available - + Please install firmware to use the home menu. - + Home Menu Applet - + Home Menu is not available. Please reinstall firmware. @@ -9755,55 +9797,55 @@ Would you like to manually select a portable folder to use? QtCommon::Mod - + Mod Name - + What should this mod be called? - + RomFS - + ExeFS/Patch - + Cheat - + Mod Type - + Could not detect mod type automatically. Please manually specify the type of mod you downloaded. Most mods are RomFS mods, but patches (.pchtxt) are typically ExeFS mods. - - + + Mod Extract Failed - + Failed to create temporary directory %1 - + Zip file %1 is empty @@ -10497,65 +10539,65 @@ By selecting "From Eden", previous save data stored in Ryujinx will be - + New Version Location - + All Files (*.*) - - - + + + Failed to save file - + Could not open file %1 for writing. - + Downloading... - + Cancel - + Could not write to file %1. - + Could not commit to file %1. - + Failed to download file - + Could not download from %1%2 Error code: %3 - + Download Complete - + Successfully downloaded %1. Would you like to open it? diff --git a/dist/languages/ko_KR.ts b/dist/languages/ko_KR.ts index 590a031a80..2bb72d0b7d 100644 --- a/dist/languages/ko_KR.ts +++ b/dist/languages/ko_KR.ts @@ -4,12 +4,12 @@ About Eden - + Eden에 대해 <html><head/><body><p><span style=" font-size:28pt;">Eden</span></p></body></html> - + <html><head/><body><p><span style=" font-size:28pt;">Eden</span></p></body></html> @@ -26,17 +26,24 @@ li.unchecked::marker { content: "\2610"; } li.checked::marker { content: "\2612"; } </style></head><body style=" font-family:'Noto Sans'; font-size:9pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:12pt;">Eden is an experimental open-source emulator for the Nintendo Switch licensed under GPLv3.0+ which is based on the yuzu emulator which ended development back in March 2024. <br /><br />This software should not be used to play games you have not legally obtained.</span></p></body></html> - + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style type="text/css"> +p, li { white-space: pre-wrap; } +hr { height: 1px; border-width: 0; } +li.unchecked::marker { content: "\2610"; } +li.checked::marker { content: "\2612"; } +</style></head><body style=" font-family:'Noto Sans'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:12pt;">Eden은 GPLv3.0+ 라이선스를 받은 실험적인 오픈 소스 닌텐도 스위치 에뮬레이터로, 2024년 3월에 개발이 종료된 yuzu 에뮬레이터를 기반으로 합니다. <br /><br />이 소프트웨어는 법적으로 획득하지 않은 게임을 플레이하는 데 사용되어서는 안 됩니다.</span></p></body></html> <html><head/><body><p><a href="https://eden-emulator.github.io/"><span style=" text-decoration: underline; color:#039be5;">Website</span></a> | <a href="https://git.eden-emu.dev"><span style=" text-decoration: underline; color:#039be5;">Source Code</span></a> | <a href="https://git.eden-emu.dev/eden-emu/eden/activity/contributors"><span style=" text-decoration: underline; color:#039be5;">Contributors</span></a> | <a href="https://discord.gg/HstXbPch7X"><span style=" text-decoration: underline; color:#039be5;">Discord</span></a> | <a href="https://stt.gg/qKgFEAbH"><span style=" text-decoration: underline; color:#039be5;">Stoat</span></a> | <a href="https://nitter.poast.org/edenemuofficial"><span style=" text-decoration: underline; color:#039be5;">Twitter</span></a> | <a href="https://git.eden-emu.dev/eden-emu/eden/src/branch/master/LICENSE.txt"><span style=" text-decoration: underline; color:#039be5;">License</span></a></p></body></html> - + <html><head/><body><p><a href="https://eden-emulator.github.io/"><span style=" text-decoration: underline; color:#039be5;">웹사이트</span></a> | <a href="https://git.eden-emu.dev"><span style=" text-decoration: underline; color:#039be5;">소스 코드</span></a> | <a href="https://git.eden-emu.dev/eden-emu/eden/activity/contributors"><span style=" text-decoration: underline; color:#039be5;">기여자</span></a> | <a href="https://discord.gg/HstXbPch7X"><span style=" text-decoration: underline; color:#039be5;">디스코드</span></a> | <a href="https://stt.gg/qKgFEAbH"><span style=" text-decoration: underline; color:#039be5;">Stoat</span></a> | <a href="https://nitter.poast.org/edenemuofficial"><span style=" text-decoration: underline; color:#039be5;">트위터</span></a> | <a href="https://git.eden-emu.dev/eden-emu/eden/src/branch/master/LICENSE.txt"><span style=" text-decoration: underline; color:#039be5;">라이선스</span></a></p></body></html> <html><head/><body><p><span style=" font-size:7pt;">&quot;Nintendo Switch&quot; is a trademark of Nintendo. Eden is not affiliated with Nintendo in any way.</span></p></body></html> - + <html><head/><body><p><span style=" font-size:7pt;">&quot;닌텐도 스위치&quot;는 닌텐도의 상표이며, Eden은 닌텐도와 어떤 방식으로도 제휴되어 있지 않습니다.</span></p></body></html> @@ -64,7 +71,7 @@ li.checked::marker { content: "\2612"; } Configuration completed! - 설정 완료! + 구성 완료! @@ -138,7 +145,7 @@ li.checked::marker { content: "\2612"; } Kick - 추방 + 강퇴 @@ -148,12 +155,12 @@ li.checked::marker { content: "\2612"; } Kick Player - 추방된 플레이어 + 강퇴된 플레이어 Are you sure you would like to <b>kick</b> %1? - 정말로 %1을 <b>추방</b>하겠습니까?? + 정말 %1을 <b>강퇴</b>시키겠습니까? @@ -165,9 +172,9 @@ li.checked::marker { content: "\2612"; } Are you sure you would like to <b>kick and ban</b> %1? This would ban both their forum username and their IP address. - %1을 <b>추방</b>하겠습니까? + 정말 %1을 <b>강퇴 및 차단</b>하겠습니까? -이렇게 하면 포럼 사용자 이름과 IP 주소가 모두 금지됩니다. +이렇게 하면 포럼 사용자 이름과 IP 주소 모두 차단됩니다. @@ -232,12 +239,12 @@ This would ban both their forum username and their IP address. <html><head/><body><p><span style=" font-size:10pt;">Should you choose to submit a test case to the </span><a href="https://eden-emulator.github.io/game/"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">eden Compatibility List</span></a><span style=" font-size:10pt;">, The following information will be collected and displayed on the site:</span></p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Hardware Information (CPU / GPU / Operating System)</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Which version of eden you are running</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">The connected eden account</li></ul></body></html> - + <html><head/><body><p><span style=" font-size:10pt;">만약 </span><a href="https://eden-emulator.github.io/game/"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">eden 호환성 목록</span></a><span style=" font-size:10pt;">에 시험 사례를 제출하기로 선택하면, 다음과 같은 정보가 수집되어 사이트에 표시됩니다:</span></p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">하드웨어 정보(CPU/GPU/운영 체제)</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">사용 중인 eden 버전</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">연결된 eden 계정</li></ul></body></html> <html><head/><body><p>Does the game boot?</p></body></html> - <html><head/><body><p>게임이 부팅되나요?</p></body></html> + <html><head/><body><p>게임이 실행되나요?</p></body></html> @@ -257,7 +264,7 @@ This would ban both their forum username and their IP address. No The game crashes or freezes while loading or using the menu - 아니요 메뉴를 로드하거나 사용하는 동안 게임이 충돌하거나 멈춥니다 + 아니요 메뉴 로딩 중이나 사용 중에 게임이 충돌하거나 멈춥니다 @@ -297,12 +304,12 @@ This would ban both their forum username and their IP address. Major The game has major graphical errors - 주요 게임에 주요 그래픽 오류가 있습니다 + 심각 게임에 심각한 그래픽 오류가 있습니다 Minor The game has minor graphical errors - 마이너 게임에 경미한 그래픽 오류가 있습니다 + 경미 게임에 경미한 그래픽 오류가 있습니다 @@ -317,12 +324,12 @@ This would ban both their forum username and their IP address. Major The game has major audio errors - 메이저 게임에 주요 오디오 오류가 있습니다 + 심각 게임에 심각한 오디오 오류가 있습니다 Minor The game has minor audio errors - 마이너 게임에 사소한 오디오 오류가 있습니다 + 경미 게임에 경미한 오디오 오류가 있습니다 @@ -342,12 +349,12 @@ This would ban both their forum username and their IP address. Submitting - 제출중 + 제출 중 Communication error - 통신 에러 + 통신 오류 @@ -370,17 +377,17 @@ This would ban both their forum username and their IP address. Amiibo editor - + Amiibo 편집기 Controller configuration - + 컨트롤러 구성 Data erase - + 데이터 삭제 @@ -390,12 +397,12 @@ This would ban both their forum username and their IP address. Net connect - + 망 연결 Player select - + 플레이어 선택 @@ -405,52 +412,52 @@ This would ban both their forum username and their IP address. Mii Edit - + Mii 편집 Online web - + 온라인 웹 Shop - + Photo viewer - + 사진 뷰어 Offline web - + 오프라인 웹 Login share - + 로그인 공유 Wifi web auth - + Wi-Fi 웹 인증 My page - + 마이페이지 Enable Overlay Applet - + 오버레이 애플릿 활성화 Enables Horizon's built-in overlay applet. Press and hold the home button for 1 second to show it. - + Horizon의 내장 오버레이 애플릿을 활성화합니다. HOME 버튼을 1초 동안 길게 누르면 애플릿이 나타납니다. @@ -470,7 +477,7 @@ This would ban both their forum username and their IP address. Mute audio - + 오디오 음소거 @@ -480,72 +487,77 @@ This would ban both their forum username and their IP address. Mute audio when in background - 백그라운드에서 오디오 음소거 + 백그라운드에서 실행될 때 오디오 음소거 Multicore CPU Emulation - 멀티 코어 CPU 에뮬레이션 + 다중코어 CPU 에뮬레이션 This option increases CPU emulation thread use from 1 to the maximum of 4. This is mainly a debug option and shouldn't be disabled. - + 이 옵션은 CPU 에뮬레이션 스레드 사용량을 1에서 최대 4까지 증가시킵니다. +이 옵션은 주로 디버그용이므로 비활성화해서는 안 됩니다. Memory Layout - + 메모리 레이아웃 Increases the amount of emulated RAM. Doesn't affect performance/stability but may allow HD texture mods to load. - + 에뮬레이트된 RAM의 용량을 증가시킵니다. +성능/안정성에는 영향을 미치지 않지만 HD 텍스처 모드가 로드될 수 있습니다. Limit Speed Percent - 속도 퍼센트 제한 + 속도 제한 비율 Controls the game's maximum rendering speed, but it's up to each game if it runs faster or not. 200% for a 30 FPS game is 60 FPS, and for a 60 FPS game it will be 120 FPS. Disabling it means unlocking the framerate to the maximum your PC can reach. - + 게임의 최대 렌더링 속도를 제어하지만, 실제로 더 빠르게 실행될지는 각 게임에 따라 다릅니다. +30 FPS 게임에서 200%로 설정하면 60 FPS가 되고, 60 FPS 게임에서는 120 FPS가 됩니다. +이 옵션을 비활성화하면 PC가 도달할 수 있는 최대 프레임률까지 프레임 속도 제한이 해제됩니다. Turbo Speed - + 터보 속도 When the Turbo Speed hotkey is pressed, the speed will be limited to this percentage. - + 터보 속도 단축키를 누르면 속도가 이 비율로 제한됩니다. Slow Speed - + 느린 속도 When the Slow Speed hotkey is pressed, the speed will be limited to this percentage. - + 느린 속도 단축키를 누르면 속도가 이 비율로 제한됩니다. Synchronize Core Speed - + 코어 속도 동기화 Synchronizes CPU core speed with the game's maximum rendering speed to boost FPS without affecting game speed (animations, physics, etc.). Can help reduce stuttering at lower framerates. - + CPU 코어 속도를 게임의 최대 렌더링 속도와 동기화하여 게임 속도(애니메이션, 물리 등)에 영향을 주지 않고 FPS를 향상시킵니다. +낮은 프레임률에서 발생하는 끊김 현상을 줄이는 데 도움될 수 있습니다. @@ -555,56 +567,59 @@ Can help reduce stuttering at lower framerates. Change the accuracy of the emulated CPU (for debugging only). - + 에뮬레이트된 CPU의 정확도를 변경합니다(디버깅용으로만 사용). Backend: - + 백엔드: CPU Overclock - + CPU 오버클럭 Overclocks the emulated CPU to remove some FPS limiters. Weaker CPUs may see reduced performance, and certain games may behave improperly. Use Boost (1700MHz) to run at the Switch's highest native clock, or Fast (2000MHz) to run at 2x clock. - + 에뮬레이트된 CPU를 오버클럭하여 일부 FPS 제한기를 제거합니다. CPU가 약하면 성능이 저하될 수 있으며 특정 게임이 부정확하게 작동할 수 있습니다. +스위치의 최고 네이티브 클럭으로 실행하려면 부스트(1700MHz)를 사용하고, 2배 클럭으로 실행하려면 고속(2000MHz)을 사용하세요. Custom CPU Ticks - + 사용자 지정 CPU 틱 Set a custom value of CPU ticks. Higher values can increase performance, but may cause deadlocks. A range of 77-21000 is recommended. - + CPU 틱 값을 사용자 지정으로 설정합니다. 값이 높을수록 성능이 향상될 수 있지만 교착 상태가 발생할 수 있습니다. 77~21000 범위의 값을 권장합니다. Enable Host MMU Emulation (fastmem) - + 호스트 MMU 에뮬레이션 사용(패스트멤) This optimization speeds up memory accesses by the guest program. Enabling it causes guest memory reads/writes to be done directly into memory and make use of Host's MMU. Disabling this forces all memory accesses to use Software MMU Emulation. - + 이 최적화는 게스트 프로그램의 메모리 접근 속도를 높입니다. +이 옵션을 활성화하면 게스트 메모리 읽기/쓰기가 메모리에서 직접 수행되고 호스트의 MMU를 사용하게 됩니다. +이 옵션을 비활성화하면 모든 메모리 접근에 소프트웨어 MMU 에뮬레이션을 사용하도록 강제됩니다. Unfuse FMA (improve performance on CPUs without FMA) - FMA 분리 (FMA를 지원하지 않는 CPU에서의 성능을 향상시킵니다) + FMA 분리 (FMA를 지원하지 않는 CPU의 성능 향상) This option improves speed by reducing accuracy of fused-multiply-add instructions on CPUs without native FMA support. - + 이 옵션은 네이티브 FMA를 지원하지 않는 CPU의 fused-multiply-add 명령어 정확도를 낮춰 속도를 향상시킵니다. @@ -614,7 +629,7 @@ Disabling this forces all memory accesses to use Software MMU Emulation. This option improves the speed of some approximate floating-point functions by using less accurate native approximations. - + 이 옵션은 정확도가 낮은 네이티브 근사값을 사용하여 일부 근사 부동 소수점 함수의 속도를 향상시킵니다. @@ -624,7 +639,7 @@ Disabling this forces all memory accesses to use Software MMU Emulation. This option improves the speed of 32 bits ASIMD floating-point functions by running with incorrect rounding modes. - + 이 옵션은 잘못된 반올림 모드를 사용하여 실행함으로써 32비트 ASIMD 부동 소수점 함수의 속도를 향상시킵니다. @@ -635,7 +650,8 @@ Disabling this forces all memory accesses to use Software MMU Emulation. This option improves speed by removing NaN checking. Please note this also reduces accuracy of certain floating-point instructions. - + 이 옵션은 NaN 검사를 제거하여 속도를 향상시킵니다. +이로 인해 특정 부동 소수점 명령어의 정확도가 떨어질 수 있다는 점을 유의해 주세요. @@ -646,7 +662,8 @@ Please note this also reduces accuracy of certain floating-point instructions. This option improves speed by eliminating a safety check before every memory operation. Disabling it may allow arbitrary code execution. - + 이 옵션은 모든 메모리 작업 전에 안전 검사를 제거하여 속도를 향상시킵니다. +이 옵션을 비활성화하면 임의 코드 실행이 허용될 수 있습니다. @@ -657,7 +674,8 @@ Disabling it may allow arbitrary code execution. This option improves speed by relying only on the semantics of cmpxchg to ensure safety of exclusive access instructions. Please note this may result in deadlocks and other race conditions. - + 이 옵션은 cmpxchg의 의미론에만 의존하여 배타적 접근 명령의 안전성을 보장함으로써 속도를 향상시킵니다. +이로 인해 교착 상태 및 기타 경합 조건이 발생할 수 있다는 점을 유의해 주세요. @@ -668,7 +686,8 @@ Please note this may result in deadlocks and other race conditions. Changes the output graphics API. Vulkan is recommended. - + 출력 그래픽 API를 변경합니다. +Vulkan을 권장합니다. @@ -678,7 +697,7 @@ Vulkan is recommended. This setting selects the GPU to use (Vulkan only). - + 이 설정은 사용할 GPU를 선택합니다(Vulkan 전용). @@ -690,12 +709,14 @@ Vulkan is recommended. Forces to render at a different resolution. Higher resolutions require more VRAM and bandwidth. Options lower than 1X can cause artifacts. - + 다른 해상도로 렌더링하도록 강제합니다. +더 높은 해상도는 더 많은 VRAM과 대역폭을 필요로 합니다. +1X보다 낮은 옵션은 아티팩트를 유발할 수 있습니다. Window Adapting Filter: - 윈도우 적응형 필터: + 창 적응형 필터: @@ -704,8 +725,8 @@ Options lower than 1X can cause artifacts. - Determines how sharpened the image will look using FSR's dynamic contrast. - + Determines how sharpened the image will look using FSR's or SGSR's dynamic contrast. + FSR 또는 SGSR의 동적 대비를 사용하여 이미지가 얼마나 선명하게 보일지 결정합니다. @@ -717,7 +738,9 @@ Options lower than 1X can cause artifacts. The anti-aliasing method to use. SMAA offers the best quality. FXAA can produce a more stable picture in lower resolutions. - + 사용할 안티에일리어싱 방식. +SMAA는 최고의 화질을 제공합니다. +FXAA는 저해상도에서 더 안정적인 화면을 구현할 수 있습니다. @@ -729,1482 +752,1555 @@ FXAA can produce a more stable picture in lower resolutions. The method used to render the window in fullscreen. Borderless offers the best compatibility with the on-screen keyboard that some games request for input. Exclusive fullscreen may offer better performance and better Freesync/Gsync support. - + 창을 전체 화면으로 렌더링하는 데 사용되는 방법입니다. +테두리 없는 창 모드는 일부 게임에서 입력을 위해 요구하는 화면 키보드와의 호환성이 가장 좋습니다. +전체 화면 전용 모드는 더 나은 성능과 FreeSync/GSync 지원을 제공할 수 있습니다. Aspect Ratio: - 화면비: + 화면 비율: Stretches the renderer to fit the specified aspect ratio. Most games only support 16:9, so modifications are required to get other ratios. Also controls the aspect ratio of captured screenshots. - + 렌더러를 지정된 화면 비율에 맞게 늘립니다. +대부분의 게임은 16:9만 지원하므로 다른 비율을 얻으려면 수정이 필요합니다. +또한 캡처된 스크린샷의 화면 비율도 제어합니다. Use persistent pipeline cache - + 지속적 파이프라인 캐시 사용 Allows saving shaders to storage for faster loading on following game boots. Disabling it is only intended for debugging. - - - - - Use asynchronous GPU emulation - + 셰이더를 저장소에 저장하여 다음 게임 실행 시 더 빠르게 불러올 수 있도록 합니다. +이 옵션을 비활성화하는 것은 디버깅 목적으로만 사용하세요. - Uses an extra CPU thread for rendering. -This option should always remain enabled. - + Use asynchronous GPU emulation + 비동기 GPU 에뮬레이션 사용 + Uses an extra CPU thread for rendering. +This option should always remain enabled. + 렌더링에 추가 CPU 스레드를 사용합니다. +이 옵션은 항상 활성화된 상태로 유지해야 합니다. + + + NVDEC emulation: NVDEC 에뮬레이션: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. - - - - - ASTC Decoding Method: - + 비디오를 디코딩하는 방법을 지정합니다. +디코딩에 CPU 또는 GPU를 사용할 수 있으며, 디코딩을 전혀 수행하지 않을 수도 있습니다(비디오가 검은 화면으로 표시됨). +대부분의 경우 GPU 디코딩이 가장 좋은 성능을 제공합니다. + ASTC Decoding Method: + ASTC 디코딩 방식: + + + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). CPU Asynchronously: Use the CPU to decode ASTC textures on demand. EliminatesASTC decoding stuttering but may present artifacts. - - - - - ASTC Recompression Method: - + 이 옵션은 ASTC 텍스처를 디코딩하는 방식을 제어합니다. +CPU: CPU를 사용하여 디코딩합니다. +GPU: GPU의 컴퓨트 셰이더를 사용하여 ASTC 텍스처를 디코딩합니다(권장). +CPU 비동기: 필요에 따라 CPU를 사용하여 ASTC 텍스처를 디코딩합니다. ASTC 디코딩으로 인한 끊김 현상이 없어지지만, 아티팩트가 발생할 수 있습니다. + ASTC Recompression Method: + ASTC 재압축 방식: + + + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. - - - - - Frame Pacing Mode (Vulkan only) - + 대부분의 GPU는 ASTC 텍스처를 지원하지 않으므로 중간 형식인 RGBA8로 압축 해제해야 합니다. +BC1/BC3: 중간 형식은 BC1 또는 BC3 형식으로 재압축되어 + VRAM을 절약하지만 이미지 품질이 저하됩니다. - Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. - + Frame Pacing Mode (Vulkan only) + 프레임 페이싱 모드(Vulkan 전용) - - VRAM Usage Mode: - + + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. + 에뮬레이터가 프레임 페이싱을 관리하는 방식을 제어하여 끊김 현상을 줄이고 프레임 속도를 더욱 부드럽고 일관되게 만듭니다. - Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. -Aggressive mode may impact performance of other applications such as recording software. - + VRAM Usage Mode: + VRAM 사용 모드: - - Skip CPU Inner Invalidation - + + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. +Aggressive mode may impact performance of other applications such as recording software. + 에뮬레이터가 메모리를 절약할지, 아니면 사용 가능한 비디오 메모리를 최대한 활용하여 성능을 향상시킬지 선택합니다. +적극적 모드는 녹화 소프트웨어와 같은 다른 애플리케이션의 성능에 영향을 줄 수 있습니다. - Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. - + Skip CPU Inner Invalidation + CPU 내부 무효화 건너뛰기 - - VSync Mode: - VSync 모드: + + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. + 메모리 업데이트 중 특정 캐시 무효화를 건너뛰어 CPU 사용량을 줄이고 지연 시간을 개선합니다. 하지만 이로 인해 소프트 크래시가 발생할 수 있습니다. + Anti-Flicker + 안티플리커 + + + + Forces GPU fence callbacks to wait for submitted GPU work. +Use with Fast GPU mode, to avoid flicker with lower performance impact. + GPU 펜스 콜백이 제출된 GPU 작업을 기다리도록 강제합니다. +성능 저하를 최소화하면서 깜빡임 현상을 방지하려면 고속 GPU 모드와 함께 사용하세요. + + + + VSync Mode: + 수직 동기화 모드: + + + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. Immediate (no synchronization) presents whatever is available and can exhibit tearing. - - - - - Sync Memory Operations - - - - - Ensures data consistency between compute and memory operations. -This option fixes issues in games, but may degrade performance. -Unreal Engine 4 games often see the most significant changes thereof. - + FIFO(VSync)는 프레임 드롭이나 화면 찢김 현상이 없지만 화면 새로 고침률이 제한됩니다. +FIFO Relaxed는 속도 저하 후 복구 과정에서 화면 찢김 현상이 발생할 수 있습니다. +Mailbox는 FIFO보다 지연 시간이 짧고 화면 찢김 현상이 없지만 프레임 드롭이 발생할 수 있습니다. +즉각적(동기화 없음)는 사용 가능한 모든 화면을 표시하며 화면 찢김 현상이 발생할 수 있습니다. - Enable asynchronous presentation (Vulkan only) - 비동기 프레젠테이션 활성화(Vulkan만 해당) + Sync Memory Operations + 메모리 작업 동기화 - Slightly improves performance by moving presentation to a separate CPU thread. - - - - - Force maximum clocks (Vulkan only) - 강제 최대 클록 (Vulkan 전용) + Ensures data consistency between compute and memory operations. +This option fixes issues in games, but may degrade performance. +Unreal Engine 4 games often see the most significant changes thereof. + 컴퓨팅 및 메모리 작업 간의 데이터 일관성을 보장합니다. +이 옵션은 게임 문제를 해결하지만 성능이 저하될 수 있습니다. +대개 언리얼 엔진 4 게임에서 이러한 변경 사항이 가장 두드러지게 나타납니다. - Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. - 실행은 GPU가 클럭 속도를 낮추지 않도록 그래픽 명령을 기다리는 동안 백그라운드에서 작동합니다. + Enable asynchronous presentation (Vulkan only) + 비동기 프레젠테이션 활성화(Vulkan 전용) - + + Slightly improves performance by moving presentation to a separate CPU thread. + 프레젠테이션을 별도의 CPU 스레드로 이동시켜 성능을 약간 향상시킵니다. + + + + Force maximum clocks (Vulkan only) + 최대 클럭 강제 적용(Vulkan 전용) + + + + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. + 그래픽 명령을 기다리는 동안 백그라운드에서 작업을 실행하여 GPU 클럭 속도가 저하되는 것을 방지합니다. + + + Anisotropic Filtering: 비등방성 필터링: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. - - - - - GPU Mode: - - - - - Controls the GPU emulation mode. -Most games render fine with Fast or Balanced modes, but Accurate is still required for some. -Particles tend to only render correctly with Accurate mode. - + 비스듬한 각도에서의 텍스처 렌더링 품질을 제어합니다. +대부분의 GPU에서 16x로 설정해도 안전합니다. - DMA Accuracy: - + GPU Mode: + GPU 모드: - Controls the DMA precision accuracy. Safe precision fixes issues in some games but may degrade performance. - - - - - Enable asynchronous shader compilation - + Controls the GPU emulation mode. +Most games render fine with Fast or Balanced modes, but Accurate is still required for some. +Particles tend to only render correctly with Accurate mode. + GPU 에뮬레이션 모드를 제어합니다. +대부분의 게임은 고속 또는 균형 모드에서도 제대로 렌더링되지만, 일부 게임에서는 정확 모드가 필요합니다. +특히 조각들은 정확 모드에서만 제대로 렌더링되는 경향이 있습니다. - May reduce shader stutter. - + DMA Accuracy: + DMA 정확도: - Fast GPU Time - + Controls the DMA precision accuracy. Safe precision fixes issues in some games but may degrade performance. + DMA 정확도를 제어합니다. 안전 정확도를 사용하면 일부 게임에서 발생하는 문제를 해결할 수 있지만 성능이 저하될 수 있습니다. - - Overclocks the emulated GPU to increase dynamic resolution and render distance. -Use 256 for maximal performance and 512 for maximal graphics fidelity. - + + Enable asynchronous shader compilation + 비동기 셰이더 컴파일 활성화 - GPU Unswizzle - + May reduce shader stutter. + 셰이더 끊김 현상을 줄일 수 있습니다. - Accelerates BCn 3D texture decoding using GPU compute. -Disable if experiencing crashes or graphical glitches. - + Fast GPU Time + 빠른 CPU 시간 - - GPU Unswizzle Max Texture Size - + + Overclocks the emulated GPU to increase dynamic resolution and render distance. +Use 256 for maximal performance and 512 for maximal graphics fidelity. + 에뮬레이션된 GPU를 오버클럭하여 동적 해상도와 렌더링 거리를 향상시킵니다. +최대 성능을 위해서는 256을, 최대 그래픽 품질을 위해서는 512를 사용하세요. - Sets the maximum size (MiB) for GPU-based texture unswizzling. -While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. -Adjust this to find the balance between GPU acceleration and CPU overhead. - + GPU Unswizzle + GPU 언스위즐 + + + + Accelerates BCn 3D texture decoding using GPU compute. +Disable if experiencing crashes or graphical glitches. + GPU 컴퓨팅을 사용하여 BCn 3D 텍스처 디코딩을 가속화합니다. +충돌이나 그래픽 결함이 발생하는 경우 이 기능을 비활성화하세요. + + + + GPU Unswizzle Max Texture Size + GPU 언스위즐 최대 텍스처 크기 - GPU Unswizzle Stream Size - - - - - Sets the maximum amount of texture data (in MiB) processed per frame. -Higher values can reduce stutter during texture loading but may impact frame consistency. - + Sets the maximum size (MiB) for GPU-based texture unswizzling. +While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. +Adjust this to find the balance between GPU acceleration and CPU overhead. + GPU 기반 텍스처 언스위즐링의 최대 크기(MiB)를 설정합니다. +GPU는 중간 및 큰 크기의 텍스처에서 더 빠르지만, 매우 작은 텍스처에서는 CPU가 더 효율적일 수 있습니다. +이 값을 조정하여 GPU 가속과 CPU 오버헤드 간의 균형을 찾으세요. - GPU Unswizzle Chunk Size - + GPU Unswizzle Stream Size + GPU 언스위즐 스트림 크기 - Determines the number of depth slices processed in a single dispatch. -Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. - + Sets the maximum amount of texture data (in MiB) processed per frame. +Higher values can reduce stutter during texture loading but may impact frame consistency. + 프레임당 처리할 최대 텍스처 데이터 양(MiB 단위)을 설정합니다. +값이 높을수록 텍스처 로딩 중 끊김 현상을 줄일 수 있지만 프레임 일관성에 영향을 미칠 수 있습니다. + + + + GPU Unswizzle Chunk Size + GPU 언스위즐 청크 크기 + Determines the number of depth slices processed in a single dispatch. +Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. + 단일 디스패치에서 처리할 깊이 슬라이스 수를 결정합니다. +이 값을 높이면 고성능 GPU에서 처리량이 향상될 수 있지만, 성능이 낮은 하드웨어에서는 TDR 오류나 드라이버 시간 초과가 발생할 수 있습니다. + + + Use Vulkan pipeline cache Vulkan 파이프라인 캐시 사용 - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. - + GPU 제조사별 파이프라인 캐시를 활성화합니다. +이 옵션은 Vulkan 드라이버가 파이프라인 캐시 파일을 내부에 저장하지 않는 경우 셰이더 로딩 시간을 크게 향상시킬 수 있습니다. - + Enable Compute Pipelines (Intel Vulkan Only) - - - - - Required by some games. -This setting only exists for Intel proprietary drivers and may crash if enabled. -Compute pipelines are always enabled on all other drivers. - + 컴퓨트 파이프라인 활성화(인텔 Vulkan 전용) + Required by some games. +This setting only exists for Intel proprietary drivers and may crash if enabled. +Compute pipelines are always enabled on all other drivers. + 일부 게임에 필요합니다. +이 설정은 인텔 독점 드라이버에만 적용되며 활성화된 경우 충돌할 수 있습니다. +다른 모든 드라이버에서는 컴퓨트 파이프라인이 항상 활성화됩니다. + + + Enable Reactive Flushing 반응형 플러싱 활성화 - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. - - - - - Sync to framerate of video playback - 동영상 재생 프레임 속도에 동기화 - - - - Run the game at normal speed during video playback, even when the framerate is unlocked. - 프레임 속도가 잠금 해제된 상태에서도 동영상 재생 중에 일반 속도로 게임을 실행합니다. - - - - Barrier feedback loops - 차단 피드백 루프 + 예측형 플러싱 대신 반응형 플러싱을 사용하여 보다 정확한 메모리 동기화를 가능하게 합니다. - Improves rendering of transparency effects in specific games. - 특정 게임에서 투명도 효과의 렌더링을 개선합니다. + Sync to framerate of video playback + 비디오 재생 프레임 속도에 동기화 + Run the game at normal speed during video playback, even when the framerate is unlocked. + 프레임률 제한이 해제된 경우에도 비디오 재생 중에는 게임을 정상 속도로 실행합니다. + + + + Barrier feedback loops + 피드백 루프 차단 + + + + Improves rendering of transparency effects in specific games. + 특정 게임에서 투명 효과 렌더링을 개선합니다. + + + Enable buffer history - - - - - Enables access to previous buffer states. -This option may improve rendering quality and performance consistency in some games. - - - - - Fix bloom effects - - - - - Removes bloom in Burnout. - + 버퍼 기록 활성화 - Enable Legacy Rescale Pass - + Enables access to previous buffer states. +This option may improve rendering quality and performance consistency in some games. + 이전 버퍼 상태에 접근할 수 있도록 합니다. +이 옵션은 일부 게임에서 렌더링 품질과 성능 일관성을 향상시킬 수 있습니다. - + + Fix bloom effects + 블룸 효과 수정 + + + + Removes bloom in Burnout. + Burnout에서의 블룸 현상을 제거합니다. + + + + Enable Legacy Rescale Pass + 기존 크기 조정 무시 활성화 + + + May fix rescale issues in some games by relying on behavior from the previous implementation. Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. - + 이전 구현의 동작을 활용하여 일부 게임에서 발생하는 크기 조정 문제를 해결할 수 있습니다. +루이지 맨션 3에서 발생했던 AMD 및 Intel GPU의 선 아티팩트 현상과 Nvidia GPU의 회색 텍스처 깜빡임 현상을 해결하는 기존 반응에 대한 예비 해결책입니다. - + Extended Dynamic State - - - - - Controls the number of features that can be used in Extended Dynamic State. -Higher states allow for more features and can increase performance, but may cause additional graphical issues. - + 확장된 동적 상태 - Vertex Input Dynamic State - - - - - Enables vertex input dynamic state feature for better quality and performance. - + Controls the number of features that can be used in Extended Dynamic State. +Higher states allow for more features and can increase performance, but may cause additional graphical issues. + 확장된 동적 상태에서 사용할 수 있는 기능의 수를 제어합니다. +상태 수가 많을수록 더 많은 기능을 사용할 수 있고 성능이 향상될 수 있지만, 추가적인 그래픽 문제가 발생할 수 있습니다. - Sample Shading - + Vertex Input Dynamic State + 정점 입력 동적 상태 - Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. -Higher values improve quality but degrade performance. - + Enables vertex input dynamic state feature for better quality and performance. + 정점 입력의 동적 상태 기능을 활성화하여 품질과 성능을 향상시킵니다. - + + Sample Shading + 샘플 셰이딩 + + + + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. +Higher values improve quality but degrade performance. + 조각 셰이더가 다중샘플 조각에서 한 번당 한 조각이 아닌 샘플별로 실행될 수 있도록 합니다. 성능 저하를 감수하고 그래픽 품질을 향상시킵니다. +값이 높을수록 품질은 향상되지만 성능은 저하됩니다. + + + RNG Seed RNG 시드 - + Controls the seed of the random number generator. Mainly used for speedrunning. - + 난수 생성기의 시드 값을 제어합니다. +주로 스피드런에 사용됩니다. - + Device Name 장치 이름 - + The name of the console. - + 콘솔의 이름 - - Custom RTC Date: - - - - - This option allows to change the clock of the console. -Can be used to manipulate time in games. - + + Homebrew Args + 홈브루 인수 - The number of seconds from the current unix time - + Command-line arguments passed to homebrew at launch (e.g. -noglsl). + 홈브루 실행 시 전달되는 명령줄 인수(예: -noglsl). - Language: - + Custom RTC Date: + 사용자 지정 RTC 날짜: + This option allows to change the clock of the console. +Can be used to manipulate time in games. + 이 옵션을 사용하면 콘솔의 시간을 변경할 수 있습니다. +게임 내 시간을 조작하는 데 사용할 수 있습니다. + + + + The number of seconds from the current unix time + 현재 unix 시간으로부터 경과한 초 수 + + + + Language: + 언어: + + + This option can be overridden when region setting is auto-select - + 이 옵션은 지역 설정이 자동 선택으로 되어 있을 경우 번복될 수 있습니다. - + Region: - 국가: + 지역: - + The region of the console. - + 콘솔의 지역 - + Time Zone: - 시계: + 시간대: - + The time zone of the console. - + 콘솔의 시간대 - + Sound Output Mode: 소리 출력 모드: - + Console Mode: - + 콘솔 모드: - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. - + 콘솔이 독 모드 또는 휴대 모드인지를 선택합니다. +게임은 이 설정에 따라 해상도, 세부 정보 및 지원되는 컨트롤러가 변경됩니다. +휴대 모드로 설정하면 저사양 시스템의 성능을 향상시키는 데 도움이 될 수 있습니다. - + Prompt for user profile on boot - - - - - Useful if multiple people use the same PC. - - - - - Pause when not in focus - - - - - Pauses emulation when focusing on other windows. - - - - - Confirm before stopping emulation - - - - - Overrides prompts asking to confirm stopping the emulation. -Enabling it bypasses such prompts and directly exits the emulation. - + 부팅 시 유저 프로필 입력 요청 - Hide mouse on inactivity - 비활성 상태일 때 마우스 숨기기 + Useful if multiple people use the same PC. + 여러 사람이 같은 PC를 사용할 때 유용합니다. - Hides the mouse after 2.5s of inactivity. - + Pause when not in focus + 초점이 맞지 않을 때 일시 중지 + Pauses emulation when focusing on other windows. + 다른 창에 초점을 맞출 때 에뮬레이션을 일시 중지합니다. + + + + Confirm before stopping emulation + 에뮬레이션을 중지하기 전에 확인 + + + + Overrides prompts asking to confirm stopping the emulation. +Enabling it bypasses such prompts and directly exits the emulation. + 에뮬레이션 중지를 확인하는 메시지를 무시합니다. +이 옵션을 활성화하면 이러한 메시지를 건너뛰고 에뮬레이션을 바로 종료합니다. + + + + Hide mouse on inactivity + 마우스 커서가 비활동 상태일 때 숨기기 + + + + Hides the mouse after 2.5s of inactivity. + 마우스 커서가 2.5초 동안 활동이 없으면 숨깁니다. + + + Disable controller applet 컨트롤러 애플릿 비활성화 - + Forcibly disables the use of the controller applet in emulated programs. When a program attempts to open the controller applet, it is immediately closed. - - - - - Check for updates - - - - - Whether or not to check for updates upon startup. - - - - - Enable Gamemode - + 에뮬레이션된 프로그램에서 컨트롤러 애플릿 사용을 강제적으로 비활성화합니다. +프로그램이 컨트롤러 애플릿을 열려고 하면 즉시 닫힙니다. + Check for updates + 업데이트 확인 + + + + Whether or not to check for updates upon startup. + 시작 시 업데이트를 확인할지 여부. + + + + Enable Gamemode + 게임 모드 활성화 + + + Force X11 as Graphics Backend - - - - - Custom frontend - - - - - Real applet - + X11을 그래픽 백엔드로 강제 설정 - Never - + Custom frontend + 사용자 정의 프론트엔드 - On Load - - - - - Always - + Real applet + 실제 애플릿 + Never + 안 함 + + + + On Load + 로드 시 + + + + Always + 항상 + + + CPU CPU - + GPU GPU - + CPU Asynchronous - + 비동기 CPU - + Uncompressed (Best quality) 비압축(최고 품질) - + BC1 (Low quality) BC1(저품질) - + BC3 (Medium quality) BC3(중간 품질) - - + + Auto 자동 - + 30 FPS - - - - - 60 FPS - - - - - 90 FPS - - - - - 120 FPS - + 30 FPS - Conservative - + 60 FPS + 60 FPS - Aggressive - + 90 FPS + 90 FPS - - Vulkan - Vulcan + + 120 FPS + 120 FPS + + + + Conservative + 소극적 - OpenGL GLSL - - - - - OpenGL GLASM (Assembly Shaders, NVIDIA Only) - - - - - OpenGL SPIR-V (Experimental, AMD/Mesa Only) - + Aggressive + 적극적 + Vulkan + Vulkan + + + + OpenGL GLSL + OpenGL GLSL + + + + OpenGL GLASM (Assembly Shaders, NVIDIA Only) + OpenGL GLASM(어셈블리 셰이더, NVIDIA 전용) + + + + OpenGL SPIR-V (Experimental, AMD/Mesa Only) + OpenGL SPIR-V(실험적, AMD/Mesa 전용) + + + Null Null - - - Fast - - - - - Balanced - - - - - - Accurate - 정확함 - - + Fast + 고속 + + + + Balanced + 균형 + + + + + Accurate + 정확 + + + + Default 기본값 - + Unsafe (fast) - + 불안전(빠름) - + Safe (stable) - - - - - Unsafe - 최적화 (안전하지 않음) - - - - Paranoid (disables most optimizations) - 편집증(대부분의 최적화 비활성화) - - - - Debugging - + 안전(안정적) + Unsafe + 불안전 + + + + Paranoid (disables most optimizations) + 편집증적(대부분의 최적화 비활성화) + + + + Debugging + 디버깅 + + + Dynarmic - + NCE - + NCE - + Borderless Windowed 경계 없는 창 모드 - + Exclusive Fullscreen 독점 전체화면 모드 - + No Video Output 비디오 출력 없음 - + CPU Video Decoding CPU 비디오 디코딩 - + GPU Video Decoding (Default) GPU 비디오 디코딩(기본값) - + 0.25X (180p/270p) [EXPERIMENTAL] - + 0.25X (180p/270p) [실험적] - + 0.5X (360p/540p) [EXPERIMENTAL] - + 0.5X (360p/540p) [실험적] - + 0.75X (540p/810p) [EXPERIMENTAL] 0.75X (540p/810p) [실험적] - + 1X (720p/1080p) 1X (720p/1080p) - + 1.25X (900p/1350p) [EXPERIMENTAL] - + 1.25X (900p/1350p) [실험적] - + 1.5X (1080p/1620p) [EXPERIMENTAL] 1.5X (1080p/1620p) [실험적] - + 2X (1440p/2160p) 2X (1440p/2160p) - + 3X (2160p/3240p) 3X (2160p/3240p) - + 4X (2880p/4320p) 4X (2880p/4320p) - + 5X (3600p/5400p) 5X (3600p/5400p) - + 6X (4320p/6480p) 6X (4320p/6480p) - + 7X (5040p/7560p) 7X (5040p/7560p) - + 8X (5760p/8640p) 8X (5760p/8640p) - + Nearest Neighbor 최근접 보간 - + Bilinear - Bilinear + 쌍선형 - + Bicubic - Bicubic + 쌍입방 - + Gaussian 가우시안 - + Lanczos - + 란초스 - + ScaleForce ScaleForce - - - AMD FidelityFX Super Resolution - - - - - Area - - - - - MMPX - - - - - Zero-Tangent - - - - - B-Spline - - - - - Mitchell - - - Spline-1 - + AMD FidelityFX Super Resolution + AMD FidelityFX Super Resolution + + + + Area + 영역 + + + + MMPX + MMPX + + + + Zero-Tangent + 제로탄젠트 - + B-Spline + B-스플라인 + + + + Mitchell + 미첼 + + + + Spline-1 + 스플라인-1 + + + + Snapdragon Game Super Resolution + Snapdragon Game Super Resolution + + + + Snapdragon Game Super Resolution EdgeDir + Snapdragon Game Super Resolution EdgeDir + + + + None 없음 - + FXAA FXAA - + SMAA SMAA - + Default (16:9) - 기본 (16:9) + 기본(16:9) - + Force 4:3 강제 4:3 - + Force 21:9 강제 21:9 - + Force 16:10 강제 16:10 - + Stretch to Window 창에 맞게 늘림 - + Automatic 자동 - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x - - - 32x - - - - - 64x - - - - - Japanese (日本語) - 일본어 (日本語) - - American English - 미국 영어 + 32x + 32x + 64x + 64x + + + + Japanese (日本語) + 일본어(日本語) + + + + American English + 미국식 영어 + + + French (français) 프랑스어(français) - + German (Deutsch) 독일어(Deutsch) - + Italian (italiano) 이탈리아어(italiano) - + Spanish (español) 스페인어(español) - + Chinese 중국어 - + Korean (한국어) - 한국어 (Korean) + 한국어(Korean) - + Dutch (Nederlands) - 네덜란드어 (Nederlands) + 네덜란드어(Nederlands) - + Portuguese (português) 포르투갈어(português) - + Russian (Русский) - 러시아어 (Русский) + 러시아어(Русский) - + Taiwanese 대만어 - + British English - 영어 (British English) + 영국식 영어 - + Canadian French 캐나다 프랑스어 - + Latin American Spanish - 라틴 아메리카 스페인어 + 라틴아메리카 스페인어 - + Simplified Chinese - 간체 + 중국어 간체 - + Traditional Chinese (正體中文) - 중국어 번체 (正體中文) + 중국어 번체(正體中文) - + Brazilian Portuguese (português do Brasil) 브라질 포르투갈어(português do Brasil) - + Polish (polska) - + 폴란드어(polska) - + Thai (แบบไทย) - + 태국어(แบบไทย) - - + + Japan 일본 - + USA 미국 - + Europe 유럽 - + Australia 호주 - + China 중국 - + Korea 대한민국 - + Taiwan 대만 - + Auto (%1) Auto select time zone - 자동 (%1) + 자동(%1) - + Default (%1) Default time zone - 기본 (%1) + 기본(%1) - + CET - 중앙유럽 표준시(CET) + CET - + CST6CDT CST6CDT - + Cuba 쿠바 - + EET - 동유럽 표준시(EET) + EET - + Egypt 이집트 - + Eire - Eire + 아일랜드 - + EST EST - + EST5EDT EST5EDT - + GB - 영국 하계 표준시(GB) + 영국 - + GB-Eire - GB-Eire + 영국-아일랜드 - + GMT - 그리니치 표준시(GMT) + GMT - + GMT+0 GMT+0 - + GMT-0 GMT-0 - + GMT0 GMT0 - + Greenwich 그리니치 - + Hongkong 홍콩 - + HST - 하와이-알류샨 표준시(HST) + HST - + Iceland 아이슬란드 - + Iran 이란 - + Israel 이스라엘 - + Jamaica 자메이카 - + Kwajalein - 크와잘린 + 콰잘레인 - + Libya 리비아 - + MET - 중앙유럽 표준시(MET) + MET - + MST - 산악 표준시(MST) + MST - + MST7MDT MST7MDT - + Navajo 나바호 - + NZ - 뉴질랜드 표준시(NZ) + 뉴질랜드 - + NZ-CHAT - 채텀 표준시(NZ-CHAT) + 뉴질랜드-채텀 - + Poland 폴란드 - + Portugal 포르투갈 - + PRC - PRC + 중국 - + PST8PDT PST8PDT - + ROC - ROC + 대만 - + ROK - 북한 표준시(ROK) + 대한민국 - + Singapore 싱가포르 - + Turkey 터키 - + UCT UCT - + Universal - Universal + 세계 - + UTC - 협정 세계시(UTC) + UTC - + W-SU - 유럽/모스크바(W-SU) + 모스크바 - + WET 서유럽 - + Zulu 줄루 - + Mono 모노 - + Stereo 스테레오 - + Surround 서라운드 - - - 4GB DRAM (Default) - - - - - 6GB DRAM (Unsafe) - - - - - 8GB DRAM - - - - - 10GB DRAM (Unsafe) - - - - - 12GB DRAM (Unsafe) - - - Docked - 거치 모드 + 4GB DRAM (Default) + 4GB DRAM(기본값) + 6GB DRAM (Unsafe) + 6GB DRAM(불안전) + + + + 8GB DRAM + 8GB DRAM + + + + 10GB DRAM (Unsafe) + 10GB DRAM(불안전) + + + + 12GB DRAM (Unsafe) + 12GB DRAM(불안전) + + + + Docked + 독 모드 + + + Handheld 휴대 모드 - - - - Off - - - - - Boost (1700MHz) - - - - - Fast (2000MHz) - - - - - Always ask (Default) - - - Only if game specifies not to stop - + + Off + - Never ask - + Boost (1700MHz) + 부스트(1700MHz) - - - Medium (256) - + + Fast (2000MHz) + 고속(2000MHz) - - High (512) - + Always ask (Default) + 항상 묻기(기본값) - - Very Small (16 MB) - + + Only if game specifies not to stop + 게임에서 멈추지 말라고 명시한 경우에만 - - Small (32 MB) - - - - - Normal (128 MB) - + + Never ask + 절대 묻지 않음 - Large (256 MB) - + + Medium (256) + 보통(256) - Very Large (512 MB) - + + High (512) + 빠름(512) - Very Low (4 MB) - + Very Small (16 MB) + 아주 작은 크기(16 MB) - Low (8 MB) - + Small (32 MB) + 작은 크기(32 MB) - Normal (16 MB) - + Normal (128 MB) + 보통 크기(128 MB) - Medium (32 MB) - + Large (256 MB) + 큰 크기(256 MB) - High (64 MB) - + Very Large (512 MB) + 아주 큰 크기(512 MB) - Very Low (32) - + Very Low (4 MB) + 매우 낮은 크기(4 MB) - Low (64) - + Low (8 MB) + 낮은 크기(8 MB) - Normal (128) - + Normal (16 MB) + 보통 크기(16 MB) + + + + Medium (32 MB) + 중간 크기(32 MB) + + + + High (64 MB) + 높은 크기(64 MB) + + + + Very Low (32) + 매우 낮은 크기(32) - Disabled - + Low (64) + 낮은 크기(64) - ExtendedDynamicState 1 - - - - - ExtendedDynamicState 2 - - - - - ExtendedDynamicState 3 - + Normal (128) + 보통 크기(128) - Tree View - + Disabled + 비활성화됨 + ExtendedDynamicState 1 + 확장된 동적 상태 1 + + + + ExtendedDynamicState 2 + 확장된 동적 상태 2 + + + + ExtendedDynamicState 3 + 확장된 동적 상태 3 + + + + Tree View + 트리 보기 + + + Grid View - + 그리드 보기 @@ -2217,12 +2313,12 @@ When a program attempts to open the controller applet, it is immediately closed. Applets - + 애플릿 Applet mode preference - + 애플릿 모드 선호도 @@ -2307,17 +2403,17 @@ When a program attempts to open the controller applet, it is immediately closed. CPU Backend - + CPU 백엔드 Unsafe CPU Optimization Settings - 안전하지 않은 CPU 최적화 설정 + 불안전 CPU 최적화 설정 These settings reduce accuracy for speed. - 이 설정은 속도를 위해 정확도를 떨어뜨립니다. + 이러한 설정은 속도를 위해 정확도를 낮춥니다. @@ -2394,7 +2490,7 @@ When a program attempts to open the controller applet, it is immediately closed. <div>Enable a two-tiered dispatch system. A faster dispatcher written in assembly has a small MRU cache of jump destinations is used first. If that fails, dispatch falls back to the slower C++ dispatcher.</div> - <div>2계층 디스패치 시스템을 활성화합니다. 어셈블리로 작성된 더 빠른 디스패처에는 점프 대상의 작은 MRU 캐시가 먼저 사용됩니다. 실패하면 디스패치는 더 느린 C ++ 디스패처로 대체됩니다.</div> + <div>이중 계층 디스패치 시스템을 활성화합니다. 어셈블리로 작성된 더 빠른 디스패처는 점프 대상의 MRU 캐시 크기가 작으므로 먼저 사용됩니다. 만약 이 디스패처가 실패하면, 더 느린 C++ 디스패처로 되돌아갑니다.</div> @@ -2503,9 +2599,8 @@ When a program attempts to open the controller applet, it is immediately closed. <div style="white-space: nowrap">Enabling it reduces the overhead of fastmem failure of exclusive memory accesses.</div> - - <div style="white-space: nowrap">이 최적화는 게스트 프로그램의 독점 메모리 액세스 속도를 높입니다.</div> - <div style="white-space: nowrap">활성화하면 독점 메모리 액세스의 fastmem 실패 오버헤드가 줄어듭니다.</div> + <div style="white-space: nowrap">이 최적화는 게스트 프로그램의 전용 메모리 접근 속도를 향상시킵니다.</div> + <div style="white-space: nowrap">이 옵션을 활성화하면 전용 메모리 접근 시 패스트멤 실패로 인한 오버헤드가 줄어듭니다.</div> @@ -2640,7 +2735,7 @@ When a program attempts to open the controller applet, it is immediately closed. Enable Renderdoc Hotkey - + 렌더닥 단축키 활성화 @@ -2685,12 +2780,12 @@ When a program attempts to open the controller applet, it is immediately closed. <html><head/><body><p>When checked, disables reordering of mapped memory uploads which allows to associate uploads with specific draws. May reduce performance in some cases.</p></body></html> - + <html><head/><body><p>이 옵션을 선택하면 매핑된 메모리 업로드의 재배열 기능이 비활성화되어 특정 드로우와 업로드를 연결할 수 있습니다. 경우에 따라 성능이 저하될 수 있습니다.</p></body></html> Disable Buffer Reorder - + 버퍼 재배열 비활성화 @@ -2700,7 +2795,7 @@ When a program attempts to open the controller applet, it is immediately closed. Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. - 프로그램 시작시 yuzu가 Vulkan 환경을 확인할 수 있도록 합니다. 외부 프로그램에서 유자를 보는 데 문제가 있는 경우 이 기능을 비활성화합니다. + 프로그램이 시작될 때 yuzu가 Vulkan 환경이 제대로 작동하는지 확인하도록 설정합니다. 외부 프로그램에서 yuzu를 인식하는 데 문제가 발생하는 경우 이 옵션을 비활성화하세요. @@ -2720,17 +2815,17 @@ When a program attempts to open the controller applet, it is immediately closed. Enable Auto-Stub - + 자동 스텁 활성화 Kiosk (Quest) Mode - Kiosk (Quest) 모드 + 키오스크 (퀘스트) 모드 Use dev.keys - + 개발자 키 사용 @@ -2745,37 +2840,37 @@ When a program attempts to open the controller applet, it is immediately closed. Battery Serial: - + 배터리 시리얼: Bitmask for quick development toggles - + 빠른 개발을 위한 비트마스크 토글 Set debug knobs (bitmask) - + 디버그 노브 설정(비트마스크) 16-bit debug knob set for quick development toggles - + 빠른 개발을 위한 16비트 디버그 노브 세트 (bitmask) - + (비트마스크) Debug Knobs: - + 디버그 노브: Unit Serial: - + 장치 시리얼: @@ -2790,7 +2885,7 @@ When a program attempts to open the controller applet, it is immediately closed. Flush log output on each line - + 각 줄의 로그 출력을 플러시합니다 @@ -2805,12 +2900,12 @@ When a program attempts to open the controller applet, it is immediately closed. Censor username in logs - + 로그에서 사용자 이름 검열 **This will be reset automatically when Eden closes. - + **이 설정은 Eden이 닫히면 자동으로 초기화됩니다. @@ -2818,7 +2913,7 @@ When a program attempts to open the controller applet, it is immediately closed. Configure Debug Controller - 디버그 컨트롤러 설정 + 디버그 컨트롤러 구성 @@ -2855,7 +2950,7 @@ When a program attempts to open the controller applet, it is immediately closed. Eden Configuration - + Eden 구성 @@ -2865,7 +2960,7 @@ When a program attempts to open the controller applet, it is immediately closed. Applets - + 애플릿 @@ -2909,7 +3004,7 @@ When a program attempts to open the controller applet, it is immediately closed. GraphicsExtra - + 그래픽 추가 기능 @@ -2989,7 +3084,7 @@ When a program attempts to open the controller applet, it is immediately closed. Save Data - + 저장 데이터 @@ -2999,7 +3094,7 @@ When a program attempts to open the controller applet, it is immediately closed. Path - 주소 + 경로 @@ -3029,7 +3124,7 @@ When a program attempts to open the controller applet, it is immediately closed. Mod Load Root - 모드 경로 + 모드 불러오기 경로 @@ -3054,53 +3149,53 @@ When a program attempts to open the controller applet, it is immediately closed. Select Emulated NAND Directory... - 가상 NAND 경로 선택 + 가상 NAND 경로 선택... Select Emulated SD Directory... - 가상 SD 경로 선택 + 가상 SD 경로 선택... Select Save Data Directory... - + 저장 데이터 경로 선택... Select Gamecard Path... - 게임카드 경로 설정 + 게임카드 경로 선택... Select Dump Directory... - 덤프 경로 설정 + 덤프 경로 선택... Select Mod Load Directory... - 모드 불러오기 경로 설정 + 모드 불러오기 디렉터리 선택... Save Data Directory - + 저장 데이터 디렉터리 Choose an action for the save data directory: - + 저장 데이터 디렉터리에 대한 작업을 선택하세요: Set Custom Path - + 사용자 지정 경로 설정 Reset to NAND - + NAND로 재설정 @@ -3111,7 +3206,13 @@ New: %2 Would you like to migrate saves from the old location? WARNING: This will overwrite any conflicting saves in the new location! - + 저장 데이터는 이전 위치와 새 위치 모두에 있습니다. + +이전 위치: %1 +새 위치: %2 + +이전 위치의 저장 데이터를 옮기시겠습니까? +경고: 새 위치에 있는 충돌하는 저장 데이터는 덮어쓰여집니다! @@ -3119,51 +3220,57 @@ WARNING: This will overwrite any conflicting saves in the new location! From: %1 To: %2 - + 저장 데이터를 새 위치로 옮기겠습니까? + +원본: %1 +대상: %2 Migrate Save Data - + 저장 데이터 마이그레이션 Migrating save data... - + 저장 데이터를 마이그레이션하는 중... Cancel - + 취소 Migration Failed - + 마이그레이션 실패 Failed to create destination directory. - + 대상 디렉터리를 생성하는 데 실패했습니다. Failed to migrate save data: %1 - + 저장 데이터 마이그레이션에 실패했습니다: +%1 Migration Complete - + 마이그레이션 완료 Save data has been migrated successfully. Would you like to delete the old save data? - + 저장 데이터 마이그레이션이 성공적으로 완료되었습니다. + +이전 저장 데이터를 삭제하겠습니까? @@ -3182,22 +3289,22 @@ Would you like to delete the old save data? External Content - + 외부 콘텐츠 Add directories to scan for DLCs and Updates without installing to NAND - + NAND에 설치하지 않고 DLC 및 업데이트를 검색할 디렉터리를 추가합니다 Add Directory - + 디렉터리 추가 Remove Selected - + 선택한 항목 제거 @@ -3207,27 +3314,27 @@ Would you like to delete the old save data? Eden - + Eden This reset all settings and remove all per-game configurations. This will not delete game directories, profiles, or input profiles. Proceed? - 모든 환경 설정과 게임별 맞춤 설정이 초기화됩니다. 게임 디렉토리나 프로필, 또는 입력 프로필은 삭제되지 않습니다. 진행하시겠습니까? + 이렇게 하면 모든 설정이 초기화되고 게임별 구성이 모두 제거됩니다. 게임 디렉터리, 프로필 또는 입력 프로필은 삭제되지 않습니다. 계속하겠습니까? Select External Content Directory... - + 외부 콘텐츠 경로 선택... Directory Already Added - + 디렉터리가 이미 추가됨 This directory is already in the list. - + 이 디렉터리는 이미 목록에 있습니다. @@ -3245,7 +3352,7 @@ Would you like to delete the old save data? API Settings - API 설정 + API 설정 @@ -3258,35 +3365,35 @@ Would you like to delete the old save data? 배경색: - + % - FSR sharpening percentage (e.g. 50%) + FSR/SGSR sharpening percentage (e.g. 50%) % - + Off - + VSync Off - 수직동기화 끔 + 수직 동기화 끔 - + Recommended 추천 - + On - + VSync On - 수직동기화 켬 + 수직 동기화 켬 @@ -3317,33 +3424,33 @@ Would you like to delete the old save data? Extras - + 추가 기능 Hacks - + Changing these options from their default may cause issues. Novitii cavete! - + 이러한 옵션을 기본값에서 변경하면 문제가 발생할 수 있습니다. Novitii cavete! Vulkan Extensions - + Vulkan 확장 기능 % Sample Shading percentage (e.g. 50%) - + % Extended Dynamic State is disabled on macOS due to MoltenVK compatibility issues that cause black screens. - + MoltenVK 호환성 문제로 인해 검은 화면이 발생하는 macOS에서는 확장 동적 상태가 비활성화되어 있습니다. @@ -3409,17 +3516,17 @@ Would you like to delete the old save data? Invalid - 유효하지않음 + 무효 Invalid hotkey settings - + 유효하지 않은 단축키 설정 An error occurred. Please report this issue on github. - + 오류가 발생했습니다. 이 문제를 깃허브에 신고해 주세요. @@ -3452,7 +3559,7 @@ Would you like to delete the old save data? ConfigureInput - 입력 설정 + 입력 구성 @@ -3516,7 +3623,7 @@ Would you like to delete the old save data? Docked - 거치 모드 + 독 모드 @@ -3532,7 +3639,7 @@ Would you like to delete the old save data? Configure - 설정 + 구성 @@ -3605,7 +3712,7 @@ Would you like to delete the old save data? Configure Input - 입력 설정 + 입력 구성 @@ -3718,7 +3825,7 @@ Would you like to delete the old save data? Touchscreen - 터치 스크린 + 터치스크린 @@ -3736,7 +3843,7 @@ Would you like to delete the old save data? Configure - 설정 + 구성 @@ -3763,7 +3870,7 @@ Would you like to delete the old save data? Requires restarting Eden - + Eden 재시작 필요 @@ -3803,7 +3910,7 @@ Would you like to delete the old save data? Motion / Touch - 모션 컨트롤/ 터치 + 모션 / 터치 @@ -3821,47 +3928,47 @@ Would you like to delete the old save data? Input Profiles - 입력 프로파일 + 입력 프로필 Player 1 Profile - 플레이어 1 프로파일 + 플레이어 1 프로필 Player 2 Profile - 플레이어 2 프로파일 + 플레이어 2 프로필 Player 3 Profile - 플레이어 3 프로파일 + 플레이어 3 프로필 Player 4 Profile - 플레이어 4 프로파일 + 플레이어 4 프로필 Player 5 Profile - 플레이어 5 프로파일 + 플레이어 5 프로필 Player 6 Profile - 플레이어 6 프로파일 + 플레이어 6 프로필 Player 7 Profile - 플레이어 7 프로파일 + 플레이어 7 프로필 Player 8 Profile - 플레이어 8 프로파일 + 플레이어 8 프로필 @@ -3871,7 +3978,7 @@ Would you like to delete the old save data? Player %1 profile - 플레이어 %1 프로파일 + 플레이어 %1 프로필 @@ -3879,7 +3986,7 @@ Would you like to delete the old save data? Configure Input - 입력 설정 + 입력 구성 @@ -3925,7 +4032,7 @@ Would you like to delete the old save data? Down - 아래쪽 + 아래 @@ -3957,7 +4064,7 @@ Would you like to delete the old save data? Up - 위쪽 + @@ -4002,7 +4109,7 @@ Would you like to delete the old save data? D-Pad - D-패드 + 십자키 @@ -4082,7 +4189,7 @@ Would you like to delete the old save data? Home - + HOME @@ -4127,7 +4234,7 @@ Would you like to delete the old save data? Configure - 설정 + 구성 @@ -4168,7 +4275,7 @@ Would you like to delete the old save data? Invert axis - 축 뒤집기 + 축 반전 @@ -4186,7 +4293,7 @@ Would you like to delete the old save data? Toggle axis - axis 토글 + 축 토글 @@ -4301,7 +4408,7 @@ To invert the axes, first move your joystick vertically, and then horizontally.< C-Stick - C-Stick + C스틱 @@ -4393,7 +4500,7 @@ To invert the axes, first move your joystick vertically, and then horizontally.< Configure Motion / Touch - 모션 설정 / 터치 + 모션 / 터치 구성 @@ -4408,14 +4515,14 @@ To invert the axes, first move your joystick vertically, and then horizontally.< (100, 50) - (1800, 850) - (100, 50) - (1800, 850) + (100, 50) ~ (1800, 850) - + Configure - 설정 + 구성 @@ -4425,7 +4532,7 @@ To invert the axes, first move your joystick vertically, and then horizontally.< CemuhookUDP Config - CemuhookUDP 설정 + CemuhookUDP 구성 @@ -4444,7 +4551,7 @@ To invert the axes, first move your joystick vertically, and then horizontally.< - + Test 테스트 @@ -4459,79 +4566,79 @@ To invert the axes, first move your joystick vertically, and then horizontally.< 원격 서버 - + %1:%2 %1:%2 - - - - - - + + + + + + Eden - + Eden - + Port number has invalid characters 포트 번호에 유효하지 않은 글자가 있습니다. - + Port has to be in range 0 and 65353 포트 번호는 0부터 65353까지이어야 합니다. - + IP address is not valid IP 주소가 유효하지 않습니다. - + This UDP server already exists 해당 UDP 서버는 이미 존재합니다. - + Unable to add more than 8 servers 8개보다 많은 서버를 추가하실 수는 없습니다. - + Testing 테스트 중 - + Configuring - 설정 중 + 구성 중 - + Test Successful 테스트 성공 - + Successfully received data from the server. 서버에서 성공적으로 데이터를 받았습니다. - + Test Failed 테스트 실패 - + Could not receive valid data from the server.<br>Please verify that the server is set up correctly and the address and port are correct. - 서버에서 유효한 데이터를 수신할 수 없습니다.<br>서버가 올바르게 설정되어 있고 주소와 포트가 올바른지 확인하십시오. + 서버에서 유효한 데이터를 수신할 수 없습니다.<br>서버 설정이 올바르게 되어 있는지, 주소와 포트가 정확한지 확인해 주세요. - + UDP Test or calibration configuration is in progress.<br>Please wait for them to finish. - UDP 테스트와 교정 설정이 진행 중입니다.<br>끝날 때까지 기다려주세요. + UDP 테스트 또는 보정 구성이 진행 중입니다.<br>완료될 때까지 기다려 주세요. @@ -4539,7 +4646,7 @@ To invert the axes, first move your joystick vertically, and then horizontally.< Configure mouse panning - 마우스 패닝 설정 + 마우스 패닝 구성 @@ -4549,7 +4656,7 @@ To invert the axes, first move your joystick vertically, and then horizontally.< Can be toggled via a hotkey. Default hotkey is Ctrl + F9 - 핫키를 통해 전환할 수 있습니다. 기본 핫키는 Ctrl + F9입니다 + 단축키를 통해 토글할 수 있습니다. 기본 단축키는 Ctrl + F9입니다. @@ -4629,7 +4736,7 @@ Current values are %1% and %2% respectively. Real mouse input and mouse panning are incompatible. Please disable the emulated mouse in input advanced settings to allow mouse panning. - 실제 마우스 입력과 마우스 패닝은 호환되지 않습니다. 마우스 패닝을 허용하려면 입력 고급 설정에서 에뮬레이트 마우스를 비활성화하세요. + 실제 마우스 입력과 마우스 패닝은 호환되지 않습니다. 마우스 패닝을 활성화하려면 입력 고급 설정에서 에뮬레이션된 마우스를 비활성화해 주세요. @@ -4657,7 +4764,7 @@ Current values are %1% and %2% respectively. Enable Airplane Mode - + 비행기 모드 활성화 @@ -4665,7 +4772,7 @@ Current values are %1% and %2% respectively. Dialog - Dialog + 대화 @@ -4680,7 +4787,7 @@ Current values are %1% and %2% respectively. Title ID - 타이틀 ID + 게임 ID @@ -4713,57 +4820,57 @@ Current values are %1% and %2% respectively. 일부 설정은 게임이 실행 중이 아닐 때만 사용할 수 있습니다. - + Add-Ons 부가 기능 - + System 시스템 - + CPU CPU - + Graphics 그래픽 - + Adv. Graphics 고급 그래픽 - + Ext. Graphics - + 확장 그래픽 - + Audio 오디오 - + Input Profiles 입력 프로파일 - - - Network - - - Applets - + Network + 네트워크 - + + Applets + 애플릿 + + + Properties 속성 @@ -4778,17 +4885,17 @@ Current values are %1% and %2% respectively. Add-Ons - 애드온 + 부가 기능 Import Mod from ZIP - + ZIP에서 모드 불러오기 Import Mod from Folder - + 폴더에서 모드 불러오기 @@ -4803,87 +4910,92 @@ Current values are %1% and %2% respectively. Mod Install Succeeded - + 모드 설치 성공 Successfully installed all mods. - + 모든 모드가 성공적으로 설치되었습니다. Mod Install Failed - + 모드 설치 실패 Failed to install the following mods: %1 Check the log for details. - + 다음 모드 설치에 실패했습니다: + %1 +자세한 내용은 로그를 확인하세요. Mod Folder - + 모드 폴더 Zipped Mod Location - + 압축된 모드 위치 Zipped Archives (*.zip) - + 압축된 아카이브 (*.zip) Invalid Selection - + 유효하지 않은 선택 Only mods, cheats, and patches can be deleted. To delete NAND-installed updates, right-click the game in the game list and click Remove -> Remove Installed Update. - + 모드, 치트, 패치만 삭제할 수 있습니다. +NAND에 설치된 업데이트를 삭제하려면 게임 목록에서 게임을 마우스 오른쪽 버튼으로 클릭하고 제거 -> 설치된 업데이트 제거를 클릭하세요. You are about to delete the following installed mods: - + 다음 설치된 모드를 삭제하려고 합니다: + Once deleted, these can NOT be recovered. Are you 100% sure you want to delete them? - + +삭제한 후에는 복구할 수 없습니다. 정말 100% 삭제하고 싶으신 건가요? Delete add-on(s)? - + 부가 기능을 삭제하겠습니까? Successfully deleted - + 성공적으로 삭제됨 Successfully deleted all selected mods. - + 선택한 모드가 모두 성공적으로 삭제되었습니다. &Delete - + 삭제(&D) &Open in File Manager - + 파일 관리자에서 열기(&O) @@ -4959,37 +5071,37 @@ Once deleted, these can NOT be recovered. Are you 100% sure you want to delete t Error creating user image directory - 사용자 이미지 디렉토리 생성 오류 + 유저 이미지 디렉터리 생성 중 오류 발생 Unable to create directory %1 for storing user images. - 사용자 이미지를 저장하기 위한 %1 디렉토리를 만들 수 없습니다. + 유저 이미지를 저장하기 위한 %1 디렉터리를 만들 수 없습니다. Error saving user image - + 유저 이미지 저장 중 오류 발생 Unable to save image to file - + 이미지를 파일로 저장할 수 없음 &Edit - + 편집(&E) &Delete - + 삭제(&D) Edit User - + 유저 편집 @@ -4997,7 +5109,7 @@ Once deleted, these can NOT be recovered. Are you 100% sure you want to delete t Delete this user? All of the user's save data will be deleted. - 이 사용자를 삭제하시겠습니까? 사용자의 저장 데이터가 모두 삭제됩니다. + 이 유저를 삭제하겠습니까? 유저의 저장 데이터가 모두 삭제됩니다. @@ -5017,7 +5129,7 @@ UUID: %2 Configure Ring Controller - 링 컨트롤러 설정 + 링 컨트롤러 구성 @@ -5091,7 +5203,7 @@ UUID: %2 Invert axis - 축 뒤집기 + 축 반전 @@ -5112,7 +5224,7 @@ UUID: %2 Configuring - 설정 중 + 구성 중 @@ -5174,12 +5286,12 @@ UUID: %2 <html><head/><body><p>Reads controller input from scripts in the same format as TAS-nx scripts.<br/>For a more detailed explanation, please consult the user handbook.</p></body></html> - + TAS-nx 스크립트와 동일한 형식으로 스크립트에서 컨트롤러 입력을 읽습니다. 자세한 내용은 사용자 설명서를 참조해 주세요. To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). - 재생/녹화를 제어하는 ​​단축키를 확인하려면 단축키 설정(설정 -> 일반 -> 단축키)을 참조하십시오. + 재생/녹화를 제어하는 ​​단축키를 확인하려면 단축키 설정(구성 -> 일반 -> 단축키)을 참조해 주세요. @@ -5204,12 +5316,12 @@ UUID: %2 Pause execution during loads - 로드 중 실행 일시중지 + 불러오는 중 실행 일시 중지 Show recording dialog - + 녹음 대화 표시 @@ -5219,7 +5331,7 @@ UUID: %2 Path - 주소 + 경로 @@ -5232,12 +5344,12 @@ UUID: %2 TAS Configuration - TAS 설정 + TAS 구성 Select TAS Load Directory... - TAS 로드 디렉토리 선택... + TAS 불러오기 디렉터리 선택... @@ -5245,7 +5357,7 @@ UUID: %2 Configure Touchscreen Mappings - 터치 스크린 매핑 설정 + 터치스크린 매핑 구성 @@ -5272,7 +5384,7 @@ UUID: %2 Click the bottom area to add a point, then press a button to bind. Drag points to change position, or double-click table cells to edit values. 하단 영역을 클릭하여 포인트를 추가 한 다음 버튼을 눌러 바인딩합니다. -포인트들을 끌어서 위치를 변경하거나 테이블 셀을 두 번 클릭하여 값을 편집합니다. +포인트들을 끌어서 위치를 변경하거나 테이블 셀을 더블 클릭하여 값을 편집합니다. @@ -5337,12 +5449,12 @@ Drag points to change position, or double-click table cells to edit values. Configure Touchscreen - 터치 스크린 + 터치스크린 구성 Warning: The settings in this page affect the inner workings of Eden's emulated touchscreen. Changing them may result in undesirable behavior, such as the touchscreen partially or not working. You should only use this page if you know what you are doing. - + 경고: 이 페이지의 설정은 Eden의 에뮬레이션된 터치스크린의 내부 작동 방식에 영향을 미칩니다. 설정을 변경하면 터치스크린이 부분적으로 또는 전혀 작동하지 않는 등 원치 않는 동작이 발생할 수 있습니다. 이 페이지는 자신이 무엇을 하고 있는지 정확히 알고 있는 경우에만 사용하세요. @@ -5406,12 +5518,12 @@ Drag points to change position, or double-click table cells to edit values. Title ID - 타이틀 ID + 게임 ID Title Name - 타이틀 이름 + 게임 이름 @@ -5434,7 +5546,7 @@ Drag points to change position, or double-click table cells to edit values. Note: Changing language will apply your configuration. - 참고: 언어를 변경하면 설정에 반영됩니다 + 참고: 언어를 변경하면 구성이 적용됩니다. @@ -5459,7 +5571,7 @@ Drag points to change position, or double-click table cells to edit values. Show Add-Ons Column - 추가 기능 열 표시 + 부가 기능 열 표시 @@ -5474,7 +5586,7 @@ Drag points to change position, or double-click table cells to edit values. Show Play Time Column - + 플레이 시간 열 표시 @@ -5514,7 +5626,7 @@ Drag points to change position, or double-click table cells to edit values. TextLabel - + 텍스트 라벨 @@ -5534,7 +5646,7 @@ Drag points to change position, or double-click table cells to edit values. English - English + 영어 @@ -5548,7 +5660,7 @@ Drag points to change position, or double-click table cells to edit values. Configure Vibration - 진동 설정 + 진동 구성 @@ -5620,7 +5732,7 @@ Drag points to change position, or double-click table cells to edit values. Enable Accurate Vibration - 정교한 진동 활성화 + 정밀한 진동 활성화 @@ -5638,7 +5750,7 @@ Drag points to change position, or double-click table cells to edit values. Eden Web Service - + Eden 웹 서비스 @@ -5653,7 +5765,7 @@ Drag points to change position, or double-click table cells to edit values. Generate - + 생성 @@ -5663,7 +5775,7 @@ Drag points to change position, or double-click table cells to edit values. Discord Presence - Discord 알림 + 디스코드 알림 @@ -5675,19 +5787,19 @@ Drag points to change position, or double-click table cells to edit values. All Good Tooltip - + 모두 좋음 Must be between 4-20 characters Tooltip - + 4~20자 사이여야 함 Must be 48 characters, and lowercase a-z Tooltip - + 48자여야 하며, a~z는 모두 소문자여야 함 @@ -5708,37 +5820,37 @@ Drag points to change position, or double-click table cells to edit values. Data Manager - + 데이터 관리자 Deleting ANY data is IRREVERSABLE! - + 어떤 데이터를 삭제하든 되돌릴 수 없습니다! Shaders - + 셰이더 User NAND - + 유저 NAND System NAND - + 시스템 NAND Mods - + 모드 Saves - + 저장 @@ -5751,32 +5863,32 @@ Drag points to change position, or double-click table cells to edit values. Tooltip - + 툴팁 Open with your system file manager - + 시스템 파일 관리자로 엽니다 Delete all data in this directory. THIS IS 100% IRREVERSABLE! - + 이 디렉터리의 모든 데이터를 삭제합니다. 이 작업은 100% 되돌릴 수 없습니다! Export all data in this directory. This may take a while! - + 이 디렉터리에 있는 모든 데이터를 내보냅니다. 시간이 다소 걸릴 수 있습니다! Import data for this directory. This may take a while, and will delete ALL EXISTING DATA! - + 이 디렉터리의 데이터를 가져옵니다. 시간이 다소 걸릴 수 있으며, 기존 데이터는 모두 삭제됩니다! - + Calculating... - + 계산 중... @@ -5784,27 +5896,27 @@ Drag points to change position, or double-click table cells to edit values. Eden Dependencies - + Eden 의존물 <html><head/><body><p><span style=" font-size:28pt;">Eden Dependencies</span></p></body></html> - + <html><head/><body><p><span style=" font-size:28pt;">Eden 의존물</span></p></body></html> <html><head/><body><p>The projects that make Eden possible</p></body></html> - + <html><head/><body><p>Eden을 실행할 수 있게 하는 프로젝트들</p></body></html> Dependency - + 의존물 Version - + 버전 @@ -5855,7 +5967,7 @@ Drag points to change position, or double-click table cells to edit values. Connecting - 연결중 + 연결 중 @@ -5868,438 +5980,443 @@ Drag points to change position, or double-click table cells to edit values. Username is not valid. Must be 4 to 20 alphanumeric characters. - + 유저 이름이 유효하지 않습니다. 4자에서 20자 사이의 영숫자여야 합니다. Room name is not valid. Must be 4 to 20 alphanumeric characters. - + 방 이름이 유효하지 않습니다. 4자에서 20자 사이의 영숫자여야 합니다. Username is already in use or not valid. Please choose another. - + 유저 이름이 이미 사용 중이거나 유효하지 않습니다. 다른 이름을 선택해 주세요. IP is not a valid IPv4 address. - + IP는 유효한 IPv4 주소가 아닙니다. Port must be a number between 0 to 65535. - + 포트는 0에서 65535 사이의 숫자여야 합니다. You must choose a Preferred Game to host a room. If you do not have any games in your game list yet, add a game folder by clicking on the plus icon in the game list. - + 방을 호스팅하려면 선호하는 게임을 선택해야 합니다. 게임 목록에 아직 게임이 없다면 게임 목록에서 더하기 아이콘을 클릭하여 게임 폴더를 추가하세요. Unable to find an internet connection. Check your internet settings. - + 인터넷 연결을 찾을 수 없습니다. 인터넷 설정을 확인하세요. Unable to connect to the host. Verify that the connection settings are correct. If you still cannot connect, contact the room host and verify that the host is properly configured with the external port forwarded. - + 호스트에 연결할 수 없습니다. 연결 설정이 올바른지 확인하세요. 그래도 연결할 수 없는 경우 방 관리자에게 문의하여 외부 포트 포워딩이 올바르게 구성되어 있는지 확인하세요. Unable to connect to the room because it is already full. - + 이미 방이 꽉 차서 연결할 수 없습니다. Creating a room failed. Please retry. Restarting Eden might be necessary. - + 방 생성에 실패했습니다. 다시 시도해 주세요. Eden을 재시작해야 할 수도 있습니다. The host of the room has banned you. Speak with the host to unban you or try a different room. - + 방 호스트가 당신을 차단했습니다. 호스트에게 문의하여 차단을 해제해 달라고 요청하거나 다른 방을 이용해 보세요. Version mismatch! Please update to the latest version of Eden. If the problem persists, contact the room host and ask them to update the server. - + 버전이 일치하지 않습니다! Eden을 최신 버전으로 업데이트해 주세요. 문제가 지속되면 방 관리자에게 문의하여 서버 업데이트를 요청하세요. Incorrect password. - + 비밀번호가 잘못되었습니다. An unknown error occurred. If this error continues to occur, please open an issue - + 알 수 없는 오류가 발생했습니다. 이 오류가 계속 발생하는 경우, 문제를 신고해 주세요. Connection to room lost. Try to reconnect. - + 방 연결이 끊겼습니다. 다시 연결해 보세요. You have been kicked by the room host. - + 당신은 방 호스트에게 추방당했습니다. IP address is already in use. Please choose another. - + 해당 IP 주소는 이미 사용 중입니다. 다른 주소를 선택해 주세요. You do not have enough permission to perform this action. - + 이 작업을 수행할 권한이 충분하지 않습니다. The user you are trying to kick/ban could not be found. They may have left the room. - + 강퇴/차단하려는 사용자를 찾을 수 없습니다. +방을 나갔을 수 있습니다. No valid network interface is selected. Please go to Configure -> System -> Network and make a selection. - + 유효한 네트워크 인터페이스가 선택되지 않았습니다. +구성 -> 시스템 -> 네트워크로 이동하여 선택해 주세요. Error - + 오류 GRenderWindow - - + + OpenGL not available! OpenGL을 사용할 수 없습니다! - + OpenGL shared contexts are not supported. OpenGL 공유 컨텍스트는 지원되지 않습니다. - + Eden has not been compiled with OpenGL support. - + Eden은 OpenGL 지원 기능을 포함하여 컴파일되지 않았습니다. - - - + + + Error while initializing OpenGL! OpenGL을 초기화하는 동안 오류가 발생했습니다! - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. 사용하시는 GPU가 OpenGL을 지원하지 않거나, 최신 그래픽 드라이버가 설치되어 있지 않습니다. - + Error while initializing OpenGL 4.6! OpenGL 4.6 초기화 중 오류 발생! - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 사용하시는 GPU가 OpenGL 4.6을 지원하지 않거나 최신 그래픽 드라이버가 설치되어 있지 않습니다. <br><br>GL 렌더링 장치:<br>%1 - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 - 사용하시는 GPU가 1개 이상의 OpenGL 확장 기능을 지원하지 않습니다. 최신 그래픽 드라이버가 설치되어 있는지 확인하세요. <br><br>GL 렌더링 장치:<br>%1<br><br>지원하지 않는 확장 기능:<br>%2 + 사용 중인 GPU가 하나 이상의 필수 OpenGL 확장 기능을 지원하지 않을 수 있습니다. 최신 그래픽 드라이버가 설치되어 있는지 확인해 주세요.<br><br>GL 렌더러:<br>%1<br><br>지원되지 않는 확장 기능:<br>%2 - + This build doesn't have OpenGL support. - + 이 빌드는 OpenGL을 지원하지 않습니다. GameList - + &Add New Game Directory - + 새 게임 디렉터리 추가(&A) - + Favorite 선호하는 게임 - + Start Game 게임 시작 - + Start Game without Custom Configuration - 맞춤 설정 없이 게임 시작 + 맞춤 구성 없이 게임 시작 - + Open Save Data Location 세이브 데이터 경로 열기 - + Open Mod Data Location - MOD 데이터 경로 열기 + 모드 데이터 위치 열기 - + Open Transferable Pipeline Cache - 전송 가능한 파이프라인 캐시 열기 + 이동 가능한 파이프라인 캐시 열기 - + Link to Ryujinx - + Ryujinx에 연결 - + Remove 제거 - + Remove Installed Update 설치된 업데이트 삭제 - + Remove All Installed DLC 설치된 모든 DLC 삭제 - + Remove Custom Configuration 사용자 지정 구성 제거 - + Remove Cache Storage 캐시 스토리지 제거 - + Remove OpenGL Pipeline Cache OpenGL 파이프라인 캐시 제거 - + Remove Vulkan Pipeline Cache Vulkan 파이프라인 캐시 제거 - + Remove All Pipeline Caches 모든 파이프라인 캐시 제거 - + Remove All Installed Contents 설치된 모든 컨텐츠 제거 - + Manage Play Time - + 플레이 시간 관리 - + Edit Play Time Data - + 플레이 시간 데이터 편집 - + Remove Play Time Data - + 플레이 시간 데이터 제거 - - + + Dump RomFS RomFS를 덤프 - + Dump RomFS to SDMC RomFS를 SDMC로 덤프 - + Verify Integrity - + 무결성 인증 - + Copy Title ID to Clipboard - 클립보드에 타이틀 ID 복사 + 클립보드에 게임 ID 복사 - + Navigate to GameDB entry GameDB 항목으로 이동 - + Create Shortcut 바로가기 만들기 - + Add to Desktop 데스크톱에 추가 - + Add to Applications Menu 애플리케이션 메뉴에 추가 - + Configure Game - + 게임 구성 - + Scan Subfolders 하위 폴더 스캔 - + Remove Game Directory - 게임 디렉토리 제거 + 게임 디렉터리 제거 - + ▲ Move Up ▲ 위로 이동 - + ▼ Move Down ▼ 아래로 이동 - + Open Directory Location - 디렉토리 위치 열기 + 디렉터리 위치 열기 - + Clear 초기화 - - - Name - 이름 - - - - Compatibility - 호환성 - - - - Add-ons - 부가 기능 - - - - File type - 파일 형식 - - - - Size - 크기 - - - - Play time - - GameListItemCompat - + Ingame 게임 내 - + Game starts, but crashes or major glitches prevent it from being completed. 게임이 시작되지만, 충돌이나 주요 결함으로 인해 게임이 완료되지 않습니다. - + Perfect 완벽함 - + Game can be played without issues. 문제 없이 게임 플레이가 가능합니다. - + Playable - 재생 가능 + 플레이 가능 - + Game functions with minor graphical or audio glitches and is playable from start to finish. 약간의 그래픽 또는 오디오 결함이 있는 게임 기능이 있으며 처음부터 끝까지 플레이할 수 있습니다. - + Intro/Menu 인트로/메뉴 - + Game loads, but is unable to progress past the Start Screen. 게임이 로드되지만 시작 화면을 지나서 진행할 수 없습니다. - + Won't Boot 실행 불가 - + The game crashes when attempting to startup. 게임 실행 시 크래시가 일어납니다. - + Not Tested 테스트되지 않음 - + The game has not yet been tested. 이 게임은 아직 테스트되지 않았습니다. + + GameListModel + + + Name + 이름 + + + + Compatibility + 호환성 + + + + Add-ons + 부가 기능 + + + + File type + 파일 형식 + + + + Size + 크기 + + + + Play time + 플레이 시간 + + GameListPlaceholder - + Double-click to add a new folder to the game list 더블 클릭하여 게임 목록에 새 폴더 추가 @@ -6307,17 +6424,17 @@ Please go to Configure -> System -> Network and make a selection. GameListSearchField - + %1 of %n result(s) - + %n개 결과 중 %1개 - + Filter: 필터: - + Enter pattern to filter 검색 필터 입력 @@ -6401,7 +6518,8 @@ Please go to Configure -> System -> Network and make a selection. Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid Eden account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: - + 공개 로비에 방을 알리는 데 실패했습니다. 방을 공개적으로 호스팅하려면 에뮬레이션 -> 구성 -> 웹에서 유효한 Eden 계정이 구성되어 있어야 합니다. 공개 로비에 방을 게시하지 않으려면 대신 '비공개'를 선택하세요. +디버그 메시지: @@ -6476,17 +6594,17 @@ Debug Message: Change GPU Mode - + GPU 모드 변경 Configure - + 구성 Configure Current Game - + 현재 게임 구성 @@ -6501,7 +6619,7 @@ Debug Message: Exit Eden - + Eden 종료 @@ -6511,37 +6629,37 @@ Debug Message: Load File - 파일 로드 + 파일 불러오기 Load/Remove Amiibo - Amiibo 로드/제거 + Amiibo 불러오기/제거 Browse Public Game Lobby - + 공개 게임 로비 찾아보기 Create Room - + 방 만들기 Direct Connect to Room - + 방에 직접 연결 Leave Room - + 방 나가기 Show Current Room - + 현재 방 표시 @@ -6571,7 +6689,7 @@ Debug Message: Toggle Filter Bar - 상태 표시줄 전환 + 필터 창 토글 @@ -6581,32 +6699,32 @@ Debug Message: Toggle Turbo Speed - + 터보 속도 토글 Toggle Slow Speed - + 느린 속도 토글 Toggle Mouse Panning - 마우스 패닝 활성화 + 마우스 패닝 토글 Toggle Renderdoc Capture - + 렌더닥 캡처 토글 Toggle Status Bar - 상태 표시줄 전환 + 상태 표시줄 토글 Toggle Performance Overlay - + 성능 오버레이 토글 @@ -6614,7 +6732,7 @@ Debug Message: Please confirm these are the files you wish to install. - 설치하려는 파일이 맞는지 확인하십시오. + 설치하려는 파일이 맞는지 확인해 주세요. @@ -6667,7 +6785,7 @@ Debug Message: Loading Shaders %1 / %2 - 셰이더 로딩 %1 / %2 + 셰이더 불러오는 중 %1 / %2 @@ -6756,7 +6874,7 @@ Debug Message: Refreshing - 새로 고치는 중 + 새로 고침 중 @@ -6784,7 +6902,7 @@ Debug Message: Open &Eden Folders - + Eden 폴더 열기(&E) @@ -6809,1341 +6927,1327 @@ Debug Message: &Game List Mode - + 게임 목록 모드(&G) - + Game &Icon Size - + 게임 아이콘 크기(&I) - + Reset Window Size to &720p 창 크기를 720p로 맞추기(&7) - + Reset Window Size to 720p 창 크기를 720p로 맞추기 - + Reset Window Size to &900p 창 크기를 900p로 맞추기(&9) - + Reset Window Size to 900p 창 크기를 900p로 맞추기 - + Reset Window Size to &1080p 창 크기를 1080p로 맞추기(&1) - + Reset Window Size to 1080p 창 크기를 1080p로 맞추기 - + &Multiplayer 멀티플레이어(&M) - + &Tools 도구(&T) - + Am&iibo - + Amiibo(&i) - + Launch &Applet - + 애플릿 실행(&A) - + &TAS TAS(&T) - + &Create Home Menu Shortcut - + HOME 메뉴 바로가기 만들기(&C) - + Install &Firmware - + 펌웨어 설치(&F) - + &Help 도움말(&H) - + &Install Files to NAND... - 낸드에 파일 설치(&I) + NAND에 파일 설치(&I) - + L&oad File... 파일 불러오기...(&L) - + Load &Folder... 폴더 불러오기...(&F) - + E&xit 종료(&X) - - + + &Pause 일시중지(&P) - + &Stop 정지(&S) - + &Verify Installed Contents - + 설치된 콘텐츠 확인(&V) - + &About Eden - + Eden에 대해(&A) - + Single &Window Mode - 싱글 창 모드(&W) + 단일 창 모드(&W) - + Con&figure... 설정(&f) - + Ctrl+, - + Ctrl+, - + Enable Overlay Display Applet - + 오버레이 디스플레이 애플릿 활성화 - + Show &Filter Bar 필터링 바 표시(&F) - + Show &Status Bar 상태 표시줄 보이기(&S) - + Show Status Bar 상태 표시줄 보이기 - + &Browse Public Game Lobby 공개 게임 로비 찾아보기(&B) - + &Create Room 방 만들기(&C) - + &Leave Room 방에서 나가기(&L) - + &Direct Connect to Room 방에 직접 연결(&D) - + &Show Current Room 현재 방 표시(&S) - + F&ullscreen 전체 화면(&u) - + &Restart 재시작(&R) - + Load/Remove &Amiibo... - Amiibo 로드/제거(&A)... + Amiibo 불러오기/제거(&A)... - + &Report Compatibility 호환성 보고(&R) - + Open &Mods Page 게임 모드 페이지 열기(&M) - + Open &Quickstart Guide 빠른 시작 가이드 열기(&Q) - + &FAQ FAQ(&F) - + &Capture Screenshot 스크린샷 찍기(&C) - + &Album - + 앨범(&A) - + &Set Nickname and Owner - + 닉네임과 오너 설정(&S) - + &Delete Game Data - + 게임 데이터 삭제(&D) - + &Restore Amiibo - + Amiibo 복원(&R) - + &Format Amiibo - + Amiibo 포맷(&F) - + &Mii Editor - + Mii 편집기(&M) - + &Configure TAS... - TAS설정...(&C) + TAS 구성...(&C) - + Configure C&urrent Game... - 실행중인 게임 맞춤 설정...(&u) + 실행 중인 게임 맞춤 구성...(&u) - - + + &Start 시작(&S) - + &Reset 리셋(&R) - - + + R&ecord - 레코드(&e) + 기록(&e) - + Open &Controller Menu - + 컨트롤러 메뉴 열기(&C) - + Install Decryption &Keys - + 복호화 키 설치(&K) - + &Home Menu - + HOME 메뉴(&H) - + &Desktop - + 데스크톱(&D) - + &Application Menu - + 애플리케이션 메뉴(&A) - + &Root Data Folder - + 데이터 폴더 경로(&R) - + &NAND Folder - + NAND 폴더(&N) - + &SDMC Folder - + SDMC 폴더(&S) - + &Mod Folder - + 모드 폴더(&M) - + &Log Folder - + 로그 폴더(&L) - + From Folder - + 폴더에서 - + From ZIP - + ZIP에서 - + &Eden Dependencies - + Eden 의존물(&E) - + &Data Manager - + 데이터 관리자(&D) - + &Tree View - + 트리 보기(&T) - + &Grid View - + 그리드 보기(&G) - + Game Icon Size - + 게임 아이콘 크기 - - + + None - + 없음 - + Show Game &Name - + 게임 이름 표시(&N) - + Show &Performance Overlay + 성능 오버레이 표시(&P) + + + + &Carousel View - + Small (32x32) - + 작은 크기(32x32) - + Standard (64x64) - + 기본 크기(64x64) - + Large (128x128) - + 큰 크기(128x128) - + Full Size (256x256) - + 전체 크기(256x256) - + Broken Vulkan Installation Detected - + 손상된 Vulkan 설치가 감지됨 - + Vulkan initialization failed during boot. - + 부팅 중 Vulkan 초기화에 실패했습니다. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping - + 게임 실행중 - + Loading Web Applet... - + 웹 애플릿을 불러오는 중... - - + + Disable Web Applet - + 웹 애플릿 비활성화 - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + 웹 애플릿을 비활성화하면 예기치 않은 동작이 발생할 수 있으며 슈퍼 마리오 3D 컬렉션에서만 사용해야 합니다. 웹 애플릿을 비활성화하겠습니까? +(디버그 설정에서 다시 활성화할 수 있습니다.) - + The amount of shaders currently being built - + 현재 구축 중인 셰이더 수 - + The current selected resolution scaling multiplier. - + 현재 선택된 해상도 조정 배율입니다. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. - + 현재 에뮬레이션 속도입니다. 100%보다 높거나 낮은 값은 에뮬레이션이 닌텐도 스위치보다 빠르거나 느리게 실행되고 있음을 나타냅니다. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. - + 현재 게임이 표시하는 초당 프레임 수입니다. 이 값은 게임 및 장면에 따라 다릅니다. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. - + 프레임 제한이나 수직 동기화를 제외하고 스위치 프레임을 에뮬레이션하는 데 걸리는 시간입니다. 최고 속도 에뮬레이션의 경우 최대 16.67 밀리초입니다. - + Unmute - + 음소거 해제 - + Mute - + 음소거 - + Reset Volume - + 볼륨 재설정 - + &Clear Recent Files - + 최근 파일 지우기(&C) - + &Continue - + 재개(&C) - + Warning: Outdated Game Format - + 경고: 구식 게임 형식입니다 - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. - + 현재 이 게임에 대해 구형 형식인 분해된 ROM 디렉터리 형식을 사용하고 있습니다. 이 형식은 NCA, NAX, XCI 또는 NSP와 같은 다른 형식으로 대체되었습니다. 분해된 ROM 디렉터리에는 아이콘, 메타데이터 및 업데이트 지원 기능이 없습니다.<br>Eden에서 지원하는 다양한 스위치 형식에 대한 자세한 내용은 사용자 설명서를 참조해 주세요. 이 메시지는 다시 표시되지 않습니다. - - + + Error while loading ROM! - + ROM 불러오는 중 오류 발생! - + The ROM format is not supported. - + 해당 ROM 형식은 지원되지 않습니다. - + An error occurred initializing the video core. - + 비디오 코어 초기화 중 오류가 발생했습니다. - + Eden has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-mirror.github.io/help/reference/log-files/'>How to Upload the Log File</a>. - + Eden에서 비디오 코어 실행 중 오류가 발생했습니다. 이는 일반적으로 내장 그래픽 드라이버를 포함한 GPU 드라이버가 최신 버전이 아니기 때문에 발생합니다. 자세한 내용은 로그 파일을 참조해 주세요. 로그 파일 접근 방법에 대한 자세한 내용은 다음 페이지: <a href='https://yuzu-mirror.github.io/help/reference/log-files/'>로그 파일 업로드 방법</a>을 참조해 주세요. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + ROM 불러오는 중 오류 발생! %1 - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. - + %1<br>파일을 리덤프하거나 디스코드/Stoat에서 도움을 요청해 주세요. - + An unknown error occurred. Please see the log for more details. - + 알 수 없는 오류가 발생했습니다. 자세한 내용은 로그를 참조해 주세요. - + (64-bit) - + (64비트) - + (32-bit) - + (32비트) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit - + %1 %2 - + Closing software... - + 소프트웨어 닫는 중... - + Save Data - + 저장 데이터 - + Mod Data - + 모드 데이터 - + Error Opening %1 Folder - + %1 폴더 여는 중 오류 발생 - - + + Folder does not exist! - + 폴더가 존재하지 않습니다! - + Remove Installed Game Contents? - + 설치된 게임 콘텐츠를 제거하겠습니까? - + Remove Installed Game Update? - + 설치된 게임 업데이트를 제거하겠습니까? - + Remove Installed Game DLC? - + 설치된 게임 DLC를 제거하겠습니까? - + Remove Entry - + 항목 제거 - + Delete OpenGL Transferable Shader Cache? - + OpenGL 이동 가능한 셰이더 캐시를 삭제하겠습니까? - + Delete Vulkan Transferable Shader Cache? - + Vulkan 이동 가능한 셰이더 캐시를 삭제하겠습니까? - + Delete All Transferable Shader Caches? - + 이동 가능한 셰이더 캐시를 모두 삭제하겠습니까? - + Remove Custom Game Configuration? - + 사용자 지정 게임 구성을 제거하겠습니까? + + + + Remove Cache Storage? + 캐시 저장소를 제거하겠습니까? + + + + Remove File + 파일 제거 + + + + Remove Play Time Data + 플레이 시간 데이터 제거 + + + + Reset play time? + 플레이 시간을 재설정하겠습니까? + + + + + RomFS Extraction Failed! + RomFS 추출 실패! + + + + There was an error copying the RomFS files or the user cancelled the operation. + RomFS 파일을 복사하는 중에 오류가 발생했거나 사용자가 작업을 취소했습니다. + + + + Full + 전체 + + + + Skeleton + 뼈대 + + + + Select RomFS Dump Mode + RomFS 덤프 모드 선택 + + + + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. + RomFS 덤프 방식을 선택해 주세요. 전체를 선택하면 모든 파일이 새 디렉터리로 복사되고, 뼈대를 선택하면 디렉터리 구조만 생성됩니다. + + + + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root + %1 위치에 RomFS를 추출할 수 있는 여유 공간이 부족합니다. 공간을 확보하거나 에뮬레이션 > 구성 > 시스템 > 파일 시스템 > 덤프 경로에서 다른 덤프 디렉터리를 선택해 주세요. - Remove Cache Storage? - - - - - Remove File - - - - - Remove Play Time Data - - - - - Reset play time? - - - - - - RomFS Extraction Failed! - - - - - There was an error copying the RomFS files or the user cancelled the operation. - - - - - Full - - - - - Skeleton - - - - - Select RomFS Dump Mode - - - - - Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. - - - - - There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - - - - Extracting RomFS... - + RomFS 추출 중... - - + + Cancel - + 취소 - + RomFS Extraction Succeeded! - + RomFS 추출 성공! - + The operation completed successfully. - + 작업이 성공적으로 완료되었습니다. - + Error Opening %1 - + %1 여는 중 오류 발생 - + Select Directory - + 디렉터리 선택 - + Properties - + 속성 - + The game properties could not be loaded. - + 게임 속성을 불러올 수 없습니다. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. - + 스위치 실행 파일(%1);;모든 파일(*.*) - + Load File - + 파일 불러오기 - + Open Extracted ROM Directory - + 추출된 ROM 디렉터리 열기 + + + + Invalid Directory Selected + 유효하지 않은 디렉터리 선택됨 + + + + The directory you have selected does not contain a 'main' file. + 선택한 디렉터리에는 'main' 파일이 없습니다. + + + + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) + 설치 가능한 스위치 파일(*.nca, *.nsp, *.xci);;닌텐도 콘텐츠 아카이브(*.nca);;닌텐도 제출 패키지(*.nsp);;NX 카트리지 이미지(*.xci) + + + + Install Files + 파일 설치 + + + + %n file(s) remaining + %n개 파일 남음 + + + + Installing file "%1"... + 파일 "%1" 설치 중... + + + + + Install Results + 설치 결과 + + + + To avoid possible conflicts, we discourage users from installing base games to the NAND. +Please, only use this feature to install updates and DLC. + 잠재적인 충돌을 방지하기 위해 저희는 사용자가 기본 게임을 NAND에 설치하는 것은 권장하지 않습니다. +이 기능은 업데이트 및 DLC 설치에만 사용해 주세요. + + + + %n file(s) were newly installed + + %n개 파일이 새로 설치됨 + + + + + %n file(s) were overwritten + + %n개 파일이 덮어쓰기됨 + + + + + %n file(s) failed to install + + %n개 파일 설치 실패 + + + + + System Application + 시스템 애플리케이션 + + + + System Archive + 시스템 아카이브 + + + + System Application Update + 시스템 애플리케이션 업데이트 + + + + Firmware Package (Type A) + 펌웨어 패키지 (A타입) + + + + Firmware Package (Type B) + 펌웨어 패키지 (B타입) + + + + Game + 게임 + + + + Game Update + 게임 업데이트 + + + + Game DLC + 게임 DLC - Invalid Directory Selected - - - - - The directory you have selected does not contain a 'main' file. - - - - - Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - - - - - Install Files - - - - - %n file(s) remaining - - - - - Installing file "%1"... - - - - - - Install Results - - - - - To avoid possible conflicts, we discourage users from installing base games to the NAND. -Please, only use this feature to install updates and DLC. - - - - - %n file(s) were newly installed - - - - - - %n file(s) were overwritten - - - - - - %n file(s) failed to install - - - - - - System Application - - - - - System Archive - - - - - System Application Update - - - - - Firmware Package (Type A) - - - - - Firmware Package (Type B) - - - - - Game - - - - - Game Update - - - - - Game DLC - - - - Delta Title - + Select NCA Install Type... - + NCA 설치 유형 선택... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) - + 이 NCA를 설치할 게임 유형을 선택해 주세요: +(대부분의 경우 기본값인 '게임'을 선택하면 됩니다.) - + Failed to Install - + 설치 실패 - + The title type you selected for the NCA is invalid. - + NCA에 대해 선택한 게임 유형이 유효하지 않습니다. - + File not found - + 파일을 찾을 수 없음 - + File "%1" not found - + "%1" 파일을 찾을 수 없음 - + OK - + 확인 - + Function Disabled - + 기능 비활성화됨 - + Compatibility list reporting is currently disabled. Check back later! - + 호환성 목록 보고 옵션이 현재 비활성화되어 있습니다. 나중에 다시 확인해 주세요! - + Error opening URL - + URL 여는 중 오류 발생 - + Unable to open the URL "%1". - + "%1" URL을 열 수 없습니다. - + TAS Recording - + TAS 녹화 - + Overwrite file of player 1? - + 플레이어 1의 파일을 덮어쓰겠습니까? - + Invalid config detected - + 유효하지 않은 구성 발견 - + Handheld controller can't be used on docked mode. Pro controller will be selected. - + 휴대 모드 컨트롤러는 독 모드에서 사용할 수 없습니다. 프로 컨트롤러로 선택됩니다. - - + + Amiibo - + Amiibo - - + + The current amiibo has been removed - + 현재 amiibo가 제거됨 - + Error - + 오류 - - + + The current game is not looking for amiibos - + 현재 게임은 amiibo를 찾고 있지 않습니다 - + Amiibo File (%1);; All Files (*.*) - + Amiibo 파일(%1);; 모든 파일(*.*) - + Load Amiibo - + Amiibo 불러오기 - + Error loading Amiibo data - + Amiibo 데이터 불러오는 중 오류 발생 - + The selected file is not a valid amiibo - + 선택한 파일은 유효한 amiibo가 아닙니다 - + The selected file is already on use - + 선택한 파일은 이미 사용 중입니다 - + An unknown error occurred - + 알 수 없는 오류 발생 - - - Keys not installed - - - - - - Install decryption keys and restart Eden before attempting to install firmware. - - - - - Select Dumped Firmware Source Location - - - - - Select Dumped Firmware ZIP - - - - - Zipped Archives (*.zip) - - - - - Firmware cleanup failed - - - - - Failed to clean up extracted firmware cache. -Check write permissions in the system temp directory and try again. -OS reported error: %1 - - - - + No firmware available - + 사용 가능한 펌웨어 없음 - + Firmware Corrupted - + 펌웨어 손상됨 - + Unknown applet - + 알 수 없는 애플릿 - + Applet doesn't map to a known value. - + 애플릿이 알려진 값에 매핑되지 않습니다. - + Record not found - + 기록을 찾을 수 없음 - + Applet not found. Please reinstall firmware. - + 애플릿을 찾을 수 없습니다. 펌웨어를 다시 설치해 주세요. - + Capture Screenshot - + 스크린샷 캡처 - + PNG Image (*.png) - + PNG 이미지 (*.png) - + TAS state: Running %1/%2 - + TAS 상태: %1/%2 실행 중 - + TAS state: Recording %1 - + TAS 상태: 녹화 중 %1 - + TAS state: Idle %1/%2 - + TAS 상태: %1/%2 유휴 - + TAS State: Invalid - + TAS 상태: 무효 - + &Stop Running - + 실행 중지(&S) - + Stop R&ecording - + 녹화 중지(&e) - + Building: %n shader(s) - + 구축 중: %n개 셰이더 - + Scale: %1x %1 is the resolution scaling factor - + 크기: %1x - + Speed: %1% / %2% - + 속도: %1% / %2% - + Speed: %1% - + 속도: %1% - + Game: %1 FPS - + 게임: %1 FPS - + Frame: %1 ms - + 프레임: %1 밀리초 - - - FSR - - - - + NO AA - + AA 없음 - + VOLUME: MUTE - + 볼륨: 음소거 - + VOLUME: %1% Volume percentage (e.g. 50%) - + 볼륨: %1% - + Derivation Components Missing - + 파생 구성 요소 누락 - + Decryption keys are missing. Install them now? - + 복호화 키가 없습니다. 지금 설치하겠습니까? - + Wayland Detected! - + Wayland 발견! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. Would you like to force it for future launches? - + Wayland는 심각한 성능 문제와 원인을 알 수 없는 버그가 있는 것으로 알려져 있습니다. +대신 X11 사용을 권장합니다. + +향후 출시를 위해 Wayland 사용을 강제하겠습니까? - + Use X11 - + X11 사용 - + Continue with Wayland - + Wayland와 계속 - + Don't show again - + 다시 보지 않기 - + Restart Required - + 재시작이 필요함 - + Restart Eden to apply the X11 backend. - + X11 백엔드를 적용하려면 Eden을 다시 시작하세요. - + Slow - + 저속 - + Turbo - + 터보 - + Unlocked - + 잠금 해제 - + Select RomFS Dump Target - + RomFS 덤프 대상 선택 - + Please select which RomFS you would like to dump. - + 덤프할 RomFS를 선택하세요. - + Are you sure you want to close Eden? - + 정말 Eden을 닫겠습니까? - - - + + + Eden - + Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. - + 정말 에뮬레이션을 중지하겠습니까? 저장되지 않은 진행 상황은 모두 손실됩니다. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? - + 현재 실행 중인 애플리케이션이 Eden에게 종료하지 말라고 요청했습니다. + +이 요청을 무시하고 종료하겠습니까? FXAA - + FXAA SMAA - + SMAA Nearest - + 최근방 Bilinear - + 쌍선형 Bicubic - + 쌍입방 Zero-Tangent - + 제로탄젠트 B-Spline - + B-스플라인 Mitchell - + 미첼 Spline-1 - + 스플라인-1 Gaussian - + 가우시안 Lanczos - + 란초스 ScaleForce - + ScaleForce + + + + FSR + FSR Area - + 영역 MMPX - + MMPX - + + SGSR + SGSR + + + + SGSR EdgeDir + SGSR EdgeDir + + + Docked - + 독 모드 - + Handheld - - - - - Fast - - - - - Balanced - + 휴대 모드 - Accurate - + Fast + 고속 - - Vulkan - + + Balanced + 균형 + + + + Accurate + 정확 - OpenGL GLSL - + Vulkan + Vulkan - OpenGL SPIRV - + OpenGL GLSL + OpenGL GLSL - OpenGL GLASM - + OpenGL SPIRV + OpenGL SPIRV - + + OpenGL GLASM + OpenGL GLASM + + + Null - + Null @@ -8152,7 +8256,8 @@ Would you like to bypass this and exit anyway? Linking the old directory failed. You may need to re-run with administrative privileges on Windows. OS gave error: %1 - + 이전 디렉터리 연결에 실패했습니다. Windows에서는 관리자 권한으로 다시 실행해야 할 수 있습니다. +OS 오류: %1 @@ -8163,7 +8268,13 @@ If this is not desirable, delete the following files: %2 %3 %4 - + + +구성 및 데이터가 %1과 공유된다는 점을 유의하세요. +원하지 않는 경우 다음 파일을 삭제하세요: +%2 +%3 +%4 @@ -8171,12 +8282,15 @@ If this is not desirable, delete the following files: If you wish to clean up the files which were left in the old data location, you can do so by deleting the following directory: %1 - + + +이전 데이터 위치에 남아 있는 파일을 정리하고 싶다면, 다음 디렉터리를 삭제하면 됩니다: +%1 - + Data was migrated successfully. - + 데이터 마이그레이션이 성공적으로 완료되었습니다. @@ -8184,12 +8298,12 @@ If you wish to clean up the files which were left in the old data location, you Import Mods - + 모드 가져오기 The specified folder or archive contains the following mods. Select which ones to install. - + 지정된 폴더 또는 아카이브 콘텐츠에는 다음 모드가 포함되어 있습니다. 설치할 모드를 선택하세요. @@ -8208,7 +8322,7 @@ If you wish to clean up the files which were left in the old data location, you Refreshing - 새로 고치는 중 + 새로 고침 중 @@ -8228,7 +8342,7 @@ If you wish to clean up the files which were left in the old data location, you Forum Username - 포럼 사용자이름 + 포럼 사용자 이름 @@ -8322,127 +8436,127 @@ Proceed anyway? New User - + 새 유저 Change Avatar - + 아바타 변경 Set Image - + 이미지 설정 UUID - + UUID Eden - + Eden Username - + 유저 이름 UUID must be 32 hex characters (0-9, A-F) - + UUID는 32자리 16진수 문자(0~9, A~F)여야 함 Generate - + 생성 Select User Image - + 유저 이미지 선택 Image Formats (*.jpg *.jpeg *.png *.bmp) - + 이미지 형식(*.jpg *.jpeg *.png *.bmp) No firmware available - + 사용 가능한 펌웨어 없음 Please install the firmware to use firmware avatars. - + 펌웨어 아바타를 사용하려면 펌웨어를 설치해 주세요. Error loading archive - + 아카이브 불러오는 중 오류 발생 Archive is not available. Please install/reinstall firmware. - + 아카이브를 사용할 수 없습니다. 펌웨어를 설치/재설치해 주세요. Could not locate RomFS. Your file or decryption keys may be corrupted. - + RomFS를 찾을 수 없습니다. 파일 또는 복호화 키가 손상되었을 수 있습니다. Error extracting archive - + 아카이브 추출 중 오류 발생 Could not extract RomFS. Your file or decryption keys may be corrupted. - + RomFS를 추출할 수 없습니다. 파일 또는 복호화 키가 손상되었을 수 있습니다. Error finding image directory - + 이미지 디렉터리를 찾는 중 오류 발생 Failed to find image directory in the archive. - + 아카이브에서 이미지 디렉터리를 찾지 못했습니다. No images found - + 이미지를 찾을 수 없음 No avatar images were found in the archive. - + 아카이브에서 아바타 이미지를 찾을 수 없습니다. All Good Tooltip - + 모두 좋음 Must be 32 hex characters (0-9, a-f) Tooltip - + 32자리 16진수 문자(0~9, a~f)여야 함 Must be between 1 and 32 characters Tooltip - + 1자에서 32자 사이여야 함 @@ -8462,7 +8576,7 @@ Proceed anyway? OK - OK + 확인 @@ -8488,68 +8602,68 @@ p, li { white-space: pre-wrap; } Frametime - + 프레임타임 0 ms - + 0 밀리초 Min: 0 - + 최소: 0 Max: 0 - + 최대: 0 Avg: 0 - + 평균: 0 FPS - + FPS 0 fps - + 0 fps %1 fps - + %1 fps Avg: %1 - + 평균: %1 Min: %1 - + 최소: %1 Max: %1 - + 최대: %1 %1 ms - + %1 밀리초 @@ -8565,22 +8679,22 @@ p, li { white-space: pre-wrap; } Select - + 선택 Cancel - + 취소 Background Color - + 배경색 Select Firmware Avatar - + 펌웨어 아바타 선택 @@ -8590,56 +8704,59 @@ p, li { white-space: pre-wrap; } Migration - + 마이그레이션 Clear Shader Cache - + 셰이더 캐시 지우기 Keep Old Data - + 이전 데이터 보관 Clear Old Data - + 이전 데이터 삭제 Link Old Directory - + 이전 디렉터리 연결 - + + + No - + 아니요 You can manually re-trigger this prompt by deleting the new config directory: %1 - + 새 구성 디렉터리를 삭제하면 이 프롬프트를 수동으로 다시 트리거할 수 있습니다: +%1 Migrating - + 마이그레이션 중 Migrating, this may take a while... - + 마이그레이션 중입니다. 시간이 다소 걸릴 수 있습니다... @@ -8845,13 +8962,13 @@ p, li { white-space: pre-wrap; } Share - Share + 공유 Options - Options + 옵션 @@ -8882,13 +8999,13 @@ p, li { white-space: pre-wrap; } %1%2Axis %3 - %1%2Axis %3 + %1%2축 %3 %1%2Axis %3,%4,%5 - %1%2Axis %3,%4,%5 + %1%2축 %3,%4,%5 @@ -8952,7 +9069,7 @@ p, li { white-space: pre-wrap; } Home - + HOME @@ -8983,7 +9100,7 @@ p, li { white-space: pre-wrap; } Task - Task + 태스크 @@ -9005,7 +9122,7 @@ p, li { white-space: pre-wrap; } %1%2%3Axis %4 - %1%2%3Axis %4 + %1%2%3축 %4 @@ -9016,7 +9133,7 @@ p, li { white-space: pre-wrap; } Not playing a game - 게임을 하지 않음 + 게임을 안 하고 있음 @@ -9029,47 +9146,47 @@ p, li { white-space: pre-wrap; } %1이(가) %2을(를) 플레이 중입니다 - + Play Time: %1 - + 플레이 시간: %1 - + Never Played - + 플레이한 적 없음 - + Version: %1 - + 버전: %1 - + Version: 1.0.0 - + 버전: 1.0.0 - + Installed SD Titles 설치된 SD 타이틀 - + Installed NAND Titles 설치된 NAND 타이틀 - + System Titles 시스템 타이틀 - + Add New Game Directory - 새 게임 디렉토리 추가 + 새 게임 디렉터리 추가 - + Favorites 선호하는 게임 @@ -9179,7 +9296,7 @@ p, li { white-space: pre-wrap; } Set nickname and owner: - 닉네임 및 소유자 설정: + 닉네임과 오너 설정: @@ -9189,253 +9306,291 @@ p, li { white-space: pre-wrap; } QtCommon::Content - - - Game Requires Firmware - - + Game Requires Firmware + 게임을 실행하려면 펌웨어가 필요함 + + + The game you are trying to launch requires firmware to boot or to get past the opening menu. Please <a href='https://yuzu-mirror.github.io/help/quickstart'>dump and install firmware</a>, or press "OK" to launch anyways. - + 실행하려는 게임을 실행하거나 시작 메뉴를 넘어가려면 펌웨어가 필요합니다. <a href='https://yuzu-mirror.github.io/help/quickstart'>펌웨어를 덤프하여 설치하거나</a>, 그냥 ​​"확인"을 눌러 게임을 실행해 주세요. - + Installing Firmware... - + 펌웨어 설치 중... - - - - - + + + + + Cancel - - - - - Firmware Install Failed - + 취소 - Firmware Install Succeeded - + Firmware Install Failed + 펌웨어 설치 실패 - - Firmware integrity verification failed! - + + Firmware Install Succeeded + 펌웨어 설치 성공 - + Firmware integrity verification failed! + 펌웨어 무결성 인증에 실패했습니다! + + + + Verification failed for the following files: %1 - + 다음 파일에 대한 검증에 실패했습니다: + +%1 - - + + Verifying integrity... - + 무결성 인증 중... + + + + + Integrity verification succeeded! + 무결성 인증에 성공했습니다! - Integrity verification succeeded! - + The operation completed successfully. + 작업이 성공적으로 완료되었습니다. - - - The operation completed successfully. - + + + Integrity verification failed! + 무결성 인증에 실패했습니다! - - Integrity verification failed! - + File contents may be corrupt or missing. + 파일 내용이 손상되었거나 누락되었을 수 있습니다. - - File contents may be corrupt or missing. - + + Integrity verification couldn't be performed + 무결성 인증을 진행할 수 없습니다 - Integrity verification couldn't be performed - - - - Firmware installation cancelled, firmware may be in a bad state or corrupted. File contents could not be checked for validity. - + 펌웨어 설치가 취소되었습니다. 펌웨어 상태가 좋지 않거나 손상되었을 수 있습니다. 파일 내용의 유효성을 확인할 수 없습니다. - + Select Dumped Keys Location - + 덤프된 키 위치 ​​선택 - + Decryption Keys install succeeded - + 복호화 키 설치 성공 - + Decryption Keys install failed - + 복호화 키 설치 실패 + + + + Orphaned Profiles Detected! + 사용되지 않는 프로필이 감지되었습니다! - Orphaned Profiles Detected! - - - - UNEXPECTED BAD THINGS MAY HAPPEN IF YOU DON'T READ THIS!<br>Eden has detected the following save directories with no attached profile:<br>%1<br><br>The following profiles are valid:<br>%2<br><br>Click "OK" to open your save folder and fix up your profiles.<br>Hint: copy the contents of the largest or last-modified folder elsewhere, delete all orphaned profiles, and move your copied contents to the good profile.<br><br>Still confused? See the <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/Orphaned.md'>help page</a>.<br> - + 이 내용을 읽지 않으면 예상치 못한 문제가 발생할 수 있습니다!<br>Eden이 프로필에 연결되지 않은 다음 저장 디렉터리를 발견했습니다:<br>%1<br><br>다음 프로필이 유효합니다:<br>%2<br><br>"확인"을 클릭하여 저장 폴더를 열고 프로필을 수정하세요.<br>힌트: 가장 크거나 마지막으로 수정된 폴더의 내용을 다른 곳에 복사하고, 연결되지 않은 프로필을 모두 삭제한 다음, 복사한 내용을 유효한 프로필로 이동하세요.<br><br>여전히 이해가 안 되나요? <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/Orphaned.md'>도움말 페이지</a>를 참조하세요.<br> - + Really clear data? - + 정말 데이터를 지우는 건가요? - + Important data may be lost! - + 중요 데이터가 손실될 수 있습니다! + + + + Are you REALLY sure? + 정말 확실해요? - Are you REALLY sure? - - - - Once deleted, your data will NOT come back! Only do this if you're 100% sure you want to delete this data. - + 한 번 삭제된 데이터는 절대 복구할 수 없습니다! +이 데이터를 100% 삭제하고 싶을 때만 이 작업을 수행하세요. - + Clearing... - + 지우는 중... - + Select Export Location - + 내보내기 위치 선택 - + %1.zip - + %1.zip - - + + + Zipped Archives (*.zip) - + 압축된 아카이브 (*.zip) - + Exporting data. This may take a while... - + 데이터를 내보내는 중입니다. 시간이 다소 걸릴 수 있습니다... - + Exporting - + 내보내는 중 - + Exported Successfully - + 성공적으로 내보냄 - + Data was exported successfully. - + 데이터를 성공적으로 내보냈습니다. - + Export Cancelled - + 내보내기 취소됨 - + Export was cancelled by the user. - + 사용자가 내보내기를 취소했습니다. + + + + Export Failed + 내보내기 실패 - Export Failed - - - - Ensure you have write permissions on the targeted directory and try again. - + 대상 디렉터리에 쓰기 권한이 있는지 확인하고 다시 시도하세요. - + Select Import Location - + 가져오기 위치 선택 + + + + Import Warning + 가져오기 경고 - Import Warning - - - - All previous data in this directory will be deleted. Are you sure you wish to proceed? - + 이 디렉터리의 이전 데이터가 모두 삭제됩니다. 계속하겠습니까? - + Importing data. This may take a while... - + 데이터를 가져오는 중입니다. 시간이 다소 걸릴 수 있습니다... - + Importing - + 가져오는 중 - + Imported Successfully - + 성공적으로 가져옴 - + Data was imported successfully. - + 데이터를 성공적으로 가져왔습니다. - + Import Cancelled - + 가져오기 취소됨 - + Import was cancelled by the user. - + 사용자가 가져오기를 취소했습니다. + + + + Import Failed + 가져오기 실패 - Import Failed - + Ensure you have read permissions on the targeted directory and try again. + 대상 디렉터리에 읽기 권한이 있는지 확인하고 다시 시도하세요. - - Ensure you have read permissions on the targeted directory and try again. - + + Keys not installed + 키가 설치되지 않음 + + + + Install decryption keys and restart Eden before attempting to install firmware. + 펌웨어를 설치하기 전에 복호화 키를 설치하고 Eden을 재시작하세요. + + + + Select Dumped Firmware Source Location + 덤프된 펌웨어 소스 위치 선택 + + + + Select Dumped Firmware ZIP + 덤프된 펌웨어 ZIP 선택 + + + + Firmware cleanup failed + 펌웨어 정리 실패 + + + + Failed to clean up extracted firmware cache. +Check write permissions in the system temp directory and try again. +OS reported error: %1 + 추출된 펌웨어 캐시를 정리하는 데 실패했습니다. +시스템 임시 디렉터리의 쓰기 권한을 확인하고 다시 시도하세요. +OS에서 보고된 오류: %1 @@ -9443,17 +9598,17 @@ Only do this if you're 100% sure you want to delete this data. Linked Save Data - + 연결된 저장 데이터 Save data has been linked. - + 저장 데이터가 연결되었습니다. Failed to link save data - + 저장 데이터 연결에 실패했습니다 @@ -9461,76 +9616,81 @@ Only do this if you're 100% sure you want to delete this data. %1 To: %2 - + 디렉터리를 연결할 수 없습니다: + %1 +대상: + %2 Already Linked - + 이미 연결됨 This title is already linked to Ryujinx. Would you like to unlink it? - + 이 게임은 이미 Ryujinx에 연결되어 있습니다. 연결을 해제하겠습니까? Failed to unlink old directory - + 이전 디렉터리 연결 해제에 실패했습니다 OS returned error: %1 - + OS에서 반환된 오류: %1 Failed to copy save data - + 저장 데이터 복사에 실패했습니다 Unlink Successful - + 연결 해제 성공 Successfully unlinked Ryujinx save data. Save data has been kept intact. - + Ryujinx 저장 데이터 연결이 성공적으로 해제되었습니다. 저장 데이터는 그대로 유지되었습니다. Could not find Ryujinx installation - + Ryujinx 설치 파일을 찾을 수 없음 Could not find a valid Ryujinx installation. This may typically occur if you are using Ryujinx in portable mode. Would you like to manually select a portable folder to use? - + 유효한 Ryujinx 설치 파일을 찾을 수 없습니다. 이는 일반적으로 Ryujinx를 휴대용 모드로 사용하는 경우에 발생할 수 있습니다. + +사용할 휴대용 폴더를 수동으로 선택하겠습니까? Ryujinx Portable Location - + Ryujinx 이동 가능 위치 Not a valid Ryujinx directory - + 유효한 Ryujinx 디렉터리가 아님 The specified directory does not contain valid Ryujinx data. - + 지정된 디렉터리에 유효한 Ryujinx 데이터가 없습니다. Could not find Ryujinx save data - + Ryujinx 저장 데이터를 찾을 수 없음 @@ -9538,17 +9698,17 @@ Would you like to manually select a portable folder to use? Error Removing Contents - + 콘텐츠 제거 중 오류 발생 Error Removing Update - + 업데이트 제거 중 오류 발생 Error Removing DLC - + DLC 제거 중 오류 발생 @@ -9558,252 +9718,254 @@ Would you like to manually select a portable folder to use? Successfully Removed - + 성공적으로 제거됨 Successfully removed the installed base game. - + 설치된 기본 게임 %1이(가) 성공적으로 제거됐습니다. The base game is not installed in the NAND and cannot be removed. - + 기본 게임은 NAND에 설치되어 있지 않으며 제거할 수 없습니다. Successfully removed the installed update. - + 설치된 업데이트가 성공적으로 제거됐습니다. There is no update installed for this title. - + 이 게임에 설치된 업데이트가 없습니다. There are no DLCs installed for this title. - + 이 게임에 설치된 DLC가 없습니다. Successfully removed %1 installed DLC. - + 설치된 DLC %1이(가) 성공적으로 제거됐습니다. Error Removing Transferable Shader Cache - + 이동 가능한 셰이더 캐시 제거 중 오류 발생 A shader cache for this title does not exist. - + 이 게임에 대한 셰이더 캐시가 존재하지 않습니다. Successfully removed the transferable shader cache. - + 이동 가능한 셰이더 캐시를 성공적으로 제거했습니다. Failed to remove the transferable shader cache. - + 이동 가능한 셰이더 캐시를 제거하는 데 실패했습니다. Error Removing Vulkan Driver Pipeline Cache - + Vulkan 드라이버 파이프라인 캐시 제거 중 오류 발생 Failed to remove the driver pipeline cache. - + 드라이버 파이프라인 캐시를 제거하는 데 실패했습니다. Error Removing Transferable Shader Caches - + 이동 가능한 셰이더 캐시 제거 중 오류 발생 Successfully removed the transferable shader caches. - + 이동 가능한 셰이더 캐시를 성공적으로 제거했습니다. Failed to remove the transferable shader cache directory. - + 이동 가능한 셰이더 캐시 디렉터리를 제거하는 데 실패했습니다. Error Removing Custom Configuration - + 사용자 지정 구성 제거 중 오류 발생 A custom configuration for this title does not exist. - + 이 게임에 대한 사용자 지정 구성이 존재하지 않습니다. Successfully removed the custom game configuration. - + 사용자 지정 게임 구성을 성공적으로 제거했습니다. Failed to remove the custom game configuration. - + 사용자 지정 게임 구성을 제거하지 못했습니다. Reset Metadata Cache - + 메타 데이터 캐시 초기화 The metadata cache is already empty. - + 메타데이터 캐시가 이미 비어있습니다. The operation completed successfully. - + 작업이 성공적으로 완료되었습니다. The metadata cache couldn't be deleted. It might be in use or non-existent. - + 메타데이터 캐시를 삭제할 수 없습니다. 사용 중이거나 존재하지 않을 수 있습니다. - + Create Shortcut - + 바로가기 만들기 - + Do you want to launch the game in fullscreen? - + 게임을 전체 화면으로 실행하겠습니까? - + Shortcut Created - + 바로가기 생성됨 - + Successfully created a shortcut to %1 - + %1 바로가기를 성공적으로 만듬 - + Shortcut may be Volatile! - + This will create a shortcut to the current AppImage. This may not work well if you update. Continue? - - - - - Failed to Create Shortcut - + 이렇게 하면 현재 앱 이미지에 대한 바로가기가 생성됩니다. 업데이트 후에는 제대로 작동하지 않을 수 있습니다. 계속하시겠습니까? - Failed to create a shortcut to %1 - + Failed to Create Shortcut + 바로가기 만들기 실패 - - Create Icon - + + Failed to create a shortcut to %1 + %1 바로가기를 만드는 데 실패함 - Cannot create icon file. Path "%1" does not exist and cannot be created. - + Create Icon + 아이콘 만들기 - - No firmware available - + + Cannot create icon file. Path "%1" does not exist and cannot be created. + 아이콘 파일을 만들 수 없습니다. 경로 "%1"이(가) 존재하지 않으며 만들 수 없습니다. - Please install firmware to use the home menu. - + No firmware available + 사용 가능한 펌웨어 없음 - - Home Menu Applet - + + Please install firmware to use the home menu. + HOME 메뉴를 사용하려면 펌웨어를 설치하세요. + Home Menu Applet + HOME 메뉴 애플릿 + + + Home Menu is not available. Please reinstall firmware. - + HOME 메뉴를 사용할 수 없습니다. 펌웨어를 다시 설치해 주세요. QtCommon::Mod - + Mod Name - + 모드 이름 - + What should this mod be called? - + 이 모드의 이름은 뭐라고 해야 할까요? - + RomFS - + RomFS - + ExeFS/Patch - + ExeFS/패치 - + Cheat - + 치트 - + Mod Type - + 모드 유형 - + Could not detect mod type automatically. Please manually specify the type of mod you downloaded. Most mods are RomFS mods, but patches (.pchtxt) are typically ExeFS mods. - + 모드 유형을 자동으로 감지할 수 없습니다. 다운로드한 모드의 유형을 수동으로 지정해 주세요. + +대부분의 모드는 RomFS 모드이지만, 패치 파일(.pchtxt)은 일반적으로 ExeFS 모드입니다. - - + + Mod Extract Failed - + 모드 추출 실패 - + Failed to create temporary directory %1 - + 임시 디렉터리 %1을 생성하는 데 실패함 - + Zip file %1 is empty - + 압축 파일 %1이 비어 있음 @@ -9811,12 +9973,12 @@ Most mods are RomFS mods, but patches (.pchtxt) are typically ExeFS mods. Error Opening Shader Cache - + 셰이더 캐시 여는 중 오류 발생 Failed to create or open shader cache for this title, ensure your app data directory has write permissions. - + 이 게임에 대한 셰이더 캐시를 생성하거나 열 수 없습니다. 앱 데이터 디렉터리에 쓰기 권한이 있는지 확인하세요. @@ -9824,128 +9986,133 @@ Most mods are RomFS mods, but patches (.pchtxt) are typically ExeFS mods. Contains game save data. DO NOT REMOVE UNLESS YOU KNOW WHAT YOU'RE DOING! - + 게임 저장 데이터가 포함되어 있습니다. 본인이 무엇을 하는건지 정확히 알지 못하면 절대 제거하지 마세요! Contains Vulkan and OpenGL pipeline caches. Generally safe to remove. - + Vulkan 및 OpenGL 파이프라인 캐시가 포함되어 있습니다. 일반적으로 제거해도 안전합니다. Contains updates and DLC for games. - + 게임 업데이트 및 DLC가 포함되어 있습니다. Contains firmware and applet data. - + 펌웨어 및 애플릿 데이터가 포함되어 있습니다. Contains game mods, patches, and cheats. - + 게임 모드, 패치, 치트가 포함되어 있습니다. Decryption Keys were successfully installed - + 복호화 키가 성공적으로 설치됨 Unable to read key directory, aborting - + 키 디렉터리를 읽을 수 없음, 중단 중 One or more keys failed to copy. - + 하나 이상의 키 복사에 실패했습니다. Verify your keys file has a .keys extension and try again. - + 키 파일에 .keys 확장자가 있는지 확인하고 다시 시도하세요. Decryption Keys failed to initialize. Check that your dumping tools are up to date and re-dump keys. - + 복호화 키 초기화에 실패했습니다. 덤프 도구가 최신 상태인지 확인하고 키를 다시 덤프하세요. Successfully installed firmware version %1 - + 펌웨어 버전 %1이 성공적으로 설치됨 Unable to locate potential firmware NCA files - + 잠재적인 펌웨어 NCA 파일을 찾을 수 없음 Failed to delete one or more firmware files. - + 펌웨어 파일 하나 이상을 삭제하는 데 실패했습니다. One or more firmware files failed to copy into NAND. - + 하나 이상의 펌웨어 파일이 NAND로 복사되는 데 실패했습니다. Firmware installation cancelled, firmware may be in a bad state or corrupted. Restart Eden or re-install firmware. - + 펌웨어 설치가 취소되었습니다. 펌웨어 상태가 좋지 않거나 손상되었을 수 있습니다. Eden을 재시작하거나 펌웨어를 재설치하세요. Firmware missing. Firmware is required to run certain games and use the Home Menu. - + 펌웨어가 없습니다. 일부 게임을 실행하고 HOME 메뉴를 사용하려면 펌웨어가 필요합니다. Firmware reported as present, but was unable to be read. Check for decryption keys and redump firmware if necessary. - + 펌웨어가 있는 것으로 보고되었지만 읽을 수 없습니다. 복호화 키를 확인하고 필요한 경우 펌웨어를 다시 덤프하세요. Eden has detected user data for the following emulators: - + Eden은 다음 에뮬레이터에 대한 유저 데이터를 발견했습니다. Would you like to migrate your data for use in Eden? Select the corresponding button to migrate data from that emulator. This may take a while. - + Eden에서 사용하기 위해 데이터를 마이그레이션하겠습니까? +해당 에뮬레이터에서 데이터를 마이그레이션하려면 해당 버튼을 선택하세요. +이 작업은 다소 시간이 걸릴 수 있습니다. Clearing shader cache is recommended for all users. Do not uncheck unless you know what you're doing. - + 모든 사용자에게 셰이더 캐시를 지우는 것을 권장합니다. +무엇을 하는 건지 정확히 알지 못하는 경우 이 옵션을 해제하지 마세요. Keeps the old data directory. This is recommended if you aren't space-constrained and want to keep separate data for the old emulator. - + 이전 데이터 디렉터리를 유지합니다. 저장 공간에 제한이 없고 이전 에뮬레이터 데이터를 별도로 보관하려는 경우 이 옵션을 권장합니다. Deletes the old data directory. This is recommended on devices with space constraints. - + 이전 데이터 디렉터리를 삭제합니다. +저장 공간이 제한된 기기에서 사용하는 것이 좋습니다. Creates a filesystem link between the old directory and Eden directory. This is recommended if you want to share data between emulators. - + 이전 디렉터리와 Eden 디렉터리 사이를 연결할 파일 시스템을 생성합니다. +에뮬레이터 간에 데이터를 공유하려는 경우 이 방법을 권장합니다. Ryujinx title database does not exist. - + Ryujinx 게임 데이터베이스가 존재하지 않습니다. @@ -9965,12 +10132,12 @@ This is recommended if you want to share data between emulators. No items found in Ryujinx title database. - + Ryujinx 게임 데이터베이스에서 항목을 찾을 수 없습니다. Title %1 not found in Ryujinx title database. - + 게임 %1을 Ryujinx 게임 데이터베이스에서 찾을 수 없습니다. @@ -9993,7 +10160,7 @@ This is recommended if you want to share data between emulators. 1 - 8 - 1 - 8 + 1 ~ 8 @@ -10062,7 +10229,7 @@ This is recommended if you want to share data between emulators. Use Current Config - 현재 설정 사용 + 현재 구성 사용 @@ -10114,7 +10281,7 @@ This is recommended if you want to share data between emulators. Docked - 거치 모드 + 독 모드 @@ -10125,7 +10292,7 @@ This is recommended if you want to share data between emulators. Configure - 설정 + 구성 @@ -10195,7 +10362,7 @@ This is recommended if you want to share data between emulators. Not enough controllers - + 컨트롤러가 충분하지 않음 @@ -10235,21 +10402,21 @@ This is recommended if you want to share data between emulators. Error Code: %1-%2 (0x%3) - 에러 코드: %1-%2 (0x%3) + 오류 코드: %1-%2 (0x%3) An error has occurred. Please try again or contact the developer of the software. 오류가 발생했습니다. -다시 시도해 보시거나 해당 소프트웨어 개발자에게 연락하십시오. +다시 시도해 보거나 해당 소프트웨어 개발자에게 문의해 주세요. An error occurred on %1 at %2. Please try again or contact the developer of the software. - %2에서 %1에 대한 오류가 발생했습니다. -다시 시도해 보시거나 해당 소프트웨어 개발자에게 문의 하십시오. + %1의 %2에서 오류가 발생했습니다. +다시 시도해 보거나 해당 소프트웨어 개발자에게 문의해 주세요. @@ -10278,7 +10445,7 @@ Please try again or contact the developer of the software. Users - 사용자 + 유저 @@ -10289,7 +10456,7 @@ Please try again or contact the developer of the software. Profile Selector - 프로필 선택 + 프로필 선택기 @@ -10309,7 +10476,7 @@ Please try again or contact the developer of the software. Who is using Nintendo eShop? - 누가 Nintendo eShop을 사용하고 있습니까? + 누가 닌텐도 e숍을 사용하고 있습니까? @@ -10324,32 +10491,32 @@ Please try again or contact the developer of the software. Select a user to link to a Nintendo Account. - Nintendo 계정에 연결할 사용자를 선택하십시오. + 닌텐도 어카운트에 연결할 유저를 선택하세요. Change settings for which user? - 어떤 사용자의 설정을 변경하시겠습니까? + 어떤 유저의 설정을 변경하겠습니까? Format data for which user? - 어떤 사용자의 데이터를 포맷하시겠습니까? + 어떤 유저의 데이터를 포맷하겠습니까? Which user will be transferred to another console? - 어떤 사용자가 다른 본체로 이전되나요? + 어떤 유저가 다른 본체로 이전되나요? Send save data for which user? - 어떤 사용자의 저장 데이터를 보내시겠습니까? + 어떤 유저의 저장 데이터를 보내겠습니까? Select a user: - 사용자를 선택하세요: + 유저를 선택하세요: @@ -10381,7 +10548,7 @@ p, li { white-space: pre-wrap; } OK - OK + 확인 @@ -10394,39 +10561,41 @@ p, li { white-space: pre-wrap; } Ryujinx Link - + Ryujinx 연동 Linking save data to Ryujinx lets both Ryujinx and Eden reference the same save files for your games. By selecting "From Eden", previous save data stored in Ryujinx will be deleted, and vice versa for "From Ryujinx". - + Ryujinx와 Eden에 저장 데이터를 연동하면 Ryujinx와 Eden 모두 동일한 게임 저장 파일을 참조할 수 있습니다. + +"Eden에서 가져오기"를 선택하면 Ryujinx에 저장된 이전 저장 데이터가 삭제되고, "Ryujinx에서 가져오기"를 선택하면 그 반대로 Ryujinx에 저장된 이전 저장 데이터가 삭제됩니다. From Eden - + Eden에서 From Ryujinx - + Ryujinx에서 Cancel - + 취소 Failed to link save data - + 저장 데이터 연결에 실패했습니다 OS returned error: %1 - + OS에서 반환된 오류: %1 @@ -10442,27 +10611,27 @@ By selecting "From Eden", previous save data stored in Ryujinx will be Set Play Time Data - + 플레이 시간 데이터 설정 Hours: - + 시간: Minutes: - + 분: Seconds: - + 초: Total play time reached maximum. - + 총 플레이 시간이 최대치에 도달했습니다. @@ -10470,92 +10639,93 @@ By selecting "From Eden", previous save data stored in Ryujinx will be Update Available - + 업데이트 가능 <a href="%1">View on Forgejo</a> - + <a href="%1">Forgejo에서 보기</a> Would you like to install this update? - + 이 업데이트를 설치하겠습니까? Available Versions - + 사용 가능한 버전 %1 is available for download. - + %1 다운로드가 가능합니다. - + New Version Location - + 새 버전 위치 - + All Files (*.*) - - - - - - - Failed to save file - + 모든 파일(*.*) + + + Failed to save file + 파일 저장 실패 + + + Could not open file %1 for writing. - + Downloading... - + 다운로드 중... - + Cancel - + 취소 - + Could not write to file %1. - + 파일 %1에 쓸 수 없습니다. - + Could not commit to file %1. - + Failed to download file - + 파일 다운로드 실패 - + Could not download from %1%2 Error code: %3 - - - - - Download Complete - + %1%2에서 다운로드할 수 없습니다 +오류 코드: %3 + Download Complete + 다운로드 완료 + + + Successfully downloaded %1. Would you like to open it? - + %1이(가) 성공적으로 다운로드됐습니다. 열어보겠습니까? \ No newline at end of file diff --git a/dist/languages/nb.ts b/dist/languages/nb.ts index 1c876beafc..cd133201a8 100644 --- a/dist/languages/nb.ts +++ b/dist/languages/nb.ts @@ -704,7 +704,7 @@ Options lower than 1X can cause artifacts. - Determines how sharpened the image will look using FSR's dynamic contrast. + Determines how sharpened the image will look using FSR's or SGSR's dynamic contrast. @@ -755,35 +755,35 @@ Disabling it is only intended for debugging. - + Use asynchronous GPU emulation - + Uses an extra CPU thread for rendering. This option should always remain enabled. - + NVDEC emulation: NVDEC-emulering: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. - + ASTC Decoding Method: - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -792,55 +792,66 @@ stuttering but may present artifacts. - + ASTC Recompression Method: - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. - + Frame Pacing Mode (Vulkan only) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. - + VRAM Usage Mode: - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. - + Skip CPU Inner Invalidation - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. - + + Anti-Flicker + + + + + Forces GPU fence callbacks to wait for submitted GPU work. +Use with Fast GPU mode, to avoid flicker with lower performance impact. + + + + VSync Mode: VSync-modus: - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -848,1361 +859,1381 @@ Immediate (no synchronization) presents whatever is available and can exhibit te - + Sync Memory Operations - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. - + Enable asynchronous presentation (Vulkan only) Aktiver asynkron presentasjon (kun Vulkan) - + Slightly improves performance by moving presentation to a separate CPU thread. - + Force maximum clocks (Vulkan only) Tving maksikal klokkehastighet (kun Vulkan) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. Kjører arbeid i bakgrunnen mens den venter på grafikkommandoer for å forhindre at GPU-en senker klokkehastigheten. - + Anisotropic Filtering: Anisotropisk filtrering: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. - + GPU Mode: GPU-modus: - + Controls the GPU emulation mode. Most games render fine with Fast or Balanced modes, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. - + DMA Accuracy: DMA-nøyaktighet: - + Controls the DMA precision accuracy. Safe precision fixes issues in some games but may degrade performance. - + Enable asynchronous shader compilation - + May reduce shader stutter. - + Fast GPU Time - + Overclocks the emulated GPU to increase dynamic resolution and render distance. Use 256 for maximal performance and 512 for maximal graphics fidelity. - + GPU Unswizzle - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. - + GPU Unswizzle Max Texture Size - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. - + GPU Unswizzle Stream Size - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. - + GPU Unswizzle Chunk Size - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. - + Use Vulkan pipeline cache Bruk Vulkan-rørledningsbuffer - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. - + Enable Compute Pipelines (Intel Vulkan Only) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. - + Enable Reactive Flushing Aktiver Reaktiv Tømming - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. - + Sync to framerate of video playback Synkroniser med bildefrekvensen for videoavspilling - + Run the game at normal speed during video playback, even when the framerate is unlocked. Kjør spillet i normal hastighet under videoavspilling, selv når bildefrekvensen er låst opp. - + Barrier feedback loops Tilbakekoblingssløyfer for barrierer - + Improves rendering of transparency effects in specific games. Forbedrer gjengivelsen av transparenseffekter i spesifikke spill. - + Enable buffer history - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. - + Fix bloom effects - + Removes bloom in Burnout. - + Enable Legacy Rescale Pass - + May fix rescale issues in some games by relying on behavior from the previous implementation. Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. - + Extended Dynamic State - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. - + Vertex Input Dynamic State - + Enables vertex input dynamic state feature for better quality and performance. - + Sample Shading - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. - + RNG Seed RNG-frø - + Controls the seed of the random number generator. Mainly used for speedrunning. - + Device Name Enhetsnavn - + The name of the console. Konsollens navn. - + + Homebrew Args + + + + + Command-line arguments passed to homebrew at launch (e.g. -noglsl). + + + + Custom RTC Date: Selvvalgt RTC-dato: - + This option allows to change the clock of the console. Can be used to manipulate time in games. - + The number of seconds from the current unix time - + Language: Språk: - + This option can be overridden when region setting is auto-select - + Region: Region: - + The region of the console. Konsollens region. - + Time Zone: Tidssone: - + The time zone of the console. Konsollens tidssone. - + Sound Output Mode: Lydutdatamodus: - + Console Mode: Konsollmodus: - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. - + Prompt for user profile on boot - + Useful if multiple people use the same PC. - + Pause when not in focus - + Pauses emulation when focusing on other windows. - + Confirm before stopping emulation Bekreft før stopping av emulering - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. - + Hide mouse on inactivity Gjem mus under inaktivitet - + Hides the mouse after 2.5s of inactivity. - + Disable controller applet Deaktiver kontroller-appleten - + Forcibly disables the use of the controller applet in emulated programs. When a program attempts to open the controller applet, it is immediately closed. - + Check for updates Se etter oppdateringer - + Whether or not to check for updates upon startup. - + Enable Gamemode - + Force X11 as Graphics Backend - + Custom frontend - + Real applet - + Never Aldri - + On Load - + Always Alltid - + CPU CPU - + GPU Skjermkort - + CPU Asynchronous - + Uncompressed (Best quality) Ukomprimert (Best kvalitet) - + BC1 (Low quality) BC1 (Lav kvalitet) - + BC3 (Medium quality) BC3 (Middels kvalitet) - - + + Auto Auto - + 30 FPS 30 FPS - + 60 FPS 60 FPS - + 90 FPS 90 FPS - + 120 FPS 120 FPS - + Conservative - + Aggressive Aggressiv - + Vulkan Vulkan - + OpenGL GLSL OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) OpenGL SPIR-V (eksperimentelt, kun AMD/Mesa) - + Null Null - + Fast Rask - + Balanced Balansert - - + + Accurate Nøyaktig - - + + Default Standard - + Unsafe (fast) Utrygg (raskt) - + Safe (stable) Trygg (stabil) - + Unsafe Utrygt - + Paranoid (disables most optimizations) Paranoid (deaktiverer de fleste optimaliseringer) - + Debugging - + Dynarmic - + NCE NCE - + Borderless Windowed Rammeløst vindu - + Exclusive Fullscreen Eksklusiv fullskjerm - + No Video Output Ingen videoutdata - + CPU Video Decoding CPU-videodekoding - + GPU Video Decoding (Default) GPU-videodekoding (Standard) - + 0.25X (180p/270p) [EXPERIMENTAL] 0,25× (180p/270p) [EKSPERIMENTELL] - + 0.5X (360p/540p) [EXPERIMENTAL] 0,5× (360p/540p) [EKSPERIMENTELL] - + 0.75X (540p/810p) [EXPERIMENTAL] 0,75× (540p/810p) [EKSPERIMENTELL] - + 1X (720p/1080p) 1× (720p/1080p) - + 1.25X (900p/1350p) [EXPERIMENTAL] 1,25× (900p/1350p) [EKSPERIMENTELL] - + 1.5X (1080p/1620p) [EXPERIMENTAL] 1,5× (1080p/1620p) [EKSPERIMENTELL] - + 2X (1440p/2160p) 2× (1440p/2160p) - + 3X (2160p/3240p) 3× (2160p/3240p) - + 4X (2880p/4320p) 4× (2880p/4320p) - + 5X (3600p/5400p) 5× (3600p/5400p) - + 6X (4320p/6480p) 6× (4320p/6480p) - + 7X (5040p/7560p) 7× (5040p/7560p) - + 8X (5760p/8640p) 8× (5760p/8640p) - + Nearest Neighbor Nærmeste nabo - + Bilinear Bilineær - + Bicubic Bikubisk - + Gaussian Gaussisk - + Lanczos Lanczos - + ScaleForce ScaleForce - + AMD FidelityFX Super Resolution AMD FidelityFX Super Resolution - + Area Område - + MMPX MMPX - + Zero-Tangent - + B-Spline B-Spline - + Mitchell Mitchell - + Spline-1 Spline-1 - - + + Snapdragon Game Super Resolution + + + + + Snapdragon Game Super Resolution EdgeDir + + + + + None Ingen - + FXAA FXAA - + SMAA SMAA - + Default (16:9) Standard (16:9) - + Force 4:3 Tving 4:3 - + Force 21:9 Tving 21:9 - + Force 16:10 Tving 16:10 - + Stretch to Window Strekk til vinduet - + Automatic Automatisk - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x - + 32x 32x - + 64x 64x - + Japanese (日本語) Japansk (日本語) - + American English Amerikansk engelsk - + French (français) Fransk (français) - + German (Deutsch) Tysk (Deutsch) - + Italian (italiano) Italiensk (italiano) - + Spanish (español) Spansk (español) - + Chinese Kinesisk - + Korean (한국어) Koreansk (한국어) - + Dutch (Nederlands) Nederlandsk (Nederlands) - + Portuguese (português) Portugisisk (português) - + Russian (Русский) Russisk (Русский) - + Taiwanese Taiwansk - + British English Britisk engelsk - + Canadian French Kanadisk fransk - + Latin American Spanish Latinamerikansk spansk - + Simplified Chinese Forenklet kinesisk - + Traditional Chinese (正體中文) Tradisjonell kinesisk (正體中文) - + Brazilian Portuguese (português do Brasil) Brasiliansk portugisisk (português do Brasil) - + Polish (polska) Polsk (polska) - + Thai (แบบไทย) Thai (แบบไทย) - - + + Japan Japan - + USA USA - + Europe Europa - + Australia Australia - + China Folkerepublikken Kina - + Korea Korea - + Taiwan Taiwan (Republikken Kina) - + Auto (%1) Auto select time zone Auto (%1) - + Default (%1) Default time zone Standard (%1) - + CET Sentraleuropeisk tid - + CST6CDT CST6CDT - + Cuba Cuba - + EET Østeuropeisk tid - + Egypt Egypt - + Eire Republikken Irland - + EST EST - + EST5EDT EST5EDT - + GB Storbritannia - + GB-Eire GB-Eire - + GMT GMT - + GMT+0 GMT+0 - + GMT-0 GMT-0 - + GMT0 GMT0 - + Greenwich Greenwich - + Hongkong Hongkong - + HST HST - + Iceland Island - + Iran Iran - + Israel Israel - + Jamaica Jamaica - + Kwajalein Kwajalein - + Libya Libya - + MET MET - + MST MST - + MST7MDT MST7MDT - + Navajo Navajo - + NZ NZ - + NZ-CHAT NZ-CHAT - + Poland Polen - + Portugal Portugal - + PRC Folkerepublikken Kina - + PST8PDT PST8PDT - + ROC Taiwan (Republikken Kina) - + ROK Sør-Korea - + Singapore Singapore - + Turkey Tyrkia - + UCT UCT - + Universal Universalt - + UTC UTC - + W-SU W-SU - + WET WET - + Zulu Zulu - + Mono Mono - + Stereo Stereo - + Surround Surround - + 4GB DRAM (Default) 4 GB DRAM (Standard) - + 6GB DRAM (Unsafe) 6 GB DRAM (Utrygt) - + 8GB DRAM 8 GB DRAM - + 10GB DRAM (Unsafe) 10 GB DRAM (Utrygt) - + 12GB DRAM (Unsafe) 12 GB DRAM (Utrygt) - + Docked I dokking - + Handheld Håndholdt - - + + Off Av - + Boost (1700MHz) Boost (1700 MHz) - + Fast (2000MHz) Rask (2000 MHz) - + Always ask (Default) Spør alltid (Standard) - + Only if game specifies not to stop - + Never ask Aldri spør - - + + Medium (256) Middels (256) - - + + High (512) Høy (512) - + Very Small (16 MB) Veldig liten (16 MB) - + Small (32 MB) Liten (32 MB) - + Normal (128 MB) Normal (128 MB) - + Large (256 MB) Stor (256 MB) - + Very Large (512 MB) Veldig stor (512 MB) - + Very Low (4 MB) Veldig lav (4 MB) - + Low (8 MB) Lav (8 MB) - + Normal (16 MB) Normal (16 MB) - + Medium (32 MB) Middels (32 MB) - + High (64 MB) Høy (64 MB) - + Very Low (32) Svært lav (32) - + Low (64) Lav (64) - + Normal (128) Normal (128) - + Disabled Skrudd av - + ExtendedDynamicState 1 - + ExtendedDynamicState 2 - + ExtendedDynamicState 3 - + Tree View - + Grid View @@ -3257,33 +3288,33 @@ Would you like to delete the old save data? Bakgrunnsfarge: - + % - FSR sharpening percentage (e.g. 50%) - % + FSR/SGSR sharpening percentage (e.g. 50%) + - + Off Av - + VSync Off VSync Av - + Recommended Anbefalt - + On - + VSync On VSync På @@ -4412,7 +4443,7 @@ For å invertere aksene, flytt først stikken vertikalt, og så horistonalt. - + Configure Sett opp @@ -4443,7 +4474,7 @@ For å invertere aksene, flytt først stikken vertikalt, og så horistonalt. - + Test Test @@ -4458,77 +4489,77 @@ For å invertere aksene, flytt først stikken vertikalt, og så horistonalt.Fjern tjener - + %1:%2 %1:%2 - - - - - - + + + + + + Eden Eden - + Port number has invalid characters Portnummeret har ugyldige tegn - + Port has to be in range 0 and 65353 Porten må være i intervallet 0 til 65353 - + IP address is not valid IP-adressen er ugyldig - + This UDP server already exists Denne UDP-tjeneren eksisterer allerede - + Unable to add more than 8 servers Kan ikke legge til mer enn 8 tjenere - + Testing Testing - + Configuring Konfigurering - + Test Successful Testen var vellykket - + Successfully received data from the server. Mottatt data fra serveren vellykket. - + Test Failed Testen mislyktes - + Could not receive valid data from the server.<br>Please verify that the server is set up correctly and the address and port are correct. Kunne ikke motta gyldig data fra serveren.<br>Vennligst bekreft at serveren er satt opp riktig og at adressen og porten er riktige. - + UDP Test or calibration configuration is in progress.<br>Please wait for them to finish. UDP-test eller kalibrasjonskonfigurering er i fremgang.<br>Vennligst vent for dem til å bli ferdig. @@ -4713,57 +4744,57 @@ Gjeldende verdier er henholdsvis %1% og %2%. Noen innstillinger er bare tilgjengelige når spillet ikke er i gang. - + Add-Ons Tillegg - + System System - + CPU CPU - + Graphics Grafikk - + Adv. Graphics Avansert grafikk - + Ext. Graphics - + Audio Lyd - + Input Profiles Inndataprofiler - + Network Nettverk - + Applets - + Properties Egenskaper @@ -5773,7 +5804,7 @@ Dra punkter for å endre posisjon, eller dobbelttrykk på tabellfelter for å re - + Calculating... @@ -5975,50 +6006,50 @@ Please go to Configure -> System -> Network and make a selection. GRenderWindow - - + + OpenGL not available! OpenGL ikke tilgjengelig! - + OpenGL shared contexts are not supported. Delte OpenGL-kontekster støttes ikke. - + Eden has not been compiled with OpenGL support. - - - + + + Error while initializing OpenGL! Feil under initialisering av OpenGL! - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. Det kan hende at GPU-en din ikke støtter OpenGL, eller at du ikke har den nyeste grafikkdriveren. - + Error while initializing OpenGL 4.6! Feil under initialisering av OpenGL 4.6! - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 Det kan hende at GPU-en din ikke støtter OpenGL 4.6, eller at du ikke har den nyeste grafikkdriveren.<br><br>GL-renderer:<br>%1 - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 Det kan hende at GPU-en din ikke støtter én eller flere nødvendige OpenGL-utvidelser. Vennligst sørg for at du har den nyeste grafikkdriveren.<br><br>GL-renderer: <br>%1<br><br>Ikke-støttede utvidelser:<br>%2 - + This build doesn't have OpenGL support. @@ -6026,279 +6057,282 @@ Please go to Configure -> System -> Network and make a selection. GameList - + &Add New Game Directory Legg til ny spillm&appe - + Favorite Legg til som favoritt - + Start Game Start spill - + Start Game without Custom Configuration Start spill uten tilpasset oppsett - + Open Save Data Location Åpne lagrefilplassering - + Open Mod Data Location Åpne moddataplassering - + Open Transferable Pipeline Cache Åpne overførbar rørledningsbuffer - + Link to Ryujinx - + Remove Fjern - + Remove Installed Update Fjern installert oppdatering - + Remove All Installed DLC Fjern all installert DLC - + Remove Custom Configuration Fjern tilpasset oppsett - + Remove Cache Storage Tøm hurtiglager - + Remove OpenGL Pipeline Cache Fjern OpenGL-rørledningsbuffer - + Remove Vulkan Pipeline Cache Fjern Vulkan-rørledningsbuffer - + Remove All Pipeline Caches Fjern alle rørledningsbuffere - + Remove All Installed Contents Fjern alt installert innhold - + Manage Play Time - + Edit Play Time Data - + Remove Play Time Data - - + + Dump RomFS Dump RomFS - + Dump RomFS to SDMC Dump RomFS til SDMC - + Verify Integrity Verifiser integritet - + Copy Title ID to Clipboard Kopier Tittel-ID til Utklippstavle - + Navigate to GameDB entry Naviger til GameDB-oppføring - + Create Shortcut Opprett snarvei - + Add to Desktop Legg Til På Skrivebordet - + Add to Applications Menu Legg Til Applikasjonsmenyen - + Configure Game Oppsett av spillet - + Scan Subfolders Skann Undermapper - + Remove Game Directory Fjern spillmappe - + ▲ Move Up ▲ Flytt Opp - + ▼ Move Down ▼ Flytt Ned - + Open Directory Location Åpne Spillmappe - + Clear Tøm - - - Name - Navn - - - - Compatibility - Kompatibilitet - - - - Add-ons - Tilleggsprogrammer - - - - File type - Filtype - - - - Size - Størrelse - - - - Play time - - GameListItemCompat - + Ingame i Spillet - + Game starts, but crashes or major glitches prevent it from being completed. Spillet starter, men krasjer eller større feil gjør at det ikke kan fullføres. - + Perfect Perfekt - + Game can be played without issues. Spillet kan spilles uten problemer. - + Playable Spillbar - + Game functions with minor graphical or audio glitches and is playable from start to finish. Spillet fungerer med mindre grafiske eller lydfeil og kan spilles fra start til slutt. - + Intro/Menu Intro/Meny - + Game loads, but is unable to progress past the Start Screen. Spillet lastes inn, men kan ikke gå videre forbi startskjermen. - + Won't Boot Vil ikke starte - + The game crashes when attempting to startup. Spillet krasjer under oppstart. - + Not Tested Ikke testet - + The game has not yet been tested. Spillet har ikke blitt testet ennå. + + GameListModel + + + Name + + + + + Compatibility + + + + + Add-ons + + + + + File type + + + + + Size + + + + + Play time + + + GameListPlaceholder - + Double-click to add a new folder to the game list Dobbeltrykk for å legge til en ny mappe i spillisten @@ -6306,17 +6340,17 @@ Please go to Configure -> System -> Network and make a selection. GameListSearchField - + %1 of %n result(s) %1 of %n resultat%1 av %n resultat(er) - + Filter: Filter: - + Enter pattern to filter Angi mønster for å filtrere @@ -6811,1139 +6845,1099 @@ Debug Message: &Spilliste-modus - + Game &Icon Size Spill&ikonstørrelse - + Reset Window Size to &720p Tilbakestill vindusstørrelsen til &720p - + Reset Window Size to 720p Tilbakestill vindusstørrelsen til 720p - + Reset Window Size to &900p Tilbakestill vindusstørrelsen til &900p - + Reset Window Size to 900p Tilbakestill vindusstørrelsen til 900p - + Reset Window Size to &1080p Tilbakestill vindusstørrelsen til &1080p - + Reset Window Size to 1080p Tilbakestill vindusstørrelsen til 1080p - + &Multiplayer &Flerspiller - + &Tools Verk&tøy - + Am&iibo am&iibo - + Launch &Applet - + &TAS &TAS - + &Create Home Menu Shortcut - + Install &Firmware Installér &fastvare - + &Help &Hjelp - + &Install Files to NAND... &Installér filer til NAND ... - + L&oad File... La&st inn fil ... - + Load &Folder... Last inn ma&ppe... - + E&xit A&vslutt - - + + &Pause &Pause - + &Stop &Stopp - + &Verify Installed Contents - + &About Eden &Om Eden - + Single &Window Mode Enkelt&vindusmodus - + Con&figure... Se&tt opp ... - + Ctrl+, - + Enable Overlay Display Applet - + Show &Filter Bar Vis &filterlinje - + Show &Status Bar Vis &statuslinje - + Show Status Bar Vis statuslinje - + &Browse Public Game Lobby Bla gjennom den offentlige spillo&bbyen - + &Create Room &Opprett rom - + &Leave Room &Forlat rom - + &Direct Connect to Room &Direkte tilkobling til rommet - + &Show Current Room &Vis nåværende rom - + F&ullscreen F&ullskjerm - + &Restart &Omstart - + Load/Remove &Amiibo... Last/Fjern &amiibo ... - + &Report Compatibility Meld inn kompati&bilitet - + Open &Mods Page Åpne Modifikasjonssiden (&M) - + Open &Quickstart Guide Åpne Hurtigstartsguiden (&Q) - + &FAQ &FAQ - + &Capture Screenshot &Ta skjermklipp - + &Album &Album - + &Set Nickname and Owner - + &Delete Game Data &Slett spilldata - + &Restore Amiibo &Gjenopprett amiibo - + &Format Amiibo - + &Mii Editor - + &Configure TAS... &Konfigurer TAS ... - + Configure C&urrent Game... Konfigurer nåværende spill ... - - + + &Start &Start - + &Reset &Omstart - - + + R&ecord T&a opp - + Open &Controller Menu Åpne &kontrollermeny - + Install Decryption &Keys - + &Home Menu &Hjem-meny - + &Desktop &Skrivebord - + &Application Menu &Appmeny - + &Root Data Folder &Rotdatamappe - + &NAND Folder &NAND-mappe - + &SDMC Folder &SDMC-mappe - + &Mod Folder &Mod-mappe - + &Log Folder &Loggmappe - + From Folder Fra mappe - + From ZIP Fra ZIP - + &Eden Dependencies &Eden-avhengigheter - + &Data Manager &Databehandler - + &Tree View &Trevisning - + &Grid View &Rutenettvisning - + Game Icon Size Spillikonstørrelse - - + + None Ingen - + Show Game &Name Vis spill&navn - + Show &Performance Overlay Vis &ytelsesoverlegg - + + &Carousel View + + + + Small (32x32) Liten (32x32) - + Standard (64x64) Standard (64x64) - + Large (128x128) Stor (128x128) - + Full Size (256x256) Full størrelse (256x256) - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping Kjører et spill - + Loading Web Applet... - - + + Disable Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. - + Unmute Slå på lyd - + Mute Demp - + Reset Volume Tilbakestill volum - + &Clear Recent Files &Tøm nylige filer - + &Continue &Fortsett - + Warning: Outdated Game Format - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. - - + + Error while loading ROM! - + The ROM format is not supported. - + An error occurred initializing the video core. - + Eden has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-mirror.github.io/help/reference/log-files/'>How to Upload the Log File</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. - + (64-bit) (64-bit) - + (32-bit) (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit - + Closing software... Lukker programvare ... - + Save Data - + Mod Data - + Error Opening %1 Folder - - + + Folder does not exist! Mappen finnes ikke! - + Remove Installed Game Contents? - + Remove Installed Game Update? - + Remove Installed Game DLC? - + Remove Entry - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? - + Remove Cache Storage? - + Remove File Fjern fil - + Remove Play Time Data - + Reset play time? - - + + RomFS Extraction Failed! - + There was an error copying the RomFS files or the user cancelled the operation. - + Full Full - + Skeleton Skjelett - + Select RomFS Dump Mode - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... Utvinner RomFS... - - + + Cancel Avbryt - + RomFS Extraction Succeeded! - + The operation completed successfully. - + Error Opening %1 - + Select Directory Velg mappe - + Properties Egenskaper - + The game properties could not be loaded. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. - + Load File - + Open Extracted ROM Directory - + Invalid Directory Selected - + The directory you have selected does not contain a 'main' file. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files - + %n file(s) remaining - + Installing file "%1"... Installerer filen «%1» ... - - + + Install Results - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application Systemapp - + System Archive Systemarkiv - + System Application Update - + Firmware Package (Type A) Fastvarepakke (Type A) - + Firmware Package (Type B) Fastvarepakke (Type B) - + Game Spill - + Game Update Spilloppdatering - + Game DLC Spill-DLC - + Delta Title - + Select NCA Install Type... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) - + Failed to Install - + The title type you selected for the NCA is invalid. - + File not found Filen ble ikke funnet - + File "%1" not found - + OK OK - + Function Disabled - + Compatibility list reporting is currently disabled. Check back later! - + Error opening URL Feil under åpning av URL - + Unable to open the URL "%1". - + TAS Recording TAS-opptak - + Overwrite file of player 1? - + Invalid config detected - + Handheld controller can't be used on docked mode. Pro controller will be selected. - - + + Amiibo amiibo - - + + The current amiibo has been removed - + Error Feil - - + + The current game is not looking for amiibos Det nåværende spillet ser ikke etter amiiboer - + Amiibo File (%1);; All Files (*.*) amiibo-fil (%1);; Alle filer (*.*) - + Load Amiibo Last inn amiibo - + Error loading Amiibo data - + The selected file is not a valid amiibo - + The selected file is already on use - + An unknown error occurred - - - Keys not installed - - - - - - Install decryption keys and restart Eden before attempting to install firmware. - - - - - Select Dumped Firmware Source Location - - - - - Select Dumped Firmware ZIP - - - - - Zipped Archives (*.zip) - Zippede arkiver (*.zip) - - - - Firmware cleanup failed - - - - - Failed to clean up extracted firmware cache. -Check write permissions in the system temp directory and try again. -OS reported error: %1 - - - - + No firmware available Ingen fastvare tilgjengelig - + Firmware Corrupted - + Unknown applet - + Applet doesn't map to a known value. - + Record not found - + Applet not found. Please reinstall firmware. - + Capture Screenshot Ta skjermklipp - + PNG Image (*.png) PNG-bilde (*.png) - + TAS state: Running %1/%2 - + TAS state: Recording %1 - + TAS state: Idle %1/%2 - + TAS State: Invalid - + &Stop Running - + Stop R&ecording - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor Skala: %1x - + Speed: %1% / %2% Hastighet: %1% / %2% - + Speed: %1% Hastighet: %1% - + Game: %1 FPS Spill: %1 FPS - + Frame: %1 ms Ramme: %1 ms - - - FSR - FSR - - - + NO AA INGEN AA - + VOLUME: MUTE VOLUM: DEMP - + VOLUME: %1% Volume percentage (e.g. 50%) VOLUM: %1% - + Derivation Components Missing - + Decryption keys are missing. Install them now? - + Wayland Detected! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -7951,74 +7945,74 @@ Would you like to force it for future launches? - + Use X11 Bruk X11 - + Continue with Wayland - + Don't show again Ikke vis igjen - + Restart Required - + Restart Eden to apply the X11 backend. - + Slow Treg - + Turbo Turbo - + Unlocked Ubegrenset - + Select RomFS Dump Target - + Please select which RomFS you would like to dump. - + Are you sure you want to close Eden? - - - + + + Eden Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? @@ -8084,6 +8078,11 @@ Would you like to bypass this and exit anyway? ScaleForce + + + FSR + FSR + Area @@ -8095,52 +8094,62 @@ Would you like to bypass this and exit anyway? MMPX - + + SGSR + + + + + SGSR EdgeDir + + + + Docked I dokking - + Handheld Håndholdt - + Fast Rask - + Balanced Balansert - + Accurate Nøyaktig - + Vulkan Vulkan - + OpenGL GLSL OpenGL GLSL - + OpenGL SPIRV OpenGL SPIRV - + OpenGL GLASM OpenGL GLASM - + Null Null @@ -8173,7 +8182,7 @@ If you wish to clean up the files which were left in the old data location, you - + Data was migrated successfully. @@ -9030,47 +9039,47 @@ p, li { white-space: pre-wrap; } %1 spiller %2 - + Play Time: %1 Tid spilt: %1 - + Never Played Aldri spilt - + Version: %1 Versjon: %1 - + Version: 1.0.0 Versjon: 1.0.0 - + Installed SD Titles Installerte SD-titler - + Installed NAND Titles Installerte NAND-titler - + System Titles Systemtitler - + Add New Game Directory Legg til ny spillmappe - + Favorites Favoritter @@ -9191,253 +9200,286 @@ p, li { white-space: pre-wrap; } QtCommon::Content - + Game Requires Firmware Spillet krever fastvare - + The game you are trying to launch requires firmware to boot or to get past the opening menu. Please <a href='https://yuzu-mirror.github.io/help/quickstart'>dump and install firmware</a>, or press "OK" to launch anyways. - + Installing Firmware... Installerer fastvare ... - - - - - + + + + + Cancel Avbryt - + Firmware Install Failed - + Firmware Install Succeeded - + Firmware integrity verification failed! - - + + Verification failed for the following files: %1 - - + + Verifying integrity... + + + + Integrity verification succeeded! + + - Integrity verification succeeded! - - - - - The operation completed successfully. - - + + Integrity verification failed! - + File contents may be corrupt or missing. - + Integrity verification couldn't be performed - + Firmware installation cancelled, firmware may be in a bad state or corrupted. File contents could not be checked for validity. - + Select Dumped Keys Location - + Decryption Keys install succeeded - + Decryption Keys install failed - + Orphaned Profiles Detected! - + UNEXPECTED BAD THINGS MAY HAPPEN IF YOU DON'T READ THIS!<br>Eden has detected the following save directories with no attached profile:<br>%1<br><br>The following profiles are valid:<br>%2<br><br>Click "OK" to open your save folder and fix up your profiles.<br>Hint: copy the contents of the largest or last-modified folder elsewhere, delete all orphaned profiles, and move your copied contents to the good profile.<br><br>Still confused? See the <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/Orphaned.md'>help page</a>.<br> - + Really clear data? - + Important data may be lost! - + Are you REALLY sure? Er du HELT sikker? - + Once deleted, your data will NOT come back! Only do this if you're 100% sure you want to delete this data. - + Clearing... - + Select Export Location - + %1.zip %1.zip - - + + + Zipped Archives (*.zip) Zippede arkiver (*.zip) - + Exporting data. This may take a while... - + Exporting Eksporterer - + Exported Successfully - + Data was exported successfully. - + Export Cancelled - + Export was cancelled by the user. - + Export Failed Eksportering mislyktes - + Ensure you have write permissions on the targeted directory and try again. - + Select Import Location - + Import Warning - + All previous data in this directory will be deleted. Are you sure you wish to proceed? - + Importing data. This may take a while... - + Importing Importerer - + Imported Successfully Importeringen var vellykket - + Data was imported successfully. - + Import Cancelled - + Import was cancelled by the user. - + Import Failed Importering mislyktes - + Ensure you have read permissions on the targeted directory and try again. + + + Keys not installed + + + + + Install decryption keys and restart Eden before attempting to install firmware. + + + + + Select Dumped Firmware Source Location + + + + + Select Dumped Firmware ZIP + + + + + Firmware cleanup failed + + + + + Failed to clean up extracted firmware cache. +Check write permissions in the system temp directory and try again. +OS reported error: %1 + + QtCommon::FS @@ -9681,72 +9723,72 @@ Would you like to manually select a portable folder to use? - + Create Shortcut Opprett snarvei - + Do you want to launch the game in fullscreen? - + Shortcut Created - + Successfully created a shortcut to %1 - + Shortcut may be Volatile! - + This will create a shortcut to the current AppImage. This may not work well if you update. Continue? - + Failed to Create Shortcut - + Failed to create a shortcut to %1 - + Create Icon Opprett ikon - + Cannot create icon file. Path "%1" does not exist and cannot be created. - + No firmware available Ingen fastvare tilgjengelig - + Please install firmware to use the home menu. - + Home Menu Applet - + Home Menu is not available. Please reinstall firmware. @@ -9754,55 +9796,55 @@ Would you like to manually select a portable folder to use? QtCommon::Mod - + Mod Name Mod-navn - + What should this mod be called? - + RomFS RomFS - + ExeFS/Patch ExeFS/Patch - + Cheat - + Mod Type Mod-type - + Could not detect mod type automatically. Please manually specify the type of mod you downloaded. Most mods are RomFS mods, but patches (.pchtxt) are typically ExeFS mods. - - + + Mod Extract Failed - + Failed to create temporary directory %1 - + Zip file %1 is empty «%1»-zip-filen er tom @@ -10496,65 +10538,65 @@ By selecting "From Eden", previous save data stored in Ryujinx will be - + New Version Location - + All Files (*.*) - - - + + + Failed to save file - + Could not open file %1 for writing. - + Downloading... - + Cancel - + Could not write to file %1. - + Could not commit to file %1. - + Failed to download file - + Could not download from %1%2 Error code: %3 - + Download Complete - + Successfully downloaded %1. Would you like to open it? diff --git a/dist/languages/nl.ts b/dist/languages/nl.ts index 81d70da92c..07eb0b1f06 100644 --- a/dist/languages/nl.ts +++ b/dist/languages/nl.ts @@ -713,7 +713,7 @@ Options lower than 1X can cause artifacts. - Determines how sharpened the image will look using FSR's dynamic contrast. + Determines how sharpened the image will look using FSR's or SGSR's dynamic contrast. @@ -764,35 +764,35 @@ Disabling it is only intended for debugging. - + Use asynchronous GPU emulation - + Uses an extra CPU thread for rendering. This option should always remain enabled. - + NVDEC emulation: NVDEC-emulatie: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. - + ASTC Decoding Method: ASTC Decodeer Methode: - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -801,55 +801,66 @@ stuttering but may present artifacts. - + ASTC Recompression Method: - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. - + Frame Pacing Mode (Vulkan only) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. - + VRAM Usage Mode: - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. - + Skip CPU Inner Invalidation - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. - + + Anti-Flicker + + + + + Forces GPU fence callbacks to wait for submitted GPU work. +Use with Fast GPU mode, to avoid flicker with lower performance impact. + + + + VSync Mode: VSync-modus: - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -857,1361 +868,1381 @@ Immediate (no synchronization) presents whatever is available and can exhibit te - + Sync Memory Operations - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. - + Enable asynchronous presentation (Vulkan only) Schakel asynchrone presentatie in (alleen Vulkan) - + Slightly improves performance by moving presentation to a separate CPU thread. - + Force maximum clocks (Vulkan only) Forceer maximale klokken (alleen Vulkan) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. Werkt op de achtergrond terwijl er wordt gewacht op grafische opdrachten om te voorkomen dat de GPU zijn kloksnelheid verlaagt. - + Anisotropic Filtering: Anisotrope Filtering: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. - + GPU Mode: - + Controls the GPU emulation mode. Most games render fine with Fast or Balanced modes, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. - + DMA Accuracy: - + Controls the DMA precision accuracy. Safe precision fixes issues in some games but may degrade performance. - + Enable asynchronous shader compilation - + May reduce shader stutter. - + Fast GPU Time - + Overclocks the emulated GPU to increase dynamic resolution and render distance. Use 256 for maximal performance and 512 for maximal graphics fidelity. - + GPU Unswizzle - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. - + GPU Unswizzle Max Texture Size - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. - + GPU Unswizzle Stream Size - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. - + GPU Unswizzle Chunk Size - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. - + Use Vulkan pipeline cache Gebruik Vulkan-pijplijn-cache - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. - + Enable Compute Pipelines (Intel Vulkan Only) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. - + Enable Reactive Flushing Schakel Reactive Flushing In - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. - + Sync to framerate of video playback - + Run the game at normal speed during video playback, even when the framerate is unlocked. - + Barrier feedback loops - + Improves rendering of transparency effects in specific games. - + Enable buffer history - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. - + Fix bloom effects - + Removes bloom in Burnout. - + Enable Legacy Rescale Pass - + May fix rescale issues in some games by relying on behavior from the previous implementation. Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. - + Extended Dynamic State - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. - + Vertex Input Dynamic State - + Enables vertex input dynamic state feature for better quality and performance. - + Sample Shading - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. - + RNG Seed RNG Seed - + Controls the seed of the random number generator. Mainly used for speedrunning. - + Device Name Apparaatnaam - + The name of the console. - + + Homebrew Args + + + + + Command-line arguments passed to homebrew at launch (e.g. -noglsl). + + + + Custom RTC Date: Aangepaste RTC Datum: - + This option allows to change the clock of the console. Can be used to manipulate time in games. - + The number of seconds from the current unix time - + Language: Taal: - + This option can be overridden when region setting is auto-select - + Region: Regio: - + The region of the console. - + Time Zone: Tijdzone: - + The time zone of the console. - + Sound Output Mode: Geluidsuitvoermodus: - + Console Mode: Console Modus: - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. - + Prompt for user profile on boot - + Useful if multiple people use the same PC. - + Pause when not in focus - + Pauses emulation when focusing on other windows. - + Confirm before stopping emulation Bevestig voordat u de emulatie stopt - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. - + Hide mouse on inactivity Verberg muis wanneer inactief - + Hides the mouse after 2.5s of inactivity. - + Disable controller applet Controller-applet uitschakelen - + Forcibly disables the use of the controller applet in emulated programs. When a program attempts to open the controller applet, it is immediately closed. - + Check for updates - + Whether or not to check for updates upon startup. - + Enable Gamemode - + Force X11 as Graphics Backend - + Custom frontend - + Real applet - + Never - + On Load - + Always - + CPU CPU - + GPU - + CPU Asynchronous - + Uncompressed (Best quality) - + BC1 (Low quality) BC1 (Lage Kwaliteit) - + BC3 (Medium quality) BC3 (Gemiddelde kwaliteit) - - + + Auto Auto - + 30 FPS - + 60 FPS - + 90 FPS - + 120 FPS - + Conservative - + Aggressive - + Vulkan Vulkan - + OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) - + Null Null - + Fast - + Balanced - - + + Accurate Accuraat - - + + Default Standaard - + Unsafe (fast) - + Safe (stable) - + Unsafe Onveilig - + Paranoid (disables most optimizations) Paranoid (schakelt de meeste optimalisaties uit) - + Debugging - + Dynarmic - + NCE - + Borderless Windowed Randloos Venster - + Exclusive Fullscreen Exclusief Volledig Scherm - + No Video Output Geen Video-uitvoer - + CPU Video Decoding CPU Videodecodering - + GPU Video Decoding (Default) GPU Videodecodering (Standaard) - + 0.25X (180p/270p) [EXPERIMENTAL] - + 0.5X (360p/540p) [EXPERIMENTAL] - + 0.75X (540p/810p) [EXPERIMENTAL] 0.75X (540p/810p) [EXPERIMENTEEL] - + 1X (720p/1080p) 1X (720p/1080p) - + 1.25X (900p/1350p) [EXPERIMENTAL] - + 1.5X (1080p/1620p) [EXPERIMENTAL] 1.5X (1080p/1620p) [EXPERIMENTEEL] - + 2X (1440p/2160p) 2X (1440p/2160p) - + 3X (2160p/3240p) 3X (2160p/3240p) - + 4X (2880p/4320p) 4X (2880p/4320p) - + 5X (3600p/5400p) 5X (3600p/5400p) - + 6X (4320p/6480p) 6X (4320p/6480p) - + 7X (5040p/7560p) 7X (5040p/7560p) - + 8X (5760p/8640p) 8X (5760p/8640p) - + Nearest Neighbor Nearest Neighbor - + Bilinear Bilinear - + Bicubic Bicubic - + Gaussian Gaussian - + Lanczos - + ScaleForce ScaleForce - + AMD FidelityFX Super Resolution - + Area - + MMPX - + Zero-Tangent - + B-Spline - + Mitchell - + Spline-1 - - + + Snapdragon Game Super Resolution + + + + + Snapdragon Game Super Resolution EdgeDir + + + + + None Geen - + FXAA FXAA - + SMAA SMAA - + Default (16:9) Standaart (16:9) - + Force 4:3 Forceer 4:3 - + Force 21:9 Forceer 21:9 - + Force 16:10 Forceer 16:10 - + Stretch to Window Uitrekken naar Venster - + Automatic Automatisch - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x - + 32x - + 64x - + Japanese (日本語) Japans (日本語) - + American English Amerikaans-Engels - + French (français) Frans (Français) - + German (Deutsch) Duits (Deutsch) - + Italian (italiano) Italiaans (italiano) - + Spanish (español) Spaans (Español) - + Chinese Chinees - + Korean (한국어) Koreaans (한국어) - + Dutch (Nederlands) Nederlands (Nederlands) - + Portuguese (português) Portugees (português) - + Russian (Русский) Russisch (Русский) - + Taiwanese Taiwanese - + British English Brits-Engels - + Canadian French Canadees-Frans - + Latin American Spanish Latijns-Amerikaans Spaans - + Simplified Chinese Vereenvoudigd Chinees - + Traditional Chinese (正體中文) Traditioneel Chinees (正體中文) - + Brazilian Portuguese (português do Brasil) Braziliaans-Portugees (português do Brasil) - + Polish (polska) - + Thai (แบบไทย) - - + + Japan Japan - + USA USA - + Europe Europa - + Australia Australië - + China China - + Korea Korea - + Taiwan Taiwan - + Auto (%1) Auto select time zone Auto (%1) - + Default (%1) Default time zone Standaard (%1) - + CET CET - + CST6CDT CST6CDT - + Cuba Cuba - + EET EET - + Egypt Egypte - + Eire Eire - + EST EST - + EST5EDT EST5EDT - + GB GB - + GB-Eire GB-Eire - + GMT GMT - + GMT+0 GMT+0 - + GMT-0 GMT-0 - + GMT0 GMT0 - + Greenwich Greenwich - + Hongkong Hongkong - + HST HST - + Iceland Ijsland - + Iran Iran - + Israel Israel - + Jamaica Jamaica - + Kwajalein Kwajalein - + Libya Libië - + MET MET - + MST MST - + MST7MDT MST7MDT - + Navajo Navajo - + NZ NZ - + NZ-CHAT NZ-CHAT - + Poland Polen - + Portugal Portugal - + PRC PRC - + PST8PDT PST8PDT - + ROC ROC - + ROK ROK - + Singapore Singapore - + Turkey Turkije - + UCT UCT - + Universal Universeel - + UTC UTC - + W-SU W-SU - + WET WET - + Zulu Zulu - + Mono Mono - + Stereo Stereo - + Surround Surround - + 4GB DRAM (Default) - + 6GB DRAM (Unsafe) - + 8GB DRAM - + 10GB DRAM (Unsafe) - + 12GB DRAM (Unsafe) - + Docked Docked - + Handheld Handheld - - + + Off - + Boost (1700MHz) - + Fast (2000MHz) - + Always ask (Default) - + Only if game specifies not to stop - + Never ask - - + + Medium (256) - - + + High (512) - + Very Small (16 MB) - + Small (32 MB) - + Normal (128 MB) - + Large (256 MB) - + Very Large (512 MB) - + Very Low (4 MB) - + Low (8 MB) - + Normal (16 MB) - + Medium (32 MB) - + High (64 MB) - + Very Low (32) - + Low (64) - + Normal (128) - + Disabled - + ExtendedDynamicState 1 - + ExtendedDynamicState 2 - + ExtendedDynamicState 3 - + Tree View - + Grid View @@ -3254,33 +3285,33 @@ Would you like to delete the old save data? Achtergrondkleur: - + % - FSR sharpening percentage (e.g. 50%) - % + FSR/SGSR sharpening percentage (e.g. 50%) + - + Off Uit - + VSync Off VSync Uit - + Recommended Aanbevolen - + On Aan - + VSync On VSync Aan @@ -4409,7 +4440,7 @@ Om de assen om te keren, beweeg je de joystick eerst verticaal en vervolgens hor - + Configure Configureer @@ -4440,7 +4471,7 @@ Om de assen om te keren, beweeg je de joystick eerst verticaal en vervolgens hor - + Test Test @@ -4455,77 +4486,77 @@ Om de assen om te keren, beweeg je de joystick eerst verticaal en vervolgens hor Verwijder Server - + %1:%2 %1:%2 - - - - - - + + + + + + Eden - + Port number has invalid characters Poortnummer bevat ongeldige tekens - + Port has to be in range 0 and 65353 Poort moet in bereik 0 en 65353 zijn - + IP address is not valid IP-adress is niet geldig - + This UDP server already exists Deze UDP-server bestaat al - + Unable to add more than 8 servers Kan niet meer dan 8 servers toevoegen - + Testing Testen - + Configuring Configureren - + Test Successful Test Succesvol - + Successfully received data from the server. De data van de server is succesvol ontvangen. - + Test Failed Test Gefaald - + Could not receive valid data from the server.<br>Please verify that the server is set up correctly and the address and port are correct. Kan niet de juiste data van de server ontvangen.<br>Controleer of de server correct is ingesteld en of het adres en de poort correct zijn. - + UDP Test or calibration configuration is in progress.<br>Please wait for them to finish. UDP-test of kalibratieconfiguratie is bezig.<br>Wacht tot ze klaar zijn. @@ -4710,57 +4741,57 @@ De huidige waarden zijn %1% en %2%. Sommige instellingen zijn alleen beschikbaar als een spel niet actief is. - + Add-Ons Add-Ons - + System Systeem - + CPU CPU - + Graphics Graphics - + Adv. Graphics Adv. Graphics - + Ext. Graphics - + Audio Audio - + Input Profiles Invoerprofielen - + Network - + Applets - + Properties Eigenschappen @@ -5771,7 +5802,7 @@ Versleep punten om de positie te veranderen, of dubbelklik op tabelcellen om waa - + Calculating... @@ -5973,50 +6004,50 @@ Please go to Configure -> System -> Network and make a selection. GRenderWindow - - + + OpenGL not available! OpenGL niet beschikbaar! - + OpenGL shared contexts are not supported. OpenGL gedeelde contexten worden niet ondersteund. - + Eden has not been compiled with OpenGL support. - - - + + + Error while initializing OpenGL! Fout tijdens het initialiseren van OpenGL! - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. Je GPU ondersteunt mogelijk geen OpenGL, of je hebt niet de laatste grafische stuurprogramma. - + Error while initializing OpenGL 4.6! Fout tijdens het initialiseren van OpenGL 4.6! - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 Je GPU ondersteunt mogelijk OpenGL 4.6 niet, of je hebt niet het laatste grafische stuurprogramma.<br><br>GL Renderer:<br>%1 - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 Je GPU ondersteunt mogelijk een of meer vereiste OpenGL-extensies niet. Zorg ervoor dat je het laatste grafische stuurprogramma hebt.<br><br>GL Renderer:<br>%1<br><br>Ondersteunde extensies:<br>%2 - + This build doesn't have OpenGL support. @@ -6024,279 +6055,282 @@ Please go to Configure -> System -> Network and make a selection. GameList - + &Add New Game Directory - + Favorite Favoriet - + Start Game Start Spel - + Start Game without Custom Configuration Start Spel zonder Aangepaste Configuratie - + Open Save Data Location Open Locatie van Save-data - + Open Mod Data Location Open Locatie van Mod-data - + Open Transferable Pipeline Cache Open Overdraagbare Pijplijn-cache - + Link to Ryujinx - + Remove Verwijder - + Remove Installed Update Verwijder Geïnstalleerde Update - + Remove All Installed DLC Verwijder Alle Geïnstalleerde DLC's - + Remove Custom Configuration Verwijder Aangepaste Configuraties - + Remove Cache Storage Verwijder Cache-opslag - + Remove OpenGL Pipeline Cache Verwijder OpenGL-pijplijn-cache - + Remove Vulkan Pipeline Cache Verwijder Vulkan-pijplijn-cache - + Remove All Pipeline Caches Verwijder Alle Pijplijn-caches - + Remove All Installed Contents Verwijder Alle Geïnstalleerde Inhoud - + Manage Play Time - + Edit Play Time Data - + Remove Play Time Data - - + + Dump RomFS Dump RomFS - + Dump RomFS to SDMC Dump RomFS naar SDMC - + Verify Integrity Verifieer Integriteit - + Copy Title ID to Clipboard Kopiëer Titel-ID naar Klembord - + Navigate to GameDB entry Navigeer naar GameDB-invoer - + Create Shortcut Maak Snelkoppeling - + Add to Desktop Toevoegen aan Bureaublad - + Add to Applications Menu Toevoegen aan menu Toepassingen - + Configure Game - + Scan Subfolders Scan Submappen - + Remove Game Directory Verwijder Spelmap - + ▲ Move Up ▲ Omhoog - + ▼ Move Down ▼ Omlaag - + Open Directory Location Open Maplocatie - + Clear Verwijder - - - Name - Naam - - - - Compatibility - Compatibiliteit - - - - Add-ons - Add-ons - - - - File type - Bestandssoort - - - - Size - Grootte - - - - Play time - - GameListItemCompat - + Ingame In het spel - + Game starts, but crashes or major glitches prevent it from being completed. Het spel start, maar crashes of grote glitches voorkomen dat het wordt voltooid. - + Perfect Perfect - + Game can be played without issues. Het spel kan zonder problemen gespeeld worden. - + Playable Speelbaar - + Game functions with minor graphical or audio glitches and is playable from start to finish. Het spel werkt met kleine grafische of audiofouten en is speelbaar van begin tot eind. - + Intro/Menu Intro/Menu - + Game loads, but is unable to progress past the Start Screen. Het spel wordt geladen, maar komt niet verder dan het startscherm. - + Won't Boot Start niet op - + The game crashes when attempting to startup. Het spel loopt vast bij het opstarten. - + Not Tested Niet Getest - + The game has not yet been tested. Het spel is nog niet getest. + + GameListModel + + + Name + + + + + Compatibility + + + + + Add-ons + + + + + File type + + + + + Size + + + + + Play time + + + GameListPlaceholder - + Double-click to add a new folder to the game list Dubbel-klik om een ​​nieuwe map toe te voegen aan de spellijst @@ -6304,17 +6338,17 @@ Please go to Configure -> System -> Network and make a selection. GameListSearchField - + %1 of %n result(s) %1 van %n resultaat(en)%1 van %n resultaat(en) - + Filter: Filter: - + Enter pattern to filter Voer patroon in om te filteren @@ -6809,1139 +6843,1099 @@ Debug Message: - + Game &Icon Size - + Reset Window Size to &720p Herstel Venstergrootte naar &720p - + Reset Window Size to 720p Herstel Venstergrootte naar 720p - + Reset Window Size to &900p Herstel Venstergrootte naar &900p - + Reset Window Size to 900p Herstel Venstergrootte naar 900p - + Reset Window Size to &1080p Herstel Venstergrootte naar &1080p - + Reset Window Size to 1080p Herstel Venstergrootte naar 1080p - + &Multiplayer &Multiplayer - + &Tools &Tools - + Am&iibo - + Launch &Applet - + &TAS &TAS - + &Create Home Menu Shortcut - + Install &Firmware - + &Help &Help - + &Install Files to NAND... &Installeer Bestanden naar NAND... - + L&oad File... L&aad Bestand... - + Load &Folder... Laad &Map... - + E&xit A&fsluiten - - + + &Pause &Onderbreken - + &Stop &Stop - + &Verify Installed Contents - + &About Eden - + Single &Window Mode Modus Enkel Venster - + Con&figure... Con&figureer... - + Ctrl+, - + Enable Overlay Display Applet - + Show &Filter Bar Toon &Filterbalk - + Show &Status Bar Toon &Statusbalk - + Show Status Bar Toon Statusbalk - + &Browse Public Game Lobby &Bladeren door Openbare Spellobby - + &Create Room &Maak Kamer - + &Leave Room &Verlaat Kamer - + &Direct Connect to Room &Directe Verbinding met Kamer - + &Show Current Room &Toon Huidige Kamer - + F&ullscreen Volledig Scherm - + &Restart &Herstart - + Load/Remove &Amiibo... Laad/Verwijder &Amiibo... - + &Report Compatibility &Rapporteer Compatibiliteit - + Open &Mods Page Open &Mod-pagina - + Open &Quickstart Guide Open &Snelstartgids - + &FAQ &FAQ - + &Capture Screenshot &Leg Schermafbeelding Vast - + &Album - + &Set Nickname and Owner - + &Delete Game Data - + &Restore Amiibo - + &Format Amiibo - + &Mii Editor - + &Configure TAS... &Configureer TAS... - + Configure C&urrent Game... Configureer Huidig Spel... - - + + &Start &Start - + &Reset &Herstel - - + + R&ecord Opnemen - + Open &Controller Menu - + Install Decryption &Keys - + &Home Menu - + &Desktop - + &Application Menu - + &Root Data Folder - + &NAND Folder - + &SDMC Folder - + &Mod Folder - + &Log Folder - + From Folder - + From ZIP - + &Eden Dependencies - + &Data Manager - + &Tree View - + &Grid View - + Game Icon Size - - + + None - + Show Game &Name - + Show &Performance Overlay - + + &Carousel View + + + + Small (32x32) - + Standard (64x64) - + Large (128x128) - + Full Size (256x256) - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping - + Loading Web Applet... - - + + Disable Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. - + Unmute - + Mute - + Reset Volume - + &Clear Recent Files - + &Continue - + Warning: Outdated Game Format - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. - - + + Error while loading ROM! - + The ROM format is not supported. - + An error occurred initializing the video core. - + Eden has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-mirror.github.io/help/reference/log-files/'>How to Upload the Log File</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. - + (64-bit) - + (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit - + Closing software... - + Save Data - + Mod Data - + Error Opening %1 Folder - - + + Folder does not exist! - + Remove Installed Game Contents? - + Remove Installed Game Update? - + Remove Installed Game DLC? - + Remove Entry - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? - + Remove Cache Storage? - + Remove File - + Remove Play Time Data - + Reset play time? - - + + RomFS Extraction Failed! - + There was an error copying the RomFS files or the user cancelled the operation. - + Full - + Skeleton - + Select RomFS Dump Mode - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... - - + + Cancel - + RomFS Extraction Succeeded! - + The operation completed successfully. - + Error Opening %1 - + Select Directory - + Properties - + The game properties could not be loaded. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. - + Load File - + Open Extracted ROM Directory - + Invalid Directory Selected - + The directory you have selected does not contain a 'main' file. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files - + %n file(s) remaining - + Installing file "%1"... - - + + Install Results - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application - + System Archive - + System Application Update - + Firmware Package (Type A) - + Firmware Package (Type B) - + Game - + Game Update - + Game DLC - + Delta Title - + Select NCA Install Type... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) - + Failed to Install - + The title type you selected for the NCA is invalid. - + File not found - + File "%1" not found - + OK - + Function Disabled - + Compatibility list reporting is currently disabled. Check back later! - + Error opening URL - + Unable to open the URL "%1". - + TAS Recording - + Overwrite file of player 1? - + Invalid config detected - + Handheld controller can't be used on docked mode. Pro controller will be selected. - - + + Amiibo - - + + The current amiibo has been removed - + Error - - + + The current game is not looking for amiibos - + Amiibo File (%1);; All Files (*.*) - + Load Amiibo - + Error loading Amiibo data - + The selected file is not a valid amiibo - + The selected file is already on use - + An unknown error occurred - - - Keys not installed - - - - - - Install decryption keys and restart Eden before attempting to install firmware. - - - - - Select Dumped Firmware Source Location - - - - - Select Dumped Firmware ZIP - - - - - Zipped Archives (*.zip) - - - - - Firmware cleanup failed - - - - - Failed to clean up extracted firmware cache. -Check write permissions in the system temp directory and try again. -OS reported error: %1 - - - - + No firmware available - + Firmware Corrupted - + Unknown applet - + Applet doesn't map to a known value. - + Record not found - + Applet not found. Please reinstall firmware. - + Capture Screenshot - + PNG Image (*.png) - + TAS state: Running %1/%2 - + TAS state: Recording %1 - + TAS state: Idle %1/%2 - + TAS State: Invalid - + &Stop Running - + Stop R&ecording - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor - + Speed: %1% / %2% - + Speed: %1% - + Game: %1 FPS - + Frame: %1 ms - - - FSR - - - - + NO AA - + VOLUME: MUTE - + VOLUME: %1% Volume percentage (e.g. 50%) - + Derivation Components Missing - + Decryption keys are missing. Install them now? - + Wayland Detected! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -7949,74 +7943,74 @@ Would you like to force it for future launches? - + Use X11 - + Continue with Wayland - + Don't show again - + Restart Required - + Restart Eden to apply the X11 backend. - + Slow - + Turbo - + Unlocked - + Select RomFS Dump Target - + Please select which RomFS you would like to dump. - + Are you sure you want to close Eden? - - - + + + Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? @@ -8082,6 +8076,11 @@ Would you like to bypass this and exit anyway? ScaleForce + + + FSR + + Area @@ -8093,52 +8092,62 @@ Would you like to bypass this and exit anyway? - + + SGSR + + + + + SGSR EdgeDir + + + + Docked - + Handheld - + Fast - + Balanced - + Accurate - + Vulkan - + OpenGL GLSL - + OpenGL SPIRV - + OpenGL GLASM - + Null @@ -8171,7 +8180,7 @@ If you wish to clean up the files which were left in the old data location, you - + Data was migrated successfully. @@ -9026,47 +9035,47 @@ p, li { white-space: pre-wrap; } %1 speelt %2 - + Play Time: %1 - + Never Played - + Version: %1 - + Version: 1.0.0 - + Installed SD Titles Geïnstalleerde SD-titels - + Installed NAND Titles Geïnstalleerde NAND-titels - + System Titles Systeemtitels - + Add New Game Directory Voeg Nieuwe Spelmap Toe - + Favorites Favorieten @@ -9187,253 +9196,286 @@ p, li { white-space: pre-wrap; } QtCommon::Content - + Game Requires Firmware - + The game you are trying to launch requires firmware to boot or to get past the opening menu. Please <a href='https://yuzu-mirror.github.io/help/quickstart'>dump and install firmware</a>, or press "OK" to launch anyways. - + Installing Firmware... - - - - - + + + + + Cancel - + Firmware Install Failed - + Firmware Install Succeeded - + Firmware integrity verification failed! - - + + Verification failed for the following files: %1 - - + + Verifying integrity... + + + + Integrity verification succeeded! + + - Integrity verification succeeded! - - - - - The operation completed successfully. - - + + Integrity verification failed! - + File contents may be corrupt or missing. - + Integrity verification couldn't be performed - + Firmware installation cancelled, firmware may be in a bad state or corrupted. File contents could not be checked for validity. - + Select Dumped Keys Location - + Decryption Keys install succeeded - + Decryption Keys install failed - + Orphaned Profiles Detected! - + UNEXPECTED BAD THINGS MAY HAPPEN IF YOU DON'T READ THIS!<br>Eden has detected the following save directories with no attached profile:<br>%1<br><br>The following profiles are valid:<br>%2<br><br>Click "OK" to open your save folder and fix up your profiles.<br>Hint: copy the contents of the largest or last-modified folder elsewhere, delete all orphaned profiles, and move your copied contents to the good profile.<br><br>Still confused? See the <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/Orphaned.md'>help page</a>.<br> - + Really clear data? - + Important data may be lost! - + Are you REALLY sure? - + Once deleted, your data will NOT come back! Only do this if you're 100% sure you want to delete this data. - + Clearing... - + Select Export Location - + %1.zip - - + + + Zipped Archives (*.zip) - + Exporting data. This may take a while... - + Exporting - + Exported Successfully - + Data was exported successfully. - + Export Cancelled - + Export was cancelled by the user. - + Export Failed - + Ensure you have write permissions on the targeted directory and try again. - + Select Import Location - + Import Warning - + All previous data in this directory will be deleted. Are you sure you wish to proceed? - + Importing data. This may take a while... - + Importing - + Imported Successfully - + Data was imported successfully. - + Import Cancelled - + Import was cancelled by the user. - + Import Failed - + Ensure you have read permissions on the targeted directory and try again. + + + Keys not installed + + + + + Install decryption keys and restart Eden before attempting to install firmware. + + + + + Select Dumped Firmware Source Location + + + + + Select Dumped Firmware ZIP + + + + + Firmware cleanup failed + + + + + Failed to clean up extracted firmware cache. +Check write permissions in the system temp directory and try again. +OS reported error: %1 + + QtCommon::FS @@ -9677,72 +9719,72 @@ Would you like to manually select a portable folder to use? - + Create Shortcut - + Do you want to launch the game in fullscreen? - + Shortcut Created - + Successfully created a shortcut to %1 - + Shortcut may be Volatile! - + This will create a shortcut to the current AppImage. This may not work well if you update. Continue? - + Failed to Create Shortcut - + Failed to create a shortcut to %1 - + Create Icon - + Cannot create icon file. Path "%1" does not exist and cannot be created. - + No firmware available - + Please install firmware to use the home menu. - + Home Menu Applet - + Home Menu is not available. Please reinstall firmware. @@ -9750,55 +9792,55 @@ Would you like to manually select a portable folder to use? QtCommon::Mod - + Mod Name - + What should this mod be called? - + RomFS - + ExeFS/Patch - + Cheat - + Mod Type - + Could not detect mod type automatically. Please manually specify the type of mod you downloaded. Most mods are RomFS mods, but patches (.pchtxt) are typically ExeFS mods. - - + + Mod Extract Failed - + Failed to create temporary directory %1 - + Zip file %1 is empty @@ -10492,65 +10534,65 @@ By selecting "From Eden", previous save data stored in Ryujinx will be - + New Version Location - + All Files (*.*) - - - + + + Failed to save file - + Could not open file %1 for writing. - + Downloading... - + Cancel - + Could not write to file %1. - + Could not commit to file %1. - + Failed to download file - + Could not download from %1%2 Error code: %3 - + Download Complete - + Successfully downloaded %1. Would you like to open it? diff --git a/dist/languages/pl.ts b/dist/languages/pl.ts index ef2078c554..08a7ca31ae 100644 --- a/dist/languages/pl.ts +++ b/dist/languages/pl.ts @@ -724,8 +724,8 @@ Opcje poniżej 1x mogą powodować artefakty. - Determines how sharpened the image will look using FSR's dynamic contrast. - Określa, jak bardzo obraz będzie wyostrzony przy użyciu dynamicznego kontrastu FSR. + Determines how sharpened the image will look using FSR's or SGSR's dynamic contrast. + @@ -782,23 +782,23 @@ Disabling it is only intended for debugging. Wyłączanie tej opcji jest przeznaczone do debugowania. - + Use asynchronous GPU emulation - + Uses an extra CPU thread for rendering. This option should always remain enabled. - + NVDEC emulation: Emulacja NVDEC: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. @@ -807,12 +807,12 @@ Dekodowanie może być wykonywane przez CPU albo GPU, albo może być całkowici W większości przypadków najlepszą wydajność zapewnia dekodowanie na GPU. - + ASTC Decoding Method: Metoda dekodowania ASTC: - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -824,12 +824,12 @@ GPU: użyj shaderów obliczeniowych GPU do dekodowania tekstur ASTC (zalecane). CPU asynchronicznie: użyj CPU do dekodowania na żądanie. Eliminuje zacięcia podczas dekodowania ASTC, ale może powodować artefakty. - + ASTC Recompression Method: Metoda rekompresji ASTC: - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. @@ -837,44 +837,55 @@ BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, BC1/BC3: format pośredni zostanie ponownie skompresowany do BC1 lub BC3 — oszczędza VRAM kosztem jakości obrazu. - + Frame Pacing Mode (Vulkan only) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. - + VRAM Usage Mode: Tryb wykorzystania VRAM: - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. Określa, czy emulator ma oszczędzać pamięć, czy maksymalnie wykorzystywać dostępną pamięć wideo dla wydajności. Tryb agresywny może pogorszyć działanie innych aplikacji, np. oprogramowania do nagrywania. - + Skip CPU Inner Invalidation Pomiń wewnętrzne unieważnienie procesora CPU - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. Pomija niektóre unieważnienia pamięci podręcznej po stronie CPU podczas aktualizacji pamięci, zmniejszając użycie CPU i poprawiając jego wydajność. Może powodować błędy lub awarie w niektórych grach. - + + Anti-Flicker + + + + + Forces GPU fence callbacks to wait for submitted GPU work. +Use with Fast GPU mode, to avoid flicker with lower performance impact. + + + + VSync Mode: Tryb synchronizacji pionowej: - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -885,12 +896,12 @@ Mailbox może mieć niższe opóźnienia niż FIFO i nie powoduje tearingu, ale Immediate (bez synchronizacji) wyświetla wszystko, co jest dostępne, i może powodować tearing. - + Sync Memory Operations Synchronizuj operacje pamięci - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. @@ -898,44 +909,44 @@ Unreal Engine 4 games often see the most significant changes thereof. Gry z Unreal Engine 4 mogą być najbardziej dotknięte. - + Enable asynchronous presentation (Vulkan only) Włącz asynchroniczną prezentację (tylko Vulkan) - + Slightly improves performance by moving presentation to a separate CPU thread. Nieznacznie poprawia wydajność, przenosząc prezentację na oddzielny wątek CPU. - + Force maximum clocks (Vulkan only) Wymuś maksymalne zegary (Tylko Vulkan) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. Uruchamia pracę w tle podczas oczekiwania na komendy graficzne aby GPU nie obniżało taktowania. - + Anisotropic Filtering: Filtrowanie anizotropowe: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. Steruje jakością renderowania tekstur pod ostrymi kątami. Na większości GPU bezpieczną wartością jest 16x. - + GPU Mode: Tryb GPU: - + Controls the GPU emulation mode. Most games render fine with Fast or Balanced modes, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. @@ -944,101 +955,101 @@ Większość gier renderuje się poprawnie w trybach Szybki lub Zrównoważony, Efekty cząsteczkowe zwykle renderują się poprawnie tylko w trybie Dokładnym. - + DMA Accuracy: Dokładność DMA: - + Controls the DMA precision accuracy. Safe precision fixes issues in some games but may degrade performance. Steruje precyzją DMA. Tryb „bezpieczna precyzja” usuwa problemy w niektórych grach, ale może pogorszyć wydajność. - + Enable asynchronous shader compilation Włącz asynchroniczną kompilację shaderów - + May reduce shader stutter. Może zmniejszyć zacięcia spowodowane kompilacją shaderów. - + Fast GPU Time Szybki czas GPU - + Overclocks the emulated GPU to increase dynamic resolution and render distance. Use 256 for maximal performance and 512 for maximal graphics fidelity. Podkręca emulowane GPU, aby zwiększyć dynamiczną rozdzielczość i zasięg renderowania. Ustaw 256 dla maksymalnej wydajności, a 512 dla maksymalnej jakości grafiki. - + GPU Unswizzle - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. - + GPU Unswizzle Max Texture Size - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. - + GPU Unswizzle Stream Size - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. - + GPU Unswizzle Chunk Size - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. - + Use Vulkan pipeline cache Użyj pamięci podręcznej strumienia dla Vulkana - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. Włącza specyficzną dla producenta GPU pamięć podręczną potoków. Ta opcja może znacząco skrócić czas ładowania shaderów w przypadkach, gdy sterownik Vulkan nie przechowuje wewnętrznie plików pamięci podręcznej potoków. - + Enable Compute Pipelines (Intel Vulkan Only) Włącz potoki obliczeniowe (tylko Intel Vulkan) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. @@ -1047,182 +1058,192 @@ To ustawienie istnieje wyłącznie dla sterowników Intela i może spowodować b Na wszystkich pozostałych sterownikach potoki obliczeniowe są zawsze włączone. - + Enable Reactive Flushing Włącz reaktywne opróżnianie buforów - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. Używa opróżniania reaktywnego zamiast predykcyjnego, co umożliwia dokładniejsze synchronizowanie pamięci. - + Sync to framerate of video playback Synchronizuj do liczby klatek odtwarzanego wideo - + Run the game at normal speed during video playback, even when the framerate is unlocked. Uruchamiaj grę z normalną prędkością podczas odtwarzania wideo, nawet przy odblokowanej liczbie klatek na sekundę. - + Barrier feedback loops Pętle sprzężenia zwrotnego barier - + Improves rendering of transparency effects in specific games. Poprawia renderowanie efektów przezroczystości w niektórych grach. - + Enable buffer history - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. - + Fix bloom effects - + Removes bloom in Burnout. - + Enable Legacy Rescale Pass - + May fix rescale issues in some games by relying on behavior from the previous implementation. Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. - + Extended Dynamic State Rozszerzony stan dynamiczny - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. Kontroluje liczbę funkcji, które mogą być używane w Extended Dynamic State. Wyższe poziomy pozwalają na użycie większej liczby funkcji i mogą zwiększyć wydajność, ale mogą powodować dodatkowe problemy z grafiką. - + Vertex Input Dynamic State Dynamiczny stan wejścia wierzchołków - + Enables vertex input dynamic state feature for better quality and performance. Włącza funkcję dynamicznego stanu wejścia wierzchołków, poprawiając jakość i wydajność. - + Sample Shading Cieniowanie próbkowe - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. Pozwala wykonywać shader fragmentów dla każdej próbki we fragmencie wielokrotnie próbkowanym zamiast raz dla każdego fragmentu. Poprawia jakość grafiki kosztem wydajności. Wyższe wartości poprawiają jakość, ale obniżają wydajność. - + RNG Seed Ziarno RNG - + Controls the seed of the random number generator. Mainly used for speedrunning. Ustala ziarno generatora liczb losowych. Głównie używane do speedrunów. - + Device Name Nazwa urządzenia - + The name of the console. The name of the console. - + + Homebrew Args + + + + + Command-line arguments passed to homebrew at launch (e.g. -noglsl). + + + + Custom RTC Date: Własna data RTC - + This option allows to change the clock of the console. Can be used to manipulate time in games. Ta opcja pozwala zmienić zegar konsoli. Może służyć do manipulowania czasem w grach. - + The number of seconds from the current unix time Liczba sekund od bieżącego czasu Unix - + Language: Język: - + This option can be overridden when region setting is auto-select Ta opcja może zostać nadpisana, gdy ustawienie regionu to automatyczny wybór. - + Region: Region: - + The region of the console. Region konsoli. - + Time Zone: Strefa czasowa: - + The time zone of the console. Strefa czasowa konsoli. - + Sound Output Mode: Tryb wyjścia dźwięku: - + Console Mode: Tryb konsoli - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. @@ -1231,1031 +1252,1041 @@ W zależności od tego ustawienia gry zmienią swoją rozdzielczość, szczegó Ustawienie na Handheld może poprawić wydajność na słabszych komputerach. - + Prompt for user profile on boot Pytaj o profil użytkownika przy uruchomieniu. - + Useful if multiple people use the same PC. Przydatne, gdy z tego samego PC korzysta wiele osób. - + Pause when not in focus Wstrzymuj, gdy okno nie jest aktywne - + Pauses emulation when focusing on other windows. Wstrzymuje emulację po przełączeniu na inne okna. - + Confirm before stopping emulation Potwierdzaj przed zatrzymaniem emulacji - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. Nadpisuje monity z prośbą o potwierdzenie zatrzymania emulacji. Włączenie powoduje pominięcie takich monitów i bezpośrednie wyjście z emulacji. - + Hide mouse on inactivity Ukryj mysz przy braku aktywności - + Hides the mouse after 2.5s of inactivity. Ukrywa kursor po 2,5 s bezczynności. - + Disable controller applet Wyłącz aplet kontrolera - + Forcibly disables the use of the controller applet in emulated programs. When a program attempts to open the controller applet, it is immediately closed. Wymusza wyłączenie użycia apletu kontrolera w oprogramowaniu uruchamianym w emulacji. Jeśli emulowane oprogramowanie próbuje otworzyć aplet kontrolera, jest on natychmiast zamykany. - + Check for updates Sprawdź aktualizacje - + Whether or not to check for updates upon startup. Czy sprawdzać aktualizacje przy uruchomieniu. - + Enable Gamemode Włącz Tryb gry - + Force X11 as Graphics Backend Wymuś X11 jako backend grafiki - + Custom frontend Niestandardowy frontend - + Real applet Prawdziwy aplet - + Never Nigdy - + On Load Przy wczytywaniu - + Always Zawsze - + CPU CPU - + GPU GPU - + CPU Asynchronous Asynchroniczne CPU - + Uncompressed (Best quality) Brak (najlepsza jakość) - + BC1 (Low quality) BC1 (niska jakość) - + BC3 (Medium quality) BC3 (średnia jakość) - - + + Auto Automatyczny - + 30 FPS - + 60 FPS - + 90 FPS - + 120 FPS - + Conservative Konserwatywny - + Aggressive Agresywny - + Vulkan Vulkan - + OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) - + Null Null - + Fast Szybkie - + Balanced Zrównoważony - - + + Accurate Dokładny - - + + Default Domyślny - + Unsafe (fast) Niezalecane (szybkie) - + Safe (stable) Bezpieczne (stabilne) - + Unsafe Niebezpieczny - + Paranoid (disables most optimizations) Paranoiczne (wyłącza większość optymalizacji) - + Debugging Debugowanie - + Dynarmic Dynamiczny - + NCE NCE - + Borderless Windowed W oknie (Bezramkowy) - + Exclusive Fullscreen Exclusive Fullscreen - + No Video Output Brak wyjścia wideo - + CPU Video Decoding Dekodowanie Wideo przez CPU - + GPU Video Decoding (Default) Dekodowanie Wideo przez GPU (Domyślne) - + 0.25X (180p/270p) [EXPERIMENTAL] 0,25x (180p/270p) [EKSPERYMENTALNE] - + 0.5X (360p/540p) [EXPERIMENTAL] 0,5x (360p/540p) [EKSPERYMENTALNE] - + 0.75X (540p/810p) [EXPERIMENTAL] 0.75X (540p/810p) [EKSPERYMENTALNE] - + 1X (720p/1080p) 1X (720p/1080p) - + 1.25X (900p/1350p) [EXPERIMENTAL] 1.25X (900p/1350p) [EKSPERYMENTALNE] - + 1.5X (1080p/1620p) [EXPERIMENTAL] 1.5X (1080p/1620p) [Ekperymentalnie] - + 2X (1440p/2160p) 2X (1440p/2160p) - + 3X (2160p/3240p) 3X (2160p/3240p) - + 4X (2880p/4320p) 4X (2880p/4320p) - + 5X (3600p/5400p) 5X (3600p/5400p) - + 6X (4320p/6480p) 6X (4320p/6480p) - + 7X (5040p/7560p) 7X (5040p/7560p) - + 8X (5760p/8640p) 8X (5760p/8640p) - + Nearest Neighbor Najbliższy sąsiadujący - + Bilinear Bilinearny - + Bicubic Bikubiczny - + Gaussian Kulisty - + Lanczos Lanczos - + ScaleForce ScaleForce - + AMD FidelityFX Super Resolution AMD FidelityFX Super Resolution - + Area Obszar - + MMPX MMPX - + Zero-Tangent Zero-Tangent - + B-Spline B-Spline - + Mitchell Mitchell - + Spline-1 Spline-1 - - + + Snapdragon Game Super Resolution + + + + + Snapdragon Game Super Resolution EdgeDir + + + + + None Żadna (wyłączony) - + FXAA FXAA - + SMAA SMAA - + Default (16:9) Domyślne (16:9) - + Force 4:3 Wymuś 4:3 - + Force 21:9 Wymuś 21:9 - + Force 16:10 Wymuś 16:10 - + Stretch to Window Rozciągnij do Okna - + Automatic Automatyczne - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x - + 32x 32x - + 64x 64x - + Japanese (日本語) Japoński (日本語) - + American English Angielski Amerykański - + French (français) Francuski (français) - + German (Deutsch) Niemiecki (Niemcy) - + Italian (italiano) Włoski (italiano) - + Spanish (español) Hiszpański (español) - + Chinese Chiński - + Korean (한국어) Koreański (한국어) - + Dutch (Nederlands) Duński (Holandia) - + Portuguese (português) Portugalski (português) - + Russian (Русский) Rosyjski (Русский) - + Taiwanese Tajwański - + British English Angielski Brytyjski - + Canadian French Fancuski (Kanada) - + Latin American Spanish Hiszpański (Latin American) - + Simplified Chinese Chiński (Uproszczony) - + Traditional Chinese (正體中文) Chiński tradycyjny (正體中文) - + Brazilian Portuguese (português do Brasil) Portugalski (português do Brasil) - + Polish (polska) - + Thai (แบบไทย) - - + + Japan Japonia - + USA USA - + Europe Europa - + Australia Australia - + China Chiny - + Korea Korea - + Taiwan Tajwan - + Auto (%1) Auto select time zone Auto (%1) - + Default (%1) Default time zone Domyślne (%1) - + CET CET - + CST6CDT CST6CDT - + Cuba Cuba - + EET EET - + Egypt Egipt - + Eire Irlandia - + EST EST - + EST5EDT EST5EDT - + GB GB - + GB-Eire GB-Eire - + GMT GMT - + GMT+0 GMT+0 - + GMT-0 GMT-0 - + GMT0 GMT0 - + Greenwich Greenwich - + Hongkong Hongkong - + HST HST - + Iceland Islandia - + Iran Iran - + Israel Izrael - + Jamaica Jamajka - + Kwajalein Kwajalein - + Libya Libia - + MET MET - + MST MST - + MST7MDT MST7MDT - + Navajo Navajo - + NZ NZ - + NZ-CHAT NZ-CHAT - + Poland Polska - + Portugal Portugalia - + PRC PRC - + PST8PDT PST8PDT - + ROC ROC - + ROK ROK - + Singapore Singapur - + Turkey Turcja - + UCT UCT - + Universal Uniwersalny - + UTC UTC - + W-SU W-SU - + WET WET - + Zulu Zulu - + Mono Mono - + Stereo Stereo - + Surround Surround - + 4GB DRAM (Default) 4GB DRAM (Domyślne) - + 6GB DRAM (Unsafe) 6GB DRAM (NIebezpieczne) - + 8GB DRAM 8GB DRAM - + 10GB DRAM (Unsafe) 10GB DRAM (NIebezpieczne) - + 12GB DRAM (Unsafe) 12GB DRAM (NIebezpieczne) - + Docked Zadokowany - + Handheld Przenośnie - - + + Off Wyłączone - + Boost (1700MHz) Boost (1700MHz) - + Fast (2000MHz) Szybki (2000MHz) - + Always ask (Default) Zawsze pytaj (Domyślne) - + Only if game specifies not to stop Tylko jeśli gra określa, aby nie zatrzymywać - + Never ask Nie pytaj więcej - - + + Medium (256) Średnie (256) - - + + High (512) Wysokie (512) - + Very Small (16 MB) - + Small (32 MB) - + Normal (128 MB) - + Large (256 MB) - + Very Large (512 MB) - + Very Low (4 MB) - + Low (8 MB) - + Normal (16 MB) - + Medium (32 MB) - + High (64 MB) - + Very Low (32) - + Low (64) - + Normal (128) - + Disabled Wyłączone - + ExtendedDynamicState 1 ExtendedDynamicState 1 - + ExtendedDynamicState 2 ExtendedDynamicState 2 - + ExtendedDynamicState 3 ExtendedDynamicState 3 - + Tree View - + Grid View @@ -3320,33 +3351,33 @@ Czy chcesz usunąć stare dane zapisu? Kolor tła - + % - FSR sharpening percentage (e.g. 50%) - % + FSR/SGSR sharpening percentage (e.g. 50%) + - + Off Wyłączone - + VSync Off VSync wyłączony - + Recommended Zalecane - + On Włączone - + VSync On VSync aktywny @@ -4475,7 +4506,7 @@ Aby odwrócić osie, najpierw przesuń joystick pionowo, a następnie poziomo. - + Configure Konfiguruj @@ -4506,7 +4537,7 @@ Aby odwrócić osie, najpierw przesuń joystick pionowo, a następnie poziomo. - + Test Test @@ -4521,77 +4552,77 @@ Aby odwrócić osie, najpierw przesuń joystick pionowo, a następnie poziomo.Usuń serwer - + %1:%2 %1:%2 - - - - - - + + + + + + Eden Eden - + Port number has invalid characters Port zawiera nieprawidłowe znaki - + Port has to be in range 0 and 65353 Port musi być w zakresie 0-65353 - + IP address is not valid Adres IP nie jest prawidłowy - + This UDP server already exists Ten serwer UDP już istnieje - + Unable to add more than 8 servers Nie można dodać więcej niż 8 serwerów - + Testing Testowanie - + Configuring Konfigurowanie - + Test Successful Test Udany - + Successfully received data from the server. Pomyślnie odebrano dane z serwera. - + Test Failed Test nieudany - + Could not receive valid data from the server.<br>Please verify that the server is set up correctly and the address and port are correct. Nie można odebrać poprawnych danych z serwera.<br>Sprawdź, czy serwer jest poprawnie skonfigurowany, a adres i port są prawidłowe. - + UDP Test or calibration configuration is in progress.<br>Please wait for them to finish. Trwa konfiguracja testu UDP lub kalibracji.<br>Poczekaj na zakończenie. @@ -4776,57 +4807,57 @@ Obecne wartości to odpowiednio %1% i %2%. Niektóre ustawienia są dostępne tylko, gdy gra nie jest uruchomiona. - + Add-Ons Dodatki - + System System - + CPU CPU - + Graphics Grafika - + Adv. Graphics Zaaw. Grafika - + Ext. Graphics Dodatkowa grafika - + Audio Dźwięk - + Input Profiles Profil wejściowy - + Network Sieć - + Applets - + Properties Właściwości @@ -5837,7 +5868,7 @@ Przeciągnij punkty, aby zmienić pozycję, lub kliknij dwukrotnie komórki tabe Zaimportuj dane dla tego katalogu. To może chwilę potrwać i USUNIE WSZYSTKIE ISTNIEJĄCE DANE! - + Calculating... Obliczanie... @@ -6041,50 +6072,50 @@ Przejdź do sekcji Konfiguracja -> System -> Sieć i dokonaj wyboru. GRenderWindow - - + + OpenGL not available! OpenGL niedostępny! - + OpenGL shared contexts are not supported. Współdzielone konteksty OpenGL nie są obsługiwane. - + Eden has not been compiled with OpenGL support. Eden nie został skompilowany z obsługą OpenGL. - - - + + + Error while initializing OpenGL! Błąd podczas inicjowania OpenGL! - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. Twoja karta graficzna może nie obsługiwać OpenGL lub nie masz najnowszych sterowników karty graficznej. - + Error while initializing OpenGL 4.6! Błąd podczas inicjowania OpenGL 4.6! - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 Twoja karta graficzna może nie obsługiwać OpenGL 4.6 lub nie masz najnowszych sterowników karty graficznej.<br><br>GL Renderer:<br>%1 - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 Twoja karta graficzna może nie obsługiwać co najmniej jednego wymaganego rozszerzenia OpenGL. Upewnij się, że masz najnowsze sterowniki karty graficznej<br><br>GL Renderer:<br>%1<br><br>Nieobsługiwane rozszerzenia:<br>%2 - + This build doesn't have OpenGL support. @@ -6092,279 +6123,282 @@ Przejdź do sekcji Konfiguracja -> System -> Sieć i dokonaj wyboru. GameList - + &Add New Game Directory - + Favorite Ulubione - + Start Game Uruchom grę - + Start Game without Custom Configuration Uruchom grę bez niestandardowej konfiguracji - + Open Save Data Location Otwórz lokalizację zapisów - + Open Mod Data Location Otwórz lokalizację modyfikacji - + Open Transferable Pipeline Cache Otwórz Transferowalną Pamięć Podręczną Pipeline - + Link to Ryujinx Połącz z Ryujinx - + Remove Usuń - + Remove Installed Update Usuń zainstalowaną łatkę - + Remove All Installed DLC Usuń wszystkie zainstalowane DLC - + Remove Custom Configuration Usuń niestandardową konfigurację - + Remove Cache Storage Usuń pamięć podręczną - + Remove OpenGL Pipeline Cache Usuń Pamięć Podręczną Pipeline OpenGL - + Remove Vulkan Pipeline Cache Usuń Pamięć Podręczną Pipeline Vulkan - + Remove All Pipeline Caches Usuń całą pamięć podręczną Pipeline - + Remove All Installed Contents Usuń całą zainstalowaną zawartość - + Manage Play Time Zarządzaj czasem gry - + Edit Play Time Data Edytuj dane czasu gry - + Remove Play Time Data Usuń dane dotyczące czasu gry - - + + Dump RomFS Zrzuć RomFS - + Dump RomFS to SDMC Zrzuć RomFS do SDMC - + Verify Integrity Sprawdź integralność - + Copy Title ID to Clipboard Kopiuj identyfikator gry do schowka - + Navigate to GameDB entry Nawiguj do wpisu kompatybilności gry - + Create Shortcut Utwórz skrót - + Add to Desktop Dodaj do pulpitu - + Add to Applications Menu Dodaj do menu aplikacji - + Configure Game Skonfiguruj grę - + Scan Subfolders Skanuj podfoldery - + Remove Game Directory Usuń katalog gier - + ▲ Move Up ▲ Przenieś w górę - + ▼ Move Down ▼ Przenieś w dół - + Open Directory Location Otwórz lokalizacje katalogu - + Clear Wyczyść - - - Name - Nazwa gry - - - - Compatibility - Kompatybilność - - - - Add-ons - Dodatki - - - - File type - Typ pliku - - - - Size - Rozmiar - - - - Play time - Czas gry - GameListItemCompat - + Ingame W grze - + Game starts, but crashes or major glitches prevent it from being completed. Gra uruchamia się, ale awarie lub poważne błędy uniemożliwiają jej ukończenie. - + Perfect Perfekcyjnie - + Game can be played without issues. Można grać bez problemów. - + Playable Grywalna - + Game functions with minor graphical or audio glitches and is playable from start to finish. Gra działa z drobnymi błędami graficznymi lub dźwiękowymi oraz jest grywalna od początku aż do końca. - + Intro/Menu Intro/Menu - + Game loads, but is unable to progress past the Start Screen. Gra się ładuje, ale nie może przejść przez ekran początkowy. - + Won't Boot Nie uruchamia się - + The game crashes when attempting to startup. Ta gra się zawiesza przy próbie startu. - + Not Tested Nie testowane - + The game has not yet been tested. Ta gra nie została jeszcze przetestowana. + + GameListModel + + + Name + + + + + Compatibility + + + + + Add-ons + + + + + File type + + + + + Size + + + + + Play time + + + GameListPlaceholder - + Double-click to add a new folder to the game list Kliknij podwójnie aby dodać folder do listy gier @@ -6372,17 +6406,17 @@ Przejdź do sekcji Konfiguracja -> System -> Sieć i dokonaj wyboru. GameListSearchField - + %1 of %n result(s) %1 z %n wyniku%1 z %n wyników%1 z %n wyników%1 z %n wyników - + Filter: Filter: - + Enter pattern to filter Wpisz typ do filtra @@ -6878,773 +6912,778 @@ Komunikat debugowania: - + Game &Icon Size - + Reset Window Size to &720p Zresetuj rozmiar okna do &720p - + Reset Window Size to 720p Zresetuj rozmiar okna do 720p - + Reset Window Size to &900p Zresetuj Rozmiar okna do &900p - + Reset Window Size to 900p Zresetuj Rozmiar okna do 900p - + Reset Window Size to &1080p Zresetuj rozmiar okna do &1080p - + Reset Window Size to 1080p Zresetuj rozmiar okna do 1080p - + &Multiplayer &Multiplayer - + &Tools &Narzędzia - + Am&iibo Am&iibo - + Launch &Applet - + &TAS &TAS - + &Create Home Menu Shortcut &Utwórz skrót w Menu głównym - + Install &Firmware Zainstaluj &Firmware - + &Help &Pomoc - + &Install Files to NAND... &Zainstaluj pliki na NAND... - + L&oad File... &Załaduj Plik... - + Load &Folder... Załaduj &Folder... - + E&xit &Wyjście - - + + &Pause &Pauza - + &Stop &Stop - + &Verify Installed Contents &Zweryfikuj zainstalowane zasoby - + &About Eden &O Eden - + Single &Window Mode Tryb &Pojedyńczego Okna - + Con&figure... &Konfiguruj... - + Ctrl+, Ctrl+, - + Enable Overlay Display Applet Włącz aplet wyświetlania nakładki - + Show &Filter Bar Pokaż Pasek &Filtrów - + Show &Status Bar Pokaż Pasek &Statusu - + Show Status Bar Pokaż pasek statusu - + &Browse Public Game Lobby &Przeglądaj publiczne lobby gier - + &Create Room &Utwórz Pokój - + &Leave Room &Wyjdź z Pokoju - + &Direct Connect to Room &Bezpośrednie połączenie z pokojem - + &Show Current Room &Pokaż bieżący pokój - + F&ullscreen &Pełny ekran - + &Restart &Restart - + Load/Remove &Amiibo... Załaduj/Usuń &Amiibo... - + &Report Compatibility &Zaraportuj kompatybilność - + Open &Mods Page Otwórz stronę z &Modami - + Open &Quickstart Guide Otwórz &Poradnik szybkiego startu - + &FAQ &FAQ - + &Capture Screenshot &Zrób zdjęcie - + &Album - + &Set Nickname and Owner &Ustaw nick oraz właściciela - + &Delete Game Data &Usuń dane gry - + &Restore Amiibo &Przywróć Amiibo - + &Format Amiibo &Sformatuj Amiibo - + &Mii Editor - + &Configure TAS... &Skonfiguruj TAS - + Configure C&urrent Game... Skonfiguruj &obecną grę... - - + + &Start &Start - + &Reset &Zresetuj - - + + R&ecord &Nagraj - + Open &Controller Menu Otwórz Menu &kontrolera - + Install Decryption &Keys Zainstaluj &klucze deszyfrujące - + &Home Menu - + &Desktop &Pulpit - + &Application Menu Menu &Aplikacji - + &Root Data Folder Folder &Root (danych głównych) - + &NAND Folder Folder &NAND - + &SDMC Folder Folder &SDMC - + &Mod Folder Folder &modów - + &Log Folder Folder &logów - + From Folder Z Folderu - + From ZIP Z pliku ZIP - + &Eden Dependencies Zależności &Eden - + &Data Manager Manager &Danych - + &Tree View - + &Grid View - + Game Icon Size - - + + None Żadna (wyłączony) - + Show Game &Name - + Show &Performance Overlay - + + &Carousel View + + + + Small (32x32) - + Standard (64x64) - + Large (128x128) - + Full Size (256x256) - + Broken Vulkan Installation Detected Wykryto uszkodzoną instalację Vulkan - + Vulkan initialization failed during boot. Inicjalizacja Vulkana nie powiodła się podczas uruchamiania. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping Gra uruchomiona - + Loading Web Applet... Ładowanie apletu internetowego... - - + + Disable Web Applet Wyłącz Aplet sieciowy - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) Wyłączenie apletu sieciowego może prowadzić do nieprzewidywalnego działania i powinno być używane wyłącznie z Super Mario 3D All-Stars. Na pewno chcesz wyłączyć aplet sieciowy? (Tę opcję można ponownie włączyć w ustawieniach debugowania). - + The amount of shaders currently being built Liczba shaderów aktualnie budowanych - + The current selected resolution scaling multiplier. Aktualnie wybrany mnożnik skalowania rozdzielczości. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. Bieżąca prędkość emulacji. Wartości wyższe lub niższe niż 100% oznaczają, że emulacja działa odpowiednio szybciej lub wolniej niż na Switchu. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Liczba klatek na sekundę aktualnie wyświetlanych przez grę. Wartość ta zależy od gry oraz od konkretnej sceny. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Czas potrzebny na emulację jednej klatki Switcha, bez uwzględniania ogranicznika klatek i synchronizacji pionowej. Dla emulacji z pełną prędkością powinno to być maksymalnie 16,67 ms. - + Unmute Wyłącz wyciszenie - + Mute Wycisz - + Reset Volume Zresetuj głośność - + &Clear Recent Files &Usuń Ostatnie pliki - + &Continue &Kontynuuj - + Warning: Outdated Game Format Ostrzeżenie: Nieaktualny format gry - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. Używasz dla tej gry formatu katalogu zdekonstruowanego ROM-u, który jest przestarzały i został zastąpiony innymi formatami, takimi jak NCA, NAX, XCI czy NSP. Katalogi takich ROM-ów nie zawierają ikon, metadanych ani nie obsługują aktualizacji.<br>Aby uzyskać wyjaśnienie dotyczące różnych formatów Switcha obsługiwanych przez Eden, zajrzyj do naszego podręcznika użytkownika. Ten komunikat nie zostanie wyświetlony ponownie. - - + + Error while loading ROM! Błąd podczas wczytywania ROMu! - + The ROM format is not supported. Ten format ROMu nie jest wspierany. - + An error occurred initializing the video core. Wystąpił błąd podczas inicjowania rdzenia wideo. - + Eden has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-mirror.github.io/help/reference/log-files/'>How to Upload the Log File</a>. Podczas uruchamiania rdzenia wideo wystąpił błąd. Zazwyczaj jest to spowodowane nieaktualnymi sterownikami GPU, w tym sterownikami zintegrowanymi. Więcej szczegółów można znaleźć w logu. Więcej informacji na temat dostępu do logu można znaleźć na następującej stronie: <a href='https://yuzu-mirror.github.io/help/reference/log-files/'>Jak przesłać Log</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. Błąd podczas wczytywania ROMu! %1 - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. %1<br>Proszę ponownie zrzucić pliki lub poprosić o pomoc na Discordzie/Stoat. - + An unknown error occurred. Please see the log for more details. Wystąpił nieznany błąd. Więcej informacji można znaleźć w pliku log. - + (64-bit) (64-bit) - + (32-bit) (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Closing software... Zamykanie aplikacji... - + Save Data Zapis danych - + Mod Data Dane modów - + Error Opening %1 Folder Błąd podczas otwierania folderu %1 - - + + Folder does not exist! Folder nie istnieje! - + Remove Installed Game Contents? Usunąć zainstalowaną zawartość gry? - + Remove Installed Game Update? Usunąć zainstalowaną aktualizację gry? - + Remove Installed Game DLC? Usunąć zainstalowane DLC gry? - + Remove Entry Usuń wpis - + Delete OpenGL Transferable Shader Cache? Usunąć przenośną pamięć podręczną shaderów OpenGL? - + Delete Vulkan Transferable Shader Cache? Usunąć przenośną pamięć podręczną shaderów Vulkan? - + Delete All Transferable Shader Caches? Usunąć wszystkie przenośne pamięci podręczne shaderów? - + Remove Custom Game Configuration? Usunąć niestandardową konfigurację gry? - + Remove Cache Storage? Usunąć pamięć podręczną? - + Remove File Usuń plik - + Remove Play Time Data Usuń dane czasu gry - + Reset play time? Zresetować czas gry? - - + + RomFS Extraction Failed! Wyodrębnianie RomFS nie powiodło się! - + There was an error copying the RomFS files or the user cancelled the operation. Wystąpił błąd podczas kopiowania plików RomFS lub użytkownik przerwał operację. - + Full Pełny - + Skeleton Szkielet - + Select RomFS Dump Mode Wybierz tryb zrzutu RomFS - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. Wybierz sposób wykonania zrzutu RomFS. <br>Tryb „Pełny” skopiuje wszystkie pliki do nowego katalogu, <br>natomiast „Szkielet” utworzy jedynie strukturę katalogów. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root Nie ma wystarczająco miejsca w %1 aby wyodrębnić RomFS. Zwolnij trochę miejsca, albo zmień ścieżkę zrzutu RomFs w Emulacja > Konfiguruj > System > System Plików > Źródło Zrzutu - + Extracting RomFS... Wypakowywanie RomFS... - - + + Cancel Anuluj - + RomFS Extraction Succeeded! Wypakowanie RomFS zakończone pomyślnie! - + The operation completed successfully. Operacja zakończona pomyślnie. - + Error Opening %1 Błąd podczas otwierania %1 - + Select Directory Wybierz folder - + Properties Właściwości - + The game properties could not be loaded. Właściwości tej gry nie mogły zostać załadowane. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Plik wykonywalny Switcha (%1);;Wszystkie pliki (*.*) - + Load File Załaduj plik - + Open Extracted ROM Directory Otwórz folder wypakowanego ROMu - + Invalid Directory Selected Wybrano niewłaściwy folder - + The directory you have selected does not contain a 'main' file. Wybrany folder nie zawiera 'głównego' pliku. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) Instalacyjne pliki Switch'a (*.nca *.nsp *.xci);;Archiwum zawartości Nintendo (*.nca);;Pakiet poddany Nintendo (*.nsp);;Obraz z kartridża NX (*.xci) - + Install Files Zainstaluj pliki - + %n file(s) remaining Pozostał %n plikPozostały %n plikiPozostało %n plikówPozostało %n plików - + Installing file "%1"... Instalowanie pliku "%1"... - - + + Install Results Wynik instalacji - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. Aby uniknąć ewentualnych konfliktów, odradzamy użytkownikom instalowanie gier na NAND. Proszę, używaj tej funkcji tylko do instalowania łatek i DLC. - + %n file(s) were newly installed %n plik został nowo zainstalowany @@ -7654,7 +7693,7 @@ Proszę, używaj tej funkcji tylko do instalowania łatek i DLC. - + %n file(s) were overwritten %n plik został nadpisany @@ -7664,7 +7703,7 @@ Proszę, używaj tej funkcji tylko do instalowania łatek i DLC. - + %n file(s) failed to install Nie udało się zainstalować %n pliku @@ -7674,361 +7713,314 @@ Proszę, używaj tej funkcji tylko do instalowania łatek i DLC. - + System Application Aplikacja systemowa - + System Archive Archiwum systemu - + System Application Update Aktualizacja aplikacji systemowej - + Firmware Package (Type A) Paczka systemowa (Typ A) - + Firmware Package (Type B) Paczka systemowa (Typ B) - + Game Gra - + Game Update Aktualizacja gry - + Game DLC Dodatek do gry - + Delta Title Tytuł Delta - + Select NCA Install Type... Wybierz typ instalacji NCA... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) Wybierz typ tytułu, do którego chcesz zainstalować ten NCA, jako: (W większości przypadków domyślna "gra" jest w porządku.) - + Failed to Install Instalacja nieudana - + The title type you selected for the NCA is invalid. Typ tytułu wybrany dla NCA jest nieprawidłowy. - + File not found Nie znaleziono pliku - + File "%1" not found Nie znaleziono pliku "%1" - + OK OK - + Function Disabled Funkcja wyłączona - + Compatibility list reporting is currently disabled. Check back later! Zgłaszanie do listy kompatybilności jest obecnie wyłączone. Spróbuj ponownie później! - + Error opening URL Błąd otwierania adresu URL - + Unable to open the URL "%1". Nie można otworzyć adresu URL "%1". - + TAS Recording Nagrywanie TAS - + Overwrite file of player 1? Nadpisać plik gracza 1? - + Invalid config detected Wykryto nieprawidłową konfigurację - + Handheld controller can't be used on docked mode. Pro controller will be selected. Nie można używać kontrolera handheld w trybie zadokowanym. Zostanie wybrany kontroler Pro. - - + + Amiibo Amiibo - - + + The current amiibo has been removed Amiibo zostało "zdjęte" - + Error Błąd - - + + The current game is not looking for amiibos Ta gra nie szuka amiibo - + Amiibo File (%1);; All Files (*.*) Plik Amiibo (%1);;Wszyskie pliki (*.*) - + Load Amiibo Zamontuj Amiibo - + Error loading Amiibo data Błąd podczas ładowania danych Amiibo - + The selected file is not a valid amiibo Wybrany plik nie jest poprawnym amiibo - + The selected file is already on use Wybrany plik jest już w użyciu - + An unknown error occurred Wystąpił nieznany błąd - - - Keys not installed - Klucze nie zainstalowane - - - - - Install decryption keys and restart Eden before attempting to install firmware. - Zainstaluj klucze deszyfrujące i uruchom ponownie Eden przed próbą instalacji firmware’u. - - - - Select Dumped Firmware Source Location - Wybierz lokalizację źródła zrzuconego firmware’u - - - - Select Dumped Firmware ZIP - Wybierz plik ZIP ze zrzuconym Firmwarem - - - - Zipped Archives (*.zip) - Archiwa ZIP (.zip) - - - - Firmware cleanup failed - Czyszczenie firmware’u nie powiodło się - - - - Failed to clean up extracted firmware cache. -Check write permissions in the system temp directory and try again. -OS reported error: %1 - Nie udało się wyczyścić pamięci podręcznej wyodrębnionego firmware’u. -Sprawdź uprawnienia zapisu do systemowego katalogu tymczasowego i spróbuj ponownie. -Błąd zgłoszony przez system: %1 - - - + No firmware available Brak dostępnego firmware'u - + Firmware Corrupted Uszkodzony Firmware - + Unknown applet - + Applet doesn't map to a known value. - + Record not found - + Applet not found. Please reinstall firmware. - + Capture Screenshot Zrób zrzut ekranu - + PNG Image (*.png) Obrazek PNG (*.png) - + TAS state: Running %1/%2 Status TAS: Działa %1%2 - + TAS state: Recording %1 Status TAS: Nagrywa %1 - + TAS state: Idle %1/%2 Status TAS: Bezczynny %1%2 - + TAS State: Invalid Status TAS: Niepoprawny - + &Stop Running &Wyłącz - + Stop R&ecording Przestań &Nagrywać - + Building: %n shader(s) Budowanie: %n shaderBudowanie: %n shaderyBudowanie: %n shaderówBudowanie: %n shaderów - + Scale: %1x %1 is the resolution scaling factor Skala: %1x - + Speed: %1% / %2% Prędkość: %1% / %2% - + Speed: %1% Prędkość: %1% - + Game: %1 FPS Gra: %1 FPS - + Frame: %1 ms Klatka: %1 ms - - - FSR - FSR - - - + NO AA BEZ AA - + VOLUME: MUTE Głośność: Wyciszony - + VOLUME: %1% Volume percentage (e.g. 50%) Głośność: %1% - + Derivation Components Missing Brak komponentów wyprowadzania - + Decryption keys are missing. Install them now? - + Wayland Detected! Wykryto Waylanda! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -8038,74 +8030,74 @@ Zaleca się zamiast tego używać X11. Czy chcesz wymusić jego użycie przy przyszłych uruchomieniach? - + Use X11 Używaj X11 - + Continue with Wayland Kontynuuj z Waylandem - + Don't show again Nie pokazuj ponownie - + Restart Required Wymagane ponowne uruchomienie - + Restart Eden to apply the X11 backend. Uruchom ponownie Edena, aby zastosować backend X11. - + Slow - + Turbo - + Unlocked - + Select RomFS Dump Target Wybierz cel zrzutu RomFS - + Please select which RomFS you would like to dump. Proszę wybrać RomFS, jakie chcesz zrzucić. - + Are you sure you want to close Eden? Czy na pewno chcesz zamknąć Edena? - - - + + + Eden Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. Czy na pewno chcesz zatrzymać emulację? Wszystkie niezapisane postępy zostaną utracone. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? @@ -8172,6 +8164,11 @@ Czy chcesz to pominąć i wyjść mimo to? ScaleForce ScaleForce + + + FSR + FSR + Area @@ -8183,52 +8180,62 @@ Czy chcesz to pominąć i wyjść mimo to? MMPX - + + SGSR + + + + + SGSR EdgeDir + + + + Docked Zadokowany - + Handheld Przenośny - + Fast Szybkie - + Balanced Zrównoważony - + Accurate Dokładny - + Vulkan Vulkan - + OpenGL GLSL - + OpenGL SPIRV - + OpenGL GLASM - + Null Null @@ -8271,7 +8278,7 @@ Jeśli chcesz usunąć pliki, które pozostały w starej lokalizacji danych, mo %1 - + Data was migrated successfully. Dane zostały pomyślnie przeniesione. @@ -9129,47 +9136,47 @@ p, li { white-space: pre-wrap; } %1 gra w %2 - + Play Time: %1 - + Never Played - + Version: %1 - + Version: 1.0.0 - + Installed SD Titles Zainstalowane tytuły SD - + Installed NAND Titles Zainstalowane tytuły NAND - + System Titles Tytuły systemu - + Add New Game Directory Dodaj nowy katalog gier - + Favorites Ulubione @@ -9290,47 +9297,47 @@ p, li { white-space: pre-wrap; } QtCommon::Content - + Game Requires Firmware Gra wymaga firmware’u - + The game you are trying to launch requires firmware to boot or to get past the opening menu. Please <a href='https://yuzu-mirror.github.io/help/quickstart'>dump and install firmware</a>, or press "OK" to launch anyways. Gra, którą próbujesz uruchomić, wymaga firmware’u do startu lub do przejścia ekranu początkowego. Please <a href='https://yuzu-mirror.github.io/help/quickstart'>Zrzuć i zainstaluj firmware</a> albo naciśnij „OK”, aby mimo to uruchomić. - + Installing Firmware... Instalacja Firmware... - - - - - + + + + + Cancel Anuluj - + Firmware Install Failed Instalacja firmware’u nie powiodła się - + Firmware Install Succeeded Instalacja firmware’u powiodła się - + Firmware integrity verification failed! Weryfikacja integralności firmware’u nie powiodła się! - - + + Verification failed for the following files: %1 @@ -9338,207 +9345,240 @@ p, li { white-space: pre-wrap; } %1 - - + + Verifying integrity... Weryfikacja integralności... - - + + Integrity verification succeeded! Weryfikacja integralności zakończona sukcesem! - - + + The operation completed successfully. Operacja zakończona pomyślnie. - - + + Integrity verification failed! Weryfikacja integralności nie powiodła się! - + File contents may be corrupt or missing. Zawartość pliku może być uszkodzona lub brakująca. - + Integrity verification couldn't be performed Nie można było przeprowadzić weryfikacji integralności - + Firmware installation cancelled, firmware may be in a bad state or corrupted. File contents could not be checked for validity. Instalacja firmware’u została anulowana — firmware może być w złym stanie lub uszkodzony. Nie udało się sprawdzić poprawności zawartości plików. - + Select Dumped Keys Location Wybierz lokalizację zrzutu kluczy - + Decryption Keys install succeeded Instalacja kluczy deszyfrujących powiodła się - + Decryption Keys install failed Instalacja kluczy deszyfrujących nie powiodła się - + Orphaned Profiles Detected! Wykryto osierocone profile! - + UNEXPECTED BAD THINGS MAY HAPPEN IF YOU DON'T READ THIS!<br>Eden has detected the following save directories with no attached profile:<br>%1<br><br>The following profiles are valid:<br>%2<br><br>Click "OK" to open your save folder and fix up your profiles.<br>Hint: copy the contents of the largest or last-modified folder elsewhere, delete all orphaned profiles, and move your copied contents to the good profile.<br><br>Still confused? See the <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/Orphaned.md'>help page</a>.<br> MOGĄ WYSTĄPIĆ NIEOCZEKIWANE PROBLEMY, JEŚLI TEGO NIE PRZECZYTASZ!<br>Eden wykrył następujące katalogi zapisów bez przypisanego profilu:<br>%1<br><br>The following profiles are valid:<br>%2<br><br>Kliknij „OK”, aby otworzyć folder zapisów i naprawić profile.<br>Wskazówka: skopiuj zawartość największego lub ostatnio modyfikowanego folderu w inne miejsce, usuń wszystkie osierocone profile, a następnie przenieś skopiowaną zawartość do właściwego profilu.<br><br>Nadal masz wątpliwości? Zobacz<a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/Orphaned.md'>tronę pomocy</a>.<br> - + Really clear data? Na pewno wyczyścić dane? - + Important data may be lost! Ważne dane mogą zostać utracone! - + Are you REALLY sure? Czy NA PEWNO chcesz to zrobić? - + Once deleted, your data will NOT come back! Only do this if you're 100% sure you want to delete this data. Po usunięciu Twoje dane NIE WRÓCĄ! Wykonaj to tylko, jeśli w 100% chcesz usunąć te dane. - + Clearing... Czyszczenie… - + Select Export Location Wybierz lokalizację eksportu - + %1.zip %1.zip - - + + + Zipped Archives (*.zip) Archiwa ZIP (.zip) - + Exporting data. This may take a while... Eksportowanie danych. To może chwilę potrwać… - + Exporting Eksportowanie - + Exported Successfully Wyeksportowano pomyślnie - + Data was exported successfully. Dane zostały pomyślnie wyeksportowane. - + Export Cancelled Eksport anulowany - + Export was cancelled by the user. Eksport został anulowany przez użytkownika. - + Export Failed Eksport nie powiódł się - + Ensure you have write permissions on the targeted directory and try again. Upewnij się, że masz uprawnienia zapisu do docelowego katalogu i spróbuj ponownie. - + Select Import Location Wybierz lokalizację importu - + Import Warning Ostrzeżenie dotyczące importu - + All previous data in this directory will be deleted. Are you sure you wish to proceed? Wszystkie dotychczasowe dane w tym katalogu zostaną usunięte. Czy na pewno chcesz kontynuować? - + Importing data. This may take a while... Importowanie danych. To może chwilę potrwać… - + Importing Importowanie - + Imported Successfully Zaimportowano pomyślnie - + Data was imported successfully. Dane zostały pomyślnie zaimportowane. - + Import Cancelled Import anulowany - + Import was cancelled by the user. Import został anulowany przez użytkownika. - + Import Failed Import nie powiódł się - + Ensure you have read permissions on the targeted directory and try again. Upewnij się, że masz uprawnienia odczytu do docelowego katalogu i spróbuj ponownie. + + + Keys not installed + + + + + Install decryption keys and restart Eden before attempting to install firmware. + + + + + Select Dumped Firmware Source Location + + + + + Select Dumped Firmware ZIP + + + + + Firmware cleanup failed + + + + + Failed to clean up extracted firmware cache. +Check write permissions in the system temp directory and try again. +OS reported error: %1 + + QtCommon::FS @@ -9787,72 +9827,72 @@ Czy chcesz ręcznie wybrać folder przenośny do użycia? Nie można było usunąć pamięci podręcznej metadanych. Może być używana albo nie istnieje. - + Create Shortcut Utwórz skrót - + Do you want to launch the game in fullscreen? Uruchomić grę w trybie pełnoekranowym? - + Shortcut Created Utworzono skrót - + Successfully created a shortcut to %1 Pomyślnie utworzono skrót do %1 - + Shortcut may be Volatile! Skrót może być nietrwały! - + This will create a shortcut to the current AppImage. This may not work well if you update. Continue? Zostanie utworzony skrót do bieżącego AppImage. Po aktualizacji może działać nieprawidłowo. Kontynuować? - + Failed to Create Shortcut Nie udało się utworzyć skrótu - + Failed to create a shortcut to %1 Nie udało się utworzyć skrótu do %1 - + Create Icon Utwórz ikonę - + Cannot create icon file. Path "%1" does not exist and cannot be created. Nie można utworzyć pliku ikony. Ścieżka „%1” nie istnieje i nie można jej utworzyć. - + No firmware available Brak dostępnego firmware'u - + Please install firmware to use the home menu. Zainstaluj firmware, aby używać Menu głównego. - + Home Menu Applet Aplet „Menu główne” - + Home Menu is not available. Please reinstall firmware. Menu główne jest niedostępne. Zainstaluj ponownie firmware. @@ -9860,55 +9900,55 @@ Czy chcesz ręcznie wybrać folder przenośny do użycia? QtCommon::Mod - + Mod Name - + What should this mod be called? - + RomFS - + ExeFS/Patch - + Cheat - + Mod Type - + Could not detect mod type automatically. Please manually specify the type of mod you downloaded. Most mods are RomFS mods, but patches (.pchtxt) are typically ExeFS mods. - - + + Mod Extract Failed - + Failed to create temporary directory %1 - + Zip file %1 is empty @@ -10609,65 +10649,65 @@ Wybierając „Z Eden”, dotychczasowe dane zapisu przechowywane w Ryujinx zost - + New Version Location - + All Files (*.*) - - - + + + Failed to save file - + Could not open file %1 for writing. - + Downloading... - + Cancel - + Could not write to file %1. - + Could not commit to file %1. - + Failed to download file - + Could not download from %1%2 Error code: %3 - + Download Complete - + Successfully downloaded %1. Would you like to open it? diff --git a/dist/languages/pt_BR.ts b/dist/languages/pt_BR.ts index 1bb88abafc..2c395e9904 100644 --- a/dist/languages/pt_BR.ts +++ b/dist/languages/pt_BR.ts @@ -377,7 +377,7 @@ Isto banirá tanto o nome de usuário como o endereço IP do fórum. Amiibo editor - Editor de Amiibo + Editor Amiibo @@ -722,8 +722,8 @@ Resoluções mais altas exigem mais VRAM e largura de banda. - Determines how sharpened the image will look using FSR's dynamic contrast. - Determines how sharpened the image will look using FSR's dynamic contrast. "Define o nível de nitidez da imagem usando o contraste dinâmico do FSR." + Determines how sharpened the image will look using FSR's or SGSR's dynamic contrast. + Determina o quão nítida a imagem ficará usando o contraste dinâmico do FSR ou do SGSR. @@ -780,24 +780,24 @@ Disabling it is only intended for debugging. Desabiltar essa opção só serve para propósitos de depuração. - + Use asynchronous GPU emulation Usar emulação assíncrona de GPU - + Uses an extra CPU thread for rendering. This option should always remain enabled. Usa uma thread de CPU extra para renderização. Esta opção deve estar sempre habilitada. - + NVDEC emulation: Emulação NVDEC: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. @@ -806,12 +806,12 @@ Tanto a CPU quanto a GPU podem ser utilizadas para decodificação, ou não deco Na maioria dos casos, a decodificação pela GPU fornece um melhor desempenho. - + ASTC Decoding Method: Método de Decodificação ASTC: - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -824,56 +824,68 @@ CPU Assíncrona: Usa a CPU para decodificar texturas ASTC sob demanda. Elimina t mas pode apresentar artefatos visuais. - + ASTC Recompression Method: Método de Recompressão ASTC: - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. A maioria das GPUs não suporta texturas ASTC e precisa descompactá-las para um formato intermediário: RGBA8.BC1/BC3: O formato intermediário será recomprimido para BC1 ou BC3, economizando VRAM mas degradando a qualidade da imagem. - + Frame Pacing Mode (Vulkan only) Modo de Ritmo de Quadros (apenas Vulkan) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. Controle como o emulador gerencia o ritmo de quadros para reduzir travamentos (engasgos) e deixar a taxa de quadros mais suave e consistente. - + VRAM Usage Mode: Modo de Uso da VRAM: - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. Define se o emulador deve priorizar a economia de memória ou fazer o uso máximo da memória de vídeo disponível para melhorar o desempenho. O modo agressivo pode impactar a performance de outros aplicativos, como softwares de gravação. - + Skip CPU Inner Invalidation Ignorar invalidação interna da CPU - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. Ignora certas invalidações de cache durante atualizações de memória, reduzindo o uso da CPU e melhorando a latência. Isso pode causar travamentos leves. - + + Anti-Flicker + Anti-Cintilação + + + + Forces GPU fence callbacks to wait for submitted GPU work. +Use with Fast GPU mode, to avoid flicker with lower performance impact. + Força os callbacks de GPU fence a aguardarem o trabalho enviado da GPU. +Use com o modo Fast GPU para evitar tremores com menor impacto no desempenho. + + + VSync Mode: Modo de Sincronização vertical: - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -884,12 +896,12 @@ Mailbox pode ter menor latência que FIFO e não apresenta cortes, mas pode perd Immediate (sem sincronização) exibe o que estiver disponível e pode apresentar cortes. - + Sync Memory Operations Sincronizar operações de memória - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. @@ -898,44 +910,44 @@ Esta opção corrige problemas em diversos jogos, mas pode reduzir o desempenho. Jogos baseados na Unreal Engine 4 costumam apresentar as mudanças mais significativas. - + Enable asynchronous presentation (Vulkan only) Ativar apresentação assíncrona (Somente Vulkan) - + Slightly improves performance by moving presentation to a separate CPU thread. Melhora ligeiramente o desempenho ao mover a apresentação para uma thread de CPU separada. - + Force maximum clocks (Vulkan only) Forçar clock máximo (somente Vulkan) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. Executa trabalho em segundo plano aguardando pelos comandos gráficos para evitar a GPU de reduzir seu clock. - + Anisotropic Filtering: Filtro Anisotrópico: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. Controla a qualidade da renderização de texturas em ângulos oblíquos. Seguro definir em 16x na maioria das GPUs. - + GPU Mode: Modo da GPU: - + Controls the GPU emulation mode. Most games render fine with Fast or Balanced modes, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. @@ -944,56 +956,56 @@ A maioria dos jogos roda bem nos modos Rápido ou Balanceado, mas o modo Preciso Partículas geralmente só renderizam corretamente no modo Preciso. - + DMA Accuracy: Precisão do DMA: - + Controls the DMA precision accuracy. Safe precision fixes issues in some games but may degrade performance. Controla a precisão do DMA. A precisão Segura corrige problemas em alguns jogos, mas pode reduzir o desempenho. - + Enable asynchronous shader compilation Ativar compilação assíncrona de shaders - + May reduce shader stutter. Pode reduzir travamentos de shaders. - + Fast GPU Time Tempo Rápido da GPU - + Overclocks the emulated GPU to increase dynamic resolution and render distance. Use 256 for maximal performance and 512 for maximal graphics fidelity. Faz overclock da GPU emulada para aumentar a resolução dinâmica e a distância de renderização. Use 256 para desempenho máximo e 512 para fidelidade gráfica máxima. - + GPU Unswizzle GPU Unswizzle - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. Acelera a decodificação de texturas 3D BCn usando processamento da GPU. Desative se ocorrerem travamentos ou falhas gráficas. - + GPU Unswizzle Max Texture Size Tamanho Máximo de Textura para GPU Unswizzle - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. @@ -1002,48 +1014,48 @@ A GPU é mais rápida para texturas médias e grandes, mas a CPU pode ser mais e Ajuste para encontrar o equilíbrio entre aceleração da GPU e sobrecarga da CPU. - + GPU Unswizzle Stream Size Tamanho do Fluxo de GPU Unswizzle - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. Define a quantidade máxima de dados de textura (em MiB) processados por quadro. Valores mais altos podem reduzir travamentos durante o carregamento de texturas, mas podem afetar a consistência dos quadros. - + GPU Unswizzle Chunk Size Tamanho do Bloco de GPU Unswizzle - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. Determina o número de fatias de profundidade processadas em uma única execução. Aumentar esse valor pode melhorar o desempenho em GPUs avançadas, mas pode causar TDR ou tempo limite do driver em hardwares mais fracos. - + Use Vulkan pipeline cache Utilizar cache de pipeline do Vulkan - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. Habilita o cache de pipeline da fabricante da GPU. Esta opção pode melhorar o tempo de carregamento de shaders significantemente em casos onde o driver Vulkan não armazena o cache de pipeline internamente. - + Enable Compute Pipelines (Intel Vulkan Only) Habilitar Pipeline de Computação (Somente Intel Vulkan) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. @@ -1052,184 +1064,194 @@ Esta configuração existe apenas para drivers proprietários da Intel e pode ca Pipelines de computação estão sempre habilitados em outros drivers. - + Enable Reactive Flushing Habilitar Flushing Reativo - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. Usa flushing reativo ao invés de flushing preditivo, permitindo mais precisão na sincronização da memória. - + Sync to framerate of video playback Sincronizar com o framerate da reprodução de vídeo - + Run the game at normal speed during video playback, even when the framerate is unlocked. Executa o jogo na velocidade normal durante a reprodução de vídeo, mesmo se o framerate estiver desbloqueado. - + Barrier feedback loops Ciclos de feedback de barreira - + Improves rendering of transparency effects in specific games. Melhora a renderização de efeitos de transparência em jogos específicos. - + Enable buffer history Ativar histórico de buffer - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. Permite acesso a estados anteriores do buffer. Esta opção pode melhorar a qualidade de renderização e a consistência de desempenho em alguns jogos. - + Fix bloom effects Corrigir efeitos de bloom - + Removes bloom in Burnout. Remove o bloom no Burnout. - + Enable Legacy Rescale Pass Ativar Passagem de Redimensionamento Legada - + May fix rescale issues in some games by relying on behavior from the previous implementation. Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. Pode corrigir problemas de redimensionamento em alguns jogos, baseando-se no comportamento da implementação anterior. Solução legada que corrige artefatos de linha em GPUs AMD e Intel, e cintilação de texturas cinzas em GPUs Nvidia no Luigi's Mansion 3. - + Extended Dynamic State Estado Dinâmico Estendido - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. Controla o número de recursos que podem ser usados no Estado Dinâmico Estendido. Estados mais altos permitem mais recursos e podem aumentar o desempenho, mas também podem causar problemas gráficos adicionais. - + Vertex Input Dynamic State Estado Dinâmico de Entrada de Vértices - + Enables vertex input dynamic state feature for better quality and performance. Ativa o recurso de estado dinâmico de entrada de vértices para melhor qualidade e desempenho. - + Sample Shading Sombreamento por Amostragem - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. Permite que o shader de fragmento seja processado por cada amostra em fragmentos multiamostrados, em vez de uma única vez por fragmento. Melhora a qualidade gráfica ao custo de desempenho. Valores mais altos aumentam a qualidade, mas reduzem a performance. - + RNG Seed Semente de RNG - + Controls the seed of the random number generator. Mainly used for speedrunning. Controla a semente do gerador de números aleatórios. Usado principalmente em speedruns. - + Device Name Nome do Dispositivo - + The name of the console. O nome do console. - + + Homebrew Args + + + + + Command-line arguments passed to homebrew at launch (e.g. -noglsl). + + + + Custom RTC Date: Data personalizada do RTC: - + This option allows to change the clock of the console. Can be used to manipulate time in games. Esta opção permite alterar o relógio do console. Pode ser usada para manipular o tempo nos jogos. - + The number of seconds from the current unix time Número de segundos a partir do tempo Unix atual - + Language: Idioma: - + This option can be overridden when region setting is auto-select Esta opção pode ser substituída quando a configuração de região estiver em seleção automática - + Region: Região: - + The region of the console. A região do console. - + Time Zone: Fuso Horário: - + The time zone of the console. O fuso horário do console. - + Sound Output Mode: Modo de saída de som - + Console Mode: Modo Console: - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. @@ -1238,1031 +1260,1041 @@ Os jogos ajustarão resolução, detalhes e controladores suportados conforme es Definir como Portátil pode ajudar a melhorar o desempenho em sistemas mais fracos. - + Prompt for user profile on boot Solicitar perfil de usuário na inicialização - + Useful if multiple people use the same PC. Útil se várias pessoas usam o mesmo PC. - + Pause when not in focus Pausar quando não estiver em foco - + Pauses emulation when focusing on other windows. Pausa a emulação ao focar em outras janelas. - + Confirm before stopping emulation Confirmar antes de parar a emulação - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. Substitui as confirmações ao parar a emulação. Ao ativar, ignora esses avisos e encerra diretamente a emulação. - + Hide mouse on inactivity Esconder rato quando inactivo. - + Hides the mouse after 2.5s of inactivity. Oculta o mouse após 2,5s de inatividade. - + Disable controller applet Desabilitar applet de controle - + Forcibly disables the use of the controller applet in emulated programs. When a program attempts to open the controller applet, it is immediately closed. Desativa forçadamente o uso do applet de controle em programas emulados. Quando um programa tenta abrir o applet, ele é imediatamente fechado. - + Check for updates Verificar atualizações - + Whether or not to check for updates upon startup. Define se deve verificar atualizações na inicialização. - + Enable Gamemode Habilitar Gamemode - + Force X11 as Graphics Backend Forçar X11 como backend gráfico - + Custom frontend Frontend customizado - + Real applet Applet real - + Never Nunca - + On Load Ao carregar - + Always Sempre - + CPU CPU - + GPU GPU - + CPU Asynchronous CPU Assíncrona - + Uncompressed (Best quality) Descompactado (Melhor Qualidade) - + BC1 (Low quality) BC1 (Baixa qualidade) - + BC3 (Medium quality) BC3 (Média qualidade) - - + + Auto Automático - + 30 FPS 30 FPS - + 60 FPS 60 FPS - + 90 FPS 90 FPS - + 120 FPS 120 FPS - + Conservative Conservador - + Aggressive Agressivo - + Vulkan Vulcano - + OpenGL GLSL OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) OpenGL GLASM (Shaders em Assembly, apenas NVIDIA) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) OpenGL SPIR-V (Experimental, apenas AMD/Mesa) - + Null Nenhum - + Fast Rápido - + Balanced Balanceado - - + + Accurate Preciso - - + + Default Padrão - + Unsafe (fast) Inseguro (rápido) - + Safe (stable) Seguro (estável) - + Unsafe Inseguro - + Paranoid (disables most optimizations) Paranoia (desativa a maioria das otimizações) - + Debugging Depuração - + Dynarmic Dynarmic - + NCE NCE - + Borderless Windowed Janela sem bordas - + Exclusive Fullscreen Tela cheia exclusiva - + No Video Output Sem saída de vídeo - + CPU Video Decoding Decodificação de vídeo pela CPU - + GPU Video Decoding (Default) Decodificação de vídeo pela GPU (Padrão) - + 0.25X (180p/270p) [EXPERIMENTAL] 0.25X (180p/270p) [EXPERIMENTAL] - + 0.5X (360p/540p) [EXPERIMENTAL] 0.5X (360p/540p) [EXPERIMENTAL] - + 0.75X (540p/810p) [EXPERIMENTAL] 0.75X (540p/810p) [EXPERIMENTAL] - + 1X (720p/1080p) 1X (720p/1080p) - + 1.25X (900p/1350p) [EXPERIMENTAL] 1.25X (900p/1350p) [EXPERIMENTAL] - + 1.5X (1080p/1620p) [EXPERIMENTAL] 1.5X (1080p/1620p) [EXPERIMENTAL] - + 2X (1440p/2160p) 2X (1440p/2160p) - + 3X (2160p/3240p) 3X (2160p/3240p) - + 4X (2880p/4320p) 4X (2880p/4320p) - + 5X (3600p/5400p) 5X (3600p/5400p) - + 6X (4320p/6480p) 6X (4320p/6480p) - + 7X (5040p/7560p) 7X (5040p/7560p) - + 8X (5760p/8640p) 8X (5760p/8640p) - + Nearest Neighbor Vizinho mais próximo - + Bilinear Bilinear - + Bicubic Bicúbico - + Gaussian Gaussiano - + Lanczos Lanczos - + ScaleForce ScaleForce - + AMD FidelityFX Super Resolution AMD FidelityFX Super Resolution - + Area Área - + MMPX MMPX - + Zero-Tangent Zero-Tangent - + B-Spline B-Spline - + Mitchell Mitchell - + Spline-1 Spline-1 - - + + Snapdragon Game Super Resolution + + + + + Snapdragon Game Super Resolution EdgeDir + + + + + None Nenhum - + FXAA FXAA - + SMAA SMAA - + Default (16:9) Padrão (16:9) - + Force 4:3 Forçar 4:3 - + Force 21:9 Forçar 21:9 - + Force 16:10 Forçar 16:10 - + Stretch to Window Esticar à Janela - + Automatic Automático - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x - + 32x 32x - + 64x 64x - + Japanese (日本語) Japonês (日本語) - + American English Inglês Americano - + French (français) Francês (français) - + German (Deutsch) Alemão (Deutsch) - + Italian (italiano) Italiano (italiano) - + Spanish (español) Espanhol (español) - + Chinese Chinês - + Korean (한국어) Coreano (한국어) - + Dutch (Nederlands) Holandês (Nederlands) - + Portuguese (português) Português (português) - + Russian (Русский) Russo (Русский) - + Taiwanese Taiwanês - + British English Inglês Britânico - + Canadian French Francês Canadense - + Latin American Spanish Espanhol Latino-Americano - + Simplified Chinese Chinês Simplificado - + Traditional Chinese (正體中文) Chinês Tradicional (正 體 中文) - + Brazilian Portuguese (português do Brasil) Português do Brasil (Brazilian Portuguese) - + Polish (polska) Polonês (polska) - + Thai (แบบไทย) Tailandês (แบบไทย) - - + + Japan Japão - + USA EUA - + Europe Europa - + Australia Austrália - + China China - + Korea Coreia - + Taiwan Taiwan - + Auto (%1) Auto select time zone Auto (%1) - + Default (%1) Default time zone Padrão (%1) - + CET CET - + CST6CDT CST6CDT - + Cuba Cuba - + EET EET - + Egypt Egito - + Eire Irlanda - + EST EST - + EST5EDT EST5EDT - + GB GB - + GB-Eire GB-Irlanda - + GMT GMT - + GMT+0 GMT+0 - + GMT-0 GMT-0 - + GMT0 GMT0 - + Greenwich Greenwich - + Hongkong Hongkong - + HST HST - + Iceland Islândia - + Iran Irã - + Israel Israel - + Jamaica Jamaica - + Kwajalein Kwajalein - + Libya Líbia - + MET MET - + MST MST - + MST7MDT MST7MDT - + Navajo Navajo - + NZ NZ - + NZ-CHAT NZ-CHAT - + Poland Polônia - + Portugal Portugal - + PRC PRC - + PST8PDT PST8PDT - + ROC ROC - + ROK ROK - + Singapore Singapura - + Turkey Turquia - + UCT UCT - + Universal Universal - + UTC UTC - + W-SU W-SU - + WET WET - + Zulu Zulu - + Mono Mono - + Stereo Estéreo - + Surround Surround - + 4GB DRAM (Default) 4GB DRAM (Padrão) - + 6GB DRAM (Unsafe) 6GB DRAM (Não seguro) - + 8GB DRAM 8GB DRAM - + 10GB DRAM (Unsafe) 10GB DRAM (Inseguro) - + 12GB DRAM (Unsafe) 12GB DRAM (Inseguro) - + Docked Ancorado - + Handheld Portátil - - + + Off Desligado - + Boost (1700MHz) Impulso (1700MHz) - + Fast (2000MHz) Rápido (2000MHz) - + Always ask (Default) Sempre perguntar (Padrão) - + Only if game specifies not to stop Somente se o jogo especificar para não parar - + Never ask Nunca perguntar - - + + Medium (256) Médio (256) - - + + High (512) Alto (512) - + Very Small (16 MB) Muito Pequeno (16 MB) - + Small (32 MB) Pequeno (32 MB) - + Normal (128 MB) Normal (128 MB) - + Large (256 MB) Grande (256 MB) - + Very Large (512 MB) Muito Grande (512 MB) - + Very Low (4 MB) Muito Baixo (4 MB) - + Low (8 MB) Baixo (8 MB) - + Normal (16 MB) Normal (16 MB) - + Medium (32 MB) Médio (32 MB) - + High (64 MB) Alto (64 MB) - + Very Low (32) Muito Baixo (32) - + Low (64) Baixo (64) - + Normal (128) Normal (128) - + Disabled Desativado - + ExtendedDynamicState 1 Estado Dinâmico Estendido 1 - + ExtendedDynamicState 2 Estado Dinâmico Estendido 2 - + ExtendedDynamicState 3 Estado Dinâmico Estendido 3 - + Tree View Visualização em Árvore - + Grid View Visualização em Grade @@ -3041,7 +3073,7 @@ Quando um programa tenta abrir o applet, ele é imediatamente fechado. Save Data - Salvar dados + Dados de Salvamento @@ -3322,33 +3354,33 @@ Gostaria de deletar o salvamento antigo? Cor de fundo: - + % - FSR sharpening percentage (e.g. 50%) - % + FSR/SGSR sharpening percentage (e.g. 50%) + - + Off Desligado - + VSync Off Sincronização vertical desligada - + Recommended Recomendado - + On Ligado - + VSync On Sincronização vertical ligada @@ -4477,7 +4509,7 @@ Para inverter os eixos, mova o seu analógico primeiro verticalmente e depois ho - + Configure Configurar @@ -4508,7 +4540,7 @@ Para inverter os eixos, mova o seu analógico primeiro verticalmente e depois ho - + Test Testar @@ -4523,77 +4555,77 @@ Para inverter os eixos, mova o seu analógico primeiro verticalmente e depois ho Remover Servidor - + %1:%2 %1:%2 - - - - - - + + + + + + Eden Eden - + Port number has invalid characters O número da porta tem caracteres inválidos - + Port has to be in range 0 and 65353 A porta tem que estar entre 0 e 65353 - + IP address is not valid O endereço IP não é válido - + This UDP server already exists Este servidor UDP já existe - + Unable to add more than 8 servers Não é possível adicionar mais de 8 servidores - + Testing Testando - + Configuring Configurando - + Test Successful Teste Bem-Sucedido - + Successfully received data from the server. Dados recebidos do servidor com êxito. - + Test Failed Teste Falhou - + Could not receive valid data from the server.<br>Please verify that the server is set up correctly and the address and port are correct. Não foi possível receber dados válidos do servidor.<br>Por favor verifica que o servidor está configurado correctamente e o endereço e porta estão correctos. - + UDP Test or calibration configuration is in progress.<br>Please wait for them to finish. Teste UDP ou configuração de calibragem em progresso.<br> Por favor espera que termine. @@ -4778,57 +4810,57 @@ Os valores atuais são %1% e %2% respectivamente. Algumas configurações só estão disponíveis apenas quando não houver nenhum jogo em execução. - + Add-Ons Add-Ons - + System Sistema - + CPU CPU - + Graphics Gráficos - + Adv. Graphics Gráficos Avç. - + Ext. Graphics Gráficos Ext. - + Audio Audio - + Input Profiles Perfis de controle - + Network Rede - + Applets Applets - + Properties Propriedades @@ -5831,22 +5863,22 @@ Arrasta os pontos para mudar a posição, ou dá duplo-clique nas células da ta Delete all data in this directory. THIS IS 100% IRREVERSABLE! - + Deletar todos os dados nesse diretório. ISSO É 100% IRREVERSÍVEL! Export all data in this directory. This may take a while! - + Exportar todos dados desse diretório. Isso pode levar um tempo! Import data for this directory. This may take a while, and will delete ALL EXISTING DATA! - + Importar todos os dados para esse diretório. Isso pode levar um tempo, e vai deletar TODOS OS DADOS JÁ EXISTENTES! - + Calculating... - + Calculando... @@ -5869,12 +5901,12 @@ Arrasta os pontos para mudar a posição, ou dá duplo-clique nas células da ta Dependency - + Dependência Version - + Versão @@ -5938,27 +5970,27 @@ Arrasta os pontos para mudar a posição, ou dá duplo-clique nas células da ta Username is not valid. Must be 4 to 20 alphanumeric characters. - + O Nome de Usuário não é válido. Deve conter entre 4 à 20 caracteres alfanuméricos. Room name is not valid. Must be 4 to 20 alphanumeric characters. - + Nome de Sala não é válido. Deve conter entre 4 à 20 caracteres alfanuméricos. Username is already in use or not valid. Please choose another. - + Nome de Usuário já está em uso ou não é válido. Por favor, escolha outro. IP is not a valid IPv4 address. - + IP não é um endereço válido IPv4. Port must be a number between 0 to 65535. - + O Número de Port deve ser entre 0 até 65535. @@ -5968,7 +6000,7 @@ Arrasta os pontos para mudar a posição, ou dá duplo-clique nas células da ta Unable to find an internet connection. Check your internet settings. - + Não foi possível achar uma conexão de Internet. Cheque suas configurações de Internet. @@ -5978,7 +6010,7 @@ Arrasta os pontos para mudar a posição, ou dá duplo-clique nas células da ta Unable to connect to the room because it is already full. - + Não foi possível conectar na sala porque já está cheia. @@ -5988,7 +6020,7 @@ Arrasta os pontos para mudar a posição, ou dá duplo-clique nas células da ta The host of the room has banned you. Speak with the host to unban you or try a different room. - + O Anfitrião da sala te baniu. Fale com o Anfitrião para que ele retire seu banimento ou tente uma sala diferente. @@ -5998,378 +6030,383 @@ Arrasta os pontos para mudar a posição, ou dá duplo-clique nas células da ta Incorrect password. - + Senha Incorreta. An unknown error occurred. If this error continues to occur, please open an issue - + Um erro desconhecido ocorreu. Se isso continuar a prosseguir, por favor, abra uma "Issue" em nosso repositório e reporte Connection to room lost. Try to reconnect. - + Conexão com a Sala foi perdida. Tentando Reconectar. You have been kicked by the room host. - + Você foi chutado da Sala do Anfitrião. IP address is already in use. Please choose another. - + Endereço IP já está em uso. Por favor, escolha outro. You do not have enough permission to perform this action. - + Você não tem permissão o suficiente para fazer essa ação. The user you are trying to kick/ban could not be found. They may have left the room. - + O Usuário que você está tentar Chutar/Banir não pode ser encontrado. +Ele deve ter saído da Sala. No valid network interface is selected. Please go to Configure -> System -> Network and make a selection. - + Nenhuma Interface de Rede foi selecionada. +Por favor vá para Configuração -> Sistema -> Rede e selecione. Error - + Erro GRenderWindow - - + + OpenGL not available! OpenGL não está disponível! - + OpenGL shared contexts are not supported. Shared contexts do OpenGL não são suportados. - + Eden has not been compiled with OpenGL support. - + Eden não foi compilado com suporte a OpenGL. - - - + + + Error while initializing OpenGL! Erro ao inicializar OpenGL! - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. O seu GPU pode não suportar OpenGL, ou não tem os drivers gráficos mais recentes. - + Error while initializing OpenGL 4.6! Erro ao inicializar o OpenGL 4.6! - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 A sua GPU não tem suporte a OpenGL 4.6, talvez você não esteja usando os drivers gráficos mais atuais.<br><br>Renderizador GL:<br>%1 - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 Sua GPU pode não suportar uma ou mais extensões necessárias do OpenGL. Verifique se você possui a última versão dos drivers gráficos.<br><br>Renderizador GL:<br>%1<br><br>Extensões não suportadas:<br>%2 - + This build doesn't have OpenGL support. - + Essa build não suporte a OpenGL. GameList - + &Add New Game Directory &Adicionar novo diretório de jogos - + Favorite Favorito - + Start Game Iniciar jogo - + Start Game without Custom Configuration Iniciar jogo sem configuração personalizada - + Open Save Data Location Abrir Localização de Dados Salvos - + Open Mod Data Location Abrir a Localização de Dados do Mod - + Open Transferable Pipeline Cache Abrir cache de pipeline transferível - + Link to Ryujinx Vincular ao Ryujinx - + Remove Remover - + Remove Installed Update Remover Actualizações Instaladas - + Remove All Installed DLC Remover Todos os DLC Instalados - + Remove Custom Configuration Remover Configuração Personalizada - + Remove Cache Storage Remove a Cache do Armazenamento - + Remove OpenGL Pipeline Cache Remover cache de pipeline do OpenGL - + Remove Vulkan Pipeline Cache Remover cache de pipeline do Vulkan - + Remove All Pipeline Caches Remover todos os caches de pipeline - + Remove All Installed Contents Remover Todos os Conteúdos Instalados - + Manage Play Time - + Gerenciar Tempo de Jogo - + Edit Play Time Data - + Editar Dados do Tempo de Jogo - + Remove Play Time Data Remover dados de tempo jogado - - + + Dump RomFS Despejar RomFS - + Dump RomFS to SDMC Extrair RomFS para SDMC - + Verify Integrity Verificar integridade - + Copy Title ID to Clipboard Copiar título de ID para a área de transferência - + Navigate to GameDB entry Navegue para a Entrada da Base de Dados de Jogos - + Create Shortcut Criar Atalho - + Add to Desktop Adicionar à Área de Trabalho - + Add to Applications Menu Adicionar ao Menu de Aplicativos - + Configure Game - + Configurar Jogo - + Scan Subfolders Examinar Sub-pastas - + Remove Game Directory Remover diretório do Jogo - + ▲ Move Up ▲ Mover para Cima - + ▼ Move Down ▼ Mover para Baixo - + Open Directory Location Abrir Localização do diretório - + Clear Limpar - - - Name - Nome - - - - Compatibility - Compatibilidade - - - - Add-ons - Add-ons - - - - File type - Tipo de Arquivo - - - - Size - Tamanho - - - - Play time - Tempo jogado - GameListItemCompat - + Ingame Não Jogável - + Game starts, but crashes or major glitches prevent it from being completed. O jogo inicia, porém problemas ou grandes falhas impedem que ele seja concluído. - + Perfect Perfeito - + Game can be played without issues. O jogo pode ser jogado sem problemas. - + Playable Jogável - + Game functions with minor graphical or audio glitches and is playable from start to finish. O jogo funciona com pequenas falhas gráficas ou de áudio e pode ser reproduzido do início ao fim. - + Intro/Menu Introdução / Menu - + Game loads, but is unable to progress past the Start Screen. O jogo carrega, porém não consegue passar da tela inicial. - + Won't Boot Não Inicia - + The game crashes when attempting to startup. O jogo trava ao tentar iniciar. - + Not Tested Não Testado - + The game has not yet been tested. O jogo ainda não foi testado. + + GameListModel + + + Name + Nome + + + + Compatibility + Compatibilidade + + + + Add-ons + Complementos + + + + File type + Tipo de arquivo + + + + Size + Tamanho + + + + Play time + Tempo de jogo + + GameListPlaceholder - + Double-click to add a new folder to the game list Clique duas vezes para adicionar uma nova pasta à lista de jogos @@ -6377,17 +6414,17 @@ Please go to Configure -> System -> Network and make a selection. GameListSearchField - + %1 of %n result(s) - + Filter: Filtro: - + Enter pattern to filter Digite o padrão para filtrar @@ -6652,12 +6689,12 @@ Mensagem de Depuração: Toggle Turbo Speed - + Alternar Modo Turbo Toggle Slow Speed - + Alternar Velocidade Lenta @@ -6883,1139 +6920,1100 @@ Mensagem de Depuração: Modo Lista de &Jogos - + Game &Icon Size Tamanho do &Ícone do Jogo - + Reset Window Size to &720p Restaurar tamanho da janela para &720p - + Reset Window Size to 720p Restaurar tamanho da janela para 720p - + Reset Window Size to &900p Restaurar tamanho da janela para &900p - + Reset Window Size to 900p Restaurar tamanho da janela para 900p - + Reset Window Size to &1080p Restaurar tamanho da janela para &1080p - + Reset Window Size to 1080p Restaurar tamanho da janela para 1080p - + &Multiplayer &Multijogador - + &Tools &Ferramentas - + Am&iibo Am&iibo - + Launch &Applet Iniciar &Applet - + &TAS &TAS - + &Create Home Menu Shortcut - + &Criar atalho para o menu Home - + Install &Firmware Instalar &Firmware - + &Help &Ajuda - + &Install Files to NAND... &Instalar arquivos na NAND... - + L&oad File... C&arregar arquivo... - + Load &Folder... Carregar &pasta... - + E&xit &Sair - - + + &Pause &Pausa - + &Stop &Parar - + &Verify Installed Contents &Verificar conteúdo instalado - + &About Eden &Sobre o Eden - + Single &Window Mode Modo de &janela única - + Con&figure... Con&figurar... - + Ctrl+, Ctrl+, - + Enable Overlay Display Applet - + Habilitar a Sobreposição de Display do Applet - + Show &Filter Bar Mostrar Barra de &Filtros - + Show &Status Bar Mostrar Barra de &Estado - + Show Status Bar Mostrar Barra de Estado - + &Browse Public Game Lobby &Navegar no Lobby de Salas Públicas - + &Create Room &Criar Sala - + &Leave Room &Sair da Sala - + &Direct Connect to Room Conectar &Diretamente Numa Sala - + &Show Current Room Exibir &Sala Atual - + F&ullscreen T&ela cheia - + &Restart &Reiniciar - + Load/Remove &Amiibo... Carregar/Remover &Amiibo... - + &Report Compatibility &Reportar compatibilidade - + Open &Mods Page Abrir Página de &Mods - + Open &Quickstart Guide Abrir &guia de início rápido - + &FAQ &Perguntas frequentes - + &Capture Screenshot &Captura de Tela - + &Album &Álbum - + &Set Nickname and Owner &Definir apelido e proprietário - + &Delete Game Data &Remover dados do jogo - + &Restore Amiibo &Recuperar Amiibo - + &Format Amiibo &Formatar Amiibo - + &Mii Editor - + &Editor de Mii - + &Configure TAS... &Configurar TAS - + Configure C&urrent Game... Configurar jogo atual... - - + + &Start &Começar - + &Reset &Restaurar - - + + R&ecord G&ravar - + Open &Controller Menu Menu Abrir &Controles - + Install Decryption &Keys - + Instalar Chaves de &Encriptação - + &Home Menu - + &Menu Home - + &Desktop &Área de Trabalho - + &Application Menu - + &Menu de Aplicativo - + &Root Data Folder - + &Pasta Raiz de Dados - + &NAND Folder &Pasta NAND - + &SDMC Folder - + &Pasta SDMC - + &Mod Folder - + &Pasta de Mods - + &Log Folder - + &Pasta de Logs - + From Folder - + Pela Pasta - + From ZIP - + Pelo ZIP - + &Eden Dependencies - + &Dependências do Eden - + &Data Manager - + &Gestor de Dados - + &Tree View - + &Visualização em Árvore - + &Grid View - + &Visualizar Grid - + Game Icon Size - + Tamanho do Ícone do jogo - - + + None - Nenhum(a) + Nenhum - + Show Game &Name - + Mostrar Nome de &Jogo - + Show &Performance Overlay Mostrar Sobreposição de &Desempenho - + + &Carousel View + + + + Small (32x32) Pequeno (32x32) - + Standard (64x64) Padrão (64x64) - + Large (128x128) Grande (128x128) - + Full Size (256x256) Tamanho completo (256x256) - + Broken Vulkan Installation Detected Detectada Instalação Defeituosa do Vulkan - + Vulkan initialization failed during boot. - + Inicialização do Vulkan falhou durante o boot. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping - + Rodando um jogo - + Loading Web Applet... - + Carregando Applet Web... - - + + Disable Web Applet - + Desativar Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + Desativar o Applet Web pode problemas indefinidos e deverá apenas ser usando com o Super Mario 3D All-Stars. Você tem certeza que quer desativar o Applet Web? +(Isso pode ser reativado nas Configurações de Depuração.) - + The amount of shaders currently being built - + A Quantidade total de Shaders está sendo atualmente construída - + The current selected resolution scaling multiplier. - + A atual multiplicação de escala de resolução selecionada. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. - + Velocidade atual da emulação. Valores altos ou mais baixo que 100% indicam que o emulador está rodando mais rápido ou mais lento que um Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. - + Quantos quadros por segundo o jogo está mostrando atualmente. Isto varia de jogo para jogo e de cena a cena. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. - + Unmute - + Mute - + Reset Volume - + &Clear Recent Files - + &Continue - + Warning: Outdated Game Format - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. - - + + Error while loading ROM! - + The ROM format is not supported. - + An error occurred initializing the video core. - + Eden has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-mirror.github.io/help/reference/log-files/'>How to Upload the Log File</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. - + (64-bit) - + (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit - + Closing software... - + Save Data - + Mod Data - + Error Opening %1 Folder - - + + Folder does not exist! - + Remove Installed Game Contents? - + Remove Installed Game Update? - + Remove Installed Game DLC? - + Remove Entry - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? - + Remove Cache Storage? - + Remove File - + Remove Play Time Data - + Reset play time? - - + + RomFS Extraction Failed! - + There was an error copying the RomFS files or the user cancelled the operation. - + Full - + Skeleton - + Select RomFS Dump Mode - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... - - + + Cancel - + RomFS Extraction Succeeded! - + The operation completed successfully. - + Error Opening %1 - + Select Directory Selecionar Diretório - + Properties - + The game properties could not be loaded. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. - + Load File - + Open Extracted ROM Directory Abrir o Diretório da ROM extraída - + Invalid Directory Selected Diretório selecionado inválido - + The directory you have selected does not contain a 'main' file. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files - + %n file(s) remaining - + Installing file "%1"... - - + + Install Results - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application - + System Archive - + System Application Update - + Firmware Package (Type A) - + Firmware Package (Type B) - + Game - + Game Update - + Game DLC - + Delta Title - + Select NCA Install Type... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) - + Failed to Install - + The title type you selected for the NCA is invalid. - + File not found - + File "%1" not found - + OK - + Function Disabled - + Compatibility list reporting is currently disabled. Check back later! - + Error opening URL - + Unable to open the URL "%1". - + TAS Recording - + Overwrite file of player 1? - + Invalid config detected - + Handheld controller can't be used on docked mode. Pro controller will be selected. - - + + Amiibo - + Amiibo - - + + The current amiibo has been removed - + O Amiibo atual foi removido - + Error - - + + The current game is not looking for amiibos - + Amiibo File (%1);; All Files (*.*) - + Load Amiibo - + Error loading Amiibo data - + The selected file is not a valid amiibo - + The selected file is already on use - + An unknown error occurred - - - Keys not installed - - - - - - Install decryption keys and restart Eden before attempting to install firmware. - - - - - Select Dumped Firmware Source Location - - - - - Select Dumped Firmware ZIP - - - - - Zipped Archives (*.zip) - - - - - Firmware cleanup failed - - - - - Failed to clean up extracted firmware cache. -Check write permissions in the system temp directory and try again. -OS reported error: %1 - - - - + No firmware available - + Firmware Corrupted - + Unknown applet - + Applet doesn't map to a known value. - + Record not found - + Applet not found. Please reinstall firmware. - + Capture Screenshot - + PNG Image (*.png) - + TAS state: Running %1/%2 - + TAS state: Recording %1 - + TAS state: Idle %1/%2 - + TAS State: Invalid - + &Stop Running - + Stop R&ecording - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor - + Speed: %1% / %2% - + Speed: %1% - + Game: %1 FPS - + Frame: %1 ms - - - FSR - - - - + NO AA - + VOLUME: MUTE - + VOLUME: %1% Volume percentage (e.g. 50%) - + Derivation Components Missing - + Decryption keys are missing. Install them now? - + Wayland Detected! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -8023,74 +8021,74 @@ Would you like to force it for future launches? - + Use X11 - + Continue with Wayland - + Don't show again - + Restart Required - + Restart Eden to apply the X11 backend. - + Slow - + Turbo - + Unlocked - + Select RomFS Dump Target - + Please select which RomFS you would like to dump. - + Are you sure you want to close Eden? - - - + + + Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? @@ -8156,6 +8154,11 @@ Would you like to bypass this and exit anyway? ScaleForce + + + FSR + + Area @@ -8167,52 +8170,62 @@ Would you like to bypass this and exit anyway? - + + SGSR + + + + + SGSR EdgeDir + + + + Docked - + Handheld - + Fast - + Balanced - + Accurate - + Vulkan - + OpenGL GLSL - + OpenGL SPIRV - + OpenGL GLASM - + Null @@ -8245,7 +8258,7 @@ If you wish to clean up the files which were left in the old data location, you - + Data was migrated successfully. @@ -9102,47 +9115,47 @@ p, li { white-space: pre-wrap; } %1 está jogando %2 - + Play Time: %1 - + Never Played - + Version: %1 - + Version: 1.0.0 - + Installed SD Titles Títulos SD instalados - + Installed NAND Titles Títulos NAND instalados - + System Titles Títulos do sistema - + Add New Game Directory Adicionar novo diretório de jogos - + Favorites Favoritos @@ -9263,253 +9276,286 @@ p, li { white-space: pre-wrap; } QtCommon::Content - + Game Requires Firmware - + The game you are trying to launch requires firmware to boot or to get past the opening menu. Please <a href='https://yuzu-mirror.github.io/help/quickstart'>dump and install firmware</a>, or press "OK" to launch anyways. - + Installing Firmware... - - - - - + + + + + Cancel - + Firmware Install Failed - + Firmware Install Succeeded - + Firmware integrity verification failed! - - + + Verification failed for the following files: %1 - - + + Verifying integrity... + + + + Integrity verification succeeded! + + - Integrity verification succeeded! - - - - - The operation completed successfully. - - + + Integrity verification failed! - + File contents may be corrupt or missing. - + Integrity verification couldn't be performed - + Firmware installation cancelled, firmware may be in a bad state or corrupted. File contents could not be checked for validity. - + Select Dumped Keys Location - + Decryption Keys install succeeded - + Decryption Keys install failed - + Orphaned Profiles Detected! - + UNEXPECTED BAD THINGS MAY HAPPEN IF YOU DON'T READ THIS!<br>Eden has detected the following save directories with no attached profile:<br>%1<br><br>The following profiles are valid:<br>%2<br><br>Click "OK" to open your save folder and fix up your profiles.<br>Hint: copy the contents of the largest or last-modified folder elsewhere, delete all orphaned profiles, and move your copied contents to the good profile.<br><br>Still confused? See the <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/Orphaned.md'>help page</a>.<br> - + Really clear data? - + Important data may be lost! - + Are you REALLY sure? - + Once deleted, your data will NOT come back! Only do this if you're 100% sure you want to delete this data. - + Clearing... - + Select Export Location - + %1.zip - - + + + Zipped Archives (*.zip) - + Exporting data. This may take a while... - + Exporting - + Exported Successfully - + Data was exported successfully. - + Export Cancelled - + Export was cancelled by the user. - + Export Failed - + Ensure you have write permissions on the targeted directory and try again. - + Select Import Location - + Import Warning - + All previous data in this directory will be deleted. Are you sure you wish to proceed? - + Importing data. This may take a while... - + Importing - + Imported Successfully - + Data was imported successfully. - + Import Cancelled - + Import was cancelled by the user. - + Import Failed - + Ensure you have read permissions on the targeted directory and try again. + + + Keys not installed + + + + + Install decryption keys and restart Eden before attempting to install firmware. + + + + + Select Dumped Firmware Source Location + + + + + Select Dumped Firmware ZIP + + + + + Firmware cleanup failed + + + + + Failed to clean up extracted firmware cache. +Check write permissions in the system temp directory and try again. +OS reported error: %1 + + QtCommon::FS @@ -9755,72 +9801,72 @@ Gostaria de selecionar manualmente uma pasta portátil para usar? - + Create Shortcut - + Do you want to launch the game in fullscreen? - + Shortcut Created - + Successfully created a shortcut to %1 - + Shortcut may be Volatile! - + This will create a shortcut to the current AppImage. This may not work well if you update. Continue? - + Failed to Create Shortcut - + Failed to create a shortcut to %1 - + Create Icon - + Cannot create icon file. Path "%1" does not exist and cannot be created. - + No firmware available - + Please install firmware to use the home menu. - + Home Menu Applet - + Home Menu is not available. Please reinstall firmware. @@ -9828,55 +9874,55 @@ Gostaria de selecionar manualmente uma pasta portátil para usar? QtCommon::Mod - + Mod Name - + What should this mod be called? - + RomFS - + ExeFS/Patch - + Cheat - + Mod Type - + Could not detect mod type automatically. Please manually specify the type of mod you downloaded. Most mods are RomFS mods, but patches (.pchtxt) are typically ExeFS mods. - - + + Mod Extract Failed - + Failed to create temporary directory %1 Falha ao criar um diretório temporário %1 - + Zip file %1 is empty @@ -10572,65 +10618,65 @@ Ao selecionar "Do Eden", os dados salvos anteriores armazenados no Ryu - + New Version Location - + All Files (*.*) - - - + + + Failed to save file - + Could not open file %1 for writing. - + Downloading... - + Cancel - + Could not write to file %1. - + Could not commit to file %1. - + Failed to download file - + Could not download from %1%2 Error code: %3 - + Download Complete - + Successfully downloaded %1. Would you like to open it? diff --git a/dist/languages/pt_PT.ts b/dist/languages/pt_PT.ts index 1e535c5e92..79b795dd1d 100644 --- a/dist/languages/pt_PT.ts +++ b/dist/languages/pt_PT.ts @@ -706,7 +706,7 @@ Options lower than 1X can cause artifacts. - Determines how sharpened the image will look using FSR's dynamic contrast. + Determines how sharpened the image will look using FSR's or SGSR's dynamic contrast. @@ -760,23 +760,23 @@ Disabling it is only intended for debugging. Desabiltar essa opção só serve para propósitos de depuração. - + Use asynchronous GPU emulation - + Uses an extra CPU thread for rendering. This option should always remain enabled. - + NVDEC emulation: Emulação NVDEC: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. @@ -785,12 +785,12 @@ Tanto a CPU quanto a GPU podem ser utilizadas para decodificação, ou não deco Na maioria dos casos, a decodificação pela GPU fornece uma melhor performance. - + ASTC Decoding Method: Método de Decodificação ASTC: - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -799,55 +799,66 @@ stuttering but may present artifacts. - + ASTC Recompression Method: Método de Recompressão ASTC: - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. - + Frame Pacing Mode (Vulkan only) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. - + VRAM Usage Mode: Modo de Uso da VRAM: - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. - + Skip CPU Inner Invalidation - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. - + + Anti-Flicker + + + + + Forces GPU fence callbacks to wait for submitted GPU work. +Use with Fast GPU mode, to avoid flicker with lower performance impact. + + + + VSync Mode: Modo de Sincronização vertical: - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -855,1362 +866,1382 @@ Immediate (no synchronization) presents whatever is available and can exhibit te - + Sync Memory Operations - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. - + Enable asynchronous presentation (Vulkan only) Ativar apresentação assíncrona (Somente Vulkan) - + Slightly improves performance by moving presentation to a separate CPU thread. Melhora ligeiramente o desempenho ao mover a apresentação para uma thread de CPU separada. - + Force maximum clocks (Vulkan only) Forçar clock máximo (somente Vulkan) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. Executa trabalho em segundo plano aguardando pelos comandos gráficos para evitar a GPU de reduzir seu clock. - + Anisotropic Filtering: Filtro Anisotrópico: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. - + GPU Mode: - + Controls the GPU emulation mode. Most games render fine with Fast or Balanced modes, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. - + DMA Accuracy: - + Controls the DMA precision accuracy. Safe precision fixes issues in some games but may degrade performance. - + Enable asynchronous shader compilation - + May reduce shader stutter. - + Fast GPU Time - + Overclocks the emulated GPU to increase dynamic resolution and render distance. Use 256 for maximal performance and 512 for maximal graphics fidelity. - + GPU Unswizzle - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. - + GPU Unswizzle Max Texture Size - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. - + GPU Unswizzle Stream Size - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. - + GPU Unswizzle Chunk Size - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. - + Use Vulkan pipeline cache Utilizar cache de pipeline do Vulkan - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. Habilita o cache de pipeline da fabricante da GPU. Esta opção pode melhorar o tempo de carregamento de shaders significantemente em casos onde o driver Vulkan não armazena o cache de pipeline internamente. - + Enable Compute Pipelines (Intel Vulkan Only) Habilitar Pipeline de Computação (Somente Intel Vulkan) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. - + Enable Reactive Flushing Habilitar Flushing Reativo - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. Usa flushing reativo ao invés de flushing preditivo, permitindo mais precisão na sincronização da memória. - + Sync to framerate of video playback Sincronizar com o framerate da reprodução de vídeo - + Run the game at normal speed during video playback, even when the framerate is unlocked. Executa o jogo na velocidade normal durante a reprodução de vídeo, mesmo se o framerate estiver desbloqueado. - + Barrier feedback loops Ciclos de feedback de barreira - + Improves rendering of transparency effects in specific games. Melhora a renderização de efeitos de transparência em jogos específicos. - + Enable buffer history - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. - + Fix bloom effects - + Removes bloom in Burnout. - + Enable Legacy Rescale Pass - + May fix rescale issues in some games by relying on behavior from the previous implementation. Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. - + Extended Dynamic State - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. - + Vertex Input Dynamic State - + Enables vertex input dynamic state feature for better quality and performance. - + Sample Shading - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. - + RNG Seed Semente de RNG - + Controls the seed of the random number generator. Mainly used for speedrunning. - + Device Name Nome do Dispositivo - + The name of the console. - + + Homebrew Args + + + + + Command-line arguments passed to homebrew at launch (e.g. -noglsl). + + + + Custom RTC Date: Data personalizada do RTC: - + This option allows to change the clock of the console. Can be used to manipulate time in games. - + The number of seconds from the current unix time - + Language: Idioma: - + This option can be overridden when region setting is auto-select - + Region: Região: - + The region of the console. - + Time Zone: Fuso Horário: - + The time zone of the console. - + Sound Output Mode: Modo de saída de som - + Console Mode: Modo Console: - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. - + Prompt for user profile on boot - + Useful if multiple people use the same PC. - + Pause when not in focus - + Pauses emulation when focusing on other windows. - + Confirm before stopping emulation Confirmar antes de parar a emulação - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. - + Hide mouse on inactivity Esconder rato quando inactivo. - + Hides the mouse after 2.5s of inactivity. - + Disable controller applet Desabilitar miniaplicativo de controle - + Forcibly disables the use of the controller applet in emulated programs. When a program attempts to open the controller applet, it is immediately closed. - + Check for updates - + Whether or not to check for updates upon startup. - + Enable Gamemode Habilitar Gamemode - + Force X11 as Graphics Backend - + Custom frontend Frontend customizado - + Real applet Miniaplicativo real - + Never - + On Load - + Always - + CPU CPU - + GPU GPU - + CPU Asynchronous CPU Assíncrona - + Uncompressed (Best quality) Descompactado (Melhor Q - + BC1 (Low quality) BC1 (Baixa qualidade) - + BC3 (Medium quality) BC3 (Média qualidade) - - + + Auto Automático - + 30 FPS - + 60 FPS - + 90 FPS - + 120 FPS - + Conservative Conservador - + Aggressive Agressivo - + Vulkan Vulcano - + OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) - + Null Nenhum (desativado) - + Fast - + Balanced - - + + Accurate Preciso - - + + Default Padrão - + Unsafe (fast) - + Safe (stable) - + Unsafe Inseguro - + Paranoid (disables most optimizations) Paranoia (desativa a maioria das otimizações) - + Debugging - + Dynarmic Dynarmic - + NCE NCE - + Borderless Windowed Janela sem bordas - + Exclusive Fullscreen Tela cheia exclusiva - + No Video Output Sem saída de vídeo - + CPU Video Decoding Decodificação de vídeo pela CPU - + GPU Video Decoding (Default) Decodificação de vídeo pela GPU (Padrão) - + 0.25X (180p/270p) [EXPERIMENTAL] - + 0.5X (360p/540p) [EXPERIMENTAL] 0.5X (360p/540p) [EXPERIMENTAL] - + 0.75X (540p/810p) [EXPERIMENTAL] 0.75X (540p/810p) [EXPERIMENTAL] - + 1X (720p/1080p) 1X (720p/1080p) - + 1.25X (900p/1350p) [EXPERIMENTAL] - + 1.5X (1080p/1620p) [EXPERIMENTAL] 1.5X (1080p/1620p) [EXPERIMENTAL] - + 2X (1440p/2160p) 2X (1440p/2160p) - + 3X (2160p/3240p) 3X (2160p/3240p) - + 4X (2880p/4320p) 4X (2880p/4320p) - + 5X (3600p/5400p) 5X (3600p/5400p) - + 6X (4320p/6480p) 6X (4320p/6480p) - + 7X (5040p/7560p) 7X (5040p/7560p) - + 8X (5760p/8640p) 8X (5760p/8640p) - + Nearest Neighbor Vizinho mais próximo - + Bilinear Bilinear - + Bicubic Bicúbico - + Gaussian Gaussiano - + Lanczos - + ScaleForce ScaleForce - + AMD FidelityFX Super Resolution - + Area - + MMPX - + Zero-Tangent - + B-Spline - + Mitchell - + Spline-1 - - + + Snapdragon Game Super Resolution + + + + + Snapdragon Game Super Resolution EdgeDir + + + + + None Nenhum - + FXAA FXAA - + SMAA SMAA - + Default (16:9) Padrão (16:9) - + Force 4:3 Forçar 4:3 - + Force 21:9 Forçar 21:9 - + Force 16:10 Forçar 16:10 - + Stretch to Window Esticar à Janela - + Automatic Automático - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x - + 32x - + 64x - + Japanese (日本語) Japonês (日本語) - + American English Inglês Americano - + French (français) Francês (français) - + German (Deutsch) Alemão (Deutsch) - + Italian (italiano) Italiano (italiano) - + Spanish (español) Espanhol (español) - + Chinese Chinês - + Korean (한국어) Coreano (한국어) - + Dutch (Nederlands) Holandês (Nederlands) - + Portuguese (português) Português (português) - + Russian (Русский) Russo (Русский) - + Taiwanese Taiwanês - + British English Inglês Britânico - + Canadian French Francês Canadense - + Latin American Spanish Espanhol Latino-Americano - + Simplified Chinese Chinês Simplificado - + Traditional Chinese (正體中文) Chinês Tradicional (正 體 中文) - + Brazilian Portuguese (português do Brasil) Português do Brasil (Brazilian Portuguese) - + Polish (polska) - + Thai (แบบไทย) - - + + Japan Japão - + USA EUA - + Europe Europa - + Australia Austrália - + China China - + Korea Coreia - + Taiwan Taiwan - + Auto (%1) Auto select time zone Auto (%1) - + Default (%1) Default time zone Padrão (%1) - + CET CET - + CST6CDT CST6CDT - + Cuba Cuba - + EET EET - + Egypt Egipto - + Eire Irlanda - + EST EST - + EST5EDT EST5EDT - + GB GB - + GB-Eire GB-Irlanda - + GMT GMT - + GMT+0 GMT+0 - + GMT-0 GMT-0 - + GMT0 GMT0 - + Greenwich Greenwich - + Hongkong Hongkong - + HST HST - + Iceland Islândia - + Iran Irão - + Israel Israel - + Jamaica Jamaica - + Kwajalein Kwajalein - + Libya Líbia - + MET MET - + MST MST - + MST7MDT MST7MDT - + Navajo Navajo - + NZ NZ - + NZ-CHAT NZ-CHAT - + Poland Polónia - + Portugal Portugal - + PRC PRC - + PST8PDT PST8PDT - + ROC ROC - + ROK ROK - + Singapore Singapura - + Turkey Turquia - + UCT UCT - + Universal Universal - + UTC UTC - + W-SU W-SU - + WET WET - + Zulu Zulu - + Mono Mono - + Stereo Estéreo - + Surround Surround - + 4GB DRAM (Default) 4GB DRAM (Padrão) - + 6GB DRAM (Unsafe) 6GB DRAM (Não seguro) - + 8GB DRAM - + 10GB DRAM (Unsafe) - + 12GB DRAM (Unsafe) - + Docked Ancorado - + Handheld Portátil - - + + Off - + Boost (1700MHz) - + Fast (2000MHz) - + Always ask (Default) Sempre perguntar (Padrão) - + Only if game specifies not to stop Somente se o jogo especificar para não parar - + Never ask Nunca perguntar - - + + Medium (256) - - + + High (512) - + Very Small (16 MB) - + Small (32 MB) - + Normal (128 MB) - + Large (256 MB) - + Very Large (512 MB) - + Very Low (4 MB) - + Low (8 MB) - + Normal (16 MB) - + Medium (32 MB) - + High (64 MB) - + Very Low (32) - + Low (64) - + Normal (128) - + Disabled - + ExtendedDynamicState 1 - + ExtendedDynamicState 2 - + ExtendedDynamicState 3 - + Tree View - + Grid View @@ -3257,33 +3288,33 @@ Would you like to delete the old save data? Cor de fundo: - + % - FSR sharpening percentage (e.g. 50%) - % + FSR/SGSR sharpening percentage (e.g. 50%) + - + Off Desligado - + VSync Off Sincronização vertical desligada - + Recommended Recomendado - + On Ligado - + VSync On Sincronização vertical ligada @@ -4412,7 +4443,7 @@ Para inverter os eixos, mova o seu analógico primeiro verticalmente e depois ho - + Configure Configurar @@ -4443,7 +4474,7 @@ Para inverter os eixos, mova o seu analógico primeiro verticalmente e depois ho - + Test Testar @@ -4458,77 +4489,77 @@ Para inverter os eixos, mova o seu analógico primeiro verticalmente e depois ho Remover Servidor - + %1:%2 %1:%2 - - - - - - + + + + + + Eden - + Port number has invalid characters O número da porta tem caracteres inválidos - + Port has to be in range 0 and 65353 A porta tem que estar entre 0 e 65353 - + IP address is not valid O endereço IP não é válido - + This UDP server already exists Este servidor UDP já existe - + Unable to add more than 8 servers Não é possível adicionar mais de 8 servidores - + Testing Testando - + Configuring Configurando - + Test Successful Teste Bem-Sucedido - + Successfully received data from the server. Dados recebidos do servidor com êxito. - + Test Failed Teste Falhou - + Could not receive valid data from the server.<br>Please verify that the server is set up correctly and the address and port are correct. Não foi possível receber dados válidos do servidor.<br>Por favor verifica que o servidor está configurado correctamente e o endereço e porta estão correctos. - + UDP Test or calibration configuration is in progress.<br>Please wait for them to finish. Teste UDP ou configuração de calibragem em progresso.<br> Por favor espera que termine. @@ -4713,57 +4744,57 @@ Os valores atuais são %1% e %2% respectivamente. Algumas configurações só estão disponíveis apenas quando não houver nenhum jogo em execução. - + Add-Ons Add-Ons - + System Sistema - + CPU CPU - + Graphics Gráficos - + Adv. Graphics Gráficos Avç. - + Ext. Graphics - + Audio Audio - + Input Profiles Perfis de controle - + Network - + Applets - + Properties Propriedades @@ -5774,7 +5805,7 @@ Arrasta os pontos para mudar a posição, ou dá duplo-clique nas células da ta - + Calculating... @@ -5976,50 +6007,50 @@ Please go to Configure -> System -> Network and make a selection. GRenderWindow - - + + OpenGL not available! OpenGL não está disponível! - + OpenGL shared contexts are not supported. Shared contexts do OpenGL não são suportados. - + Eden has not been compiled with OpenGL support. - - - + + + Error while initializing OpenGL! Erro ao inicializar OpenGL! - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. O seu GPU pode não suportar OpenGL, ou não tem os drivers gráficos mais recentes. - + Error while initializing OpenGL 4.6! Erro ao inicializar o OpenGL 4.6! - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 O teu GPU pode não suportar OpenGL 4.6, ou não tem os drivers gráficos mais recentes. - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 Sua GPU pode não suportar uma ou mais extensões necessárias do OpenGL. Verifique se você possui a última versão dos drivers gráficos.<br><br>Renderizador GL:<br>%1<br><br>Extensões não suportadas:<br>%2 - + This build doesn't have OpenGL support. @@ -6027,279 +6058,282 @@ Please go to Configure -> System -> Network and make a selection. GameList - + &Add New Game Directory - + Favorite Favorito - + Start Game Iniciar jogo - + Start Game without Custom Configuration Iniciar jogo sem configuração personalizada - + Open Save Data Location Abrir Localização de Dados Salvos - + Open Mod Data Location Abrir a Localização de Dados do Mod - + Open Transferable Pipeline Cache Abrir cache de pipeline transferível - + Link to Ryujinx - + Remove Remover - + Remove Installed Update Remover Actualizações Instaladas - + Remove All Installed DLC Remover Todos os DLC Instalados - + Remove Custom Configuration Remover Configuração Personalizada - + Remove Cache Storage Remove a Cache do Armazenamento - + Remove OpenGL Pipeline Cache Remover cache de pipeline do OpenGL - + Remove Vulkan Pipeline Cache Remover cache de pipeline do Vulkan - + Remove All Pipeline Caches Remover todos os caches de pipeline - + Remove All Installed Contents Remover Todos os Conteúdos Instalados - + Manage Play Time - + Edit Play Time Data - + Remove Play Time Data Remover dados de tempo jogado - - + + Dump RomFS Despejar RomFS - + Dump RomFS to SDMC Extrair RomFS para SDMC - + Verify Integrity Verificar integridade - + Copy Title ID to Clipboard Copiar título de ID para a área de transferência - + Navigate to GameDB entry Navegue para a Entrada da Base de Dados de Jogos - + Create Shortcut Criar Atalho - + Add to Desktop Adicionar à Área de Trabalho - + Add to Applications Menu Adicionar ao Menu de Aplicativos - + Configure Game - + Scan Subfolders Examinar Sub-pastas - + Remove Game Directory Remover diretório do Jogo - + ▲ Move Up ▲ Mover para Cima - + ▼ Move Down ▼ Mover para Baixo - + Open Directory Location Abrir Localização do diretório - + Clear Limpar - - - Name - Nome - - - - Compatibility - Compatibilidade - - - - Add-ons - Add-ons - - - - File type - Tipo de Arquivo - - - - Size - Tamanho - - - - Play time - Tempo jogado - GameListItemCompat - + Ingame Não Jogável - + Game starts, but crashes or major glitches prevent it from being completed. O jogo inicia, porém problemas ou grandes falhas impedem que ele seja concluído. - + Perfect Perfeito - + Game can be played without issues. O jogo pode ser jogado sem problemas. - + Playable Jogável - + Game functions with minor graphical or audio glitches and is playable from start to finish. O jogo funciona com pequenas falhas gráficas ou de áudio e pode ser reproduzido do início ao fim. - + Intro/Menu Introdução / Menu - + Game loads, but is unable to progress past the Start Screen. O jogo carrega, porém não consegue passar da tela inicial. - + Won't Boot Não Inicia - + The game crashes when attempting to startup. O jogo trava ao tentar iniciar. - + Not Tested Não Testado - + The game has not yet been tested. O jogo ainda não foi testado. + + GameListModel + + + Name + + + + + Compatibility + + + + + Add-ons + + + + + File type + + + + + Size + + + + + Play time + + + GameListPlaceholder - + Double-click to add a new folder to the game list Clique duas vezes para adicionar uma nova pasta à lista de jogos @@ -6307,17 +6341,17 @@ Please go to Configure -> System -> Network and make a selection. GameListSearchField - + %1 of %n result(s) - + Filter: Filtro: - + Enter pattern to filter Digite o padrão para filtrar @@ -6812,1139 +6846,1099 @@ Debug Message: - + Game &Icon Size - + Reset Window Size to &720p Restaurar tamanho da janela para &720p - + Reset Window Size to 720p Restaurar tamanho da janela para 720p - + Reset Window Size to &900p Restaurar tamanho da janela para &900p - + Reset Window Size to 900p Restaurar tamanho da janela para 900p - + Reset Window Size to &1080p Restaurar tamanho da janela para &1080p - + Reset Window Size to 1080p Restaurar tamanho da janela para 1080p - + &Multiplayer &Multijogador - + &Tools &Ferramentas - + Am&iibo - + Launch &Applet - + &TAS &TAS - + &Create Home Menu Shortcut - + Install &Firmware - + &Help &Ajuda - + &Install Files to NAND... &Instalar arquivos na NAND... - + L&oad File... C&arregar arquivo... - + Load &Folder... Carregar &pasta... - + E&xit &Sair - - + + &Pause &Pausa - + &Stop &Parar - + &Verify Installed Contents &Verificar conteúdo instalado - + &About Eden - + Single &Window Mode Modo de &janela única - + Con&figure... Con&figurar... - + Ctrl+, - + Enable Overlay Display Applet - + Show &Filter Bar Mostrar Barra de &Filtros - + Show &Status Bar Mostrar Barra de &Estado - + Show Status Bar Mostrar Barra de Estado - + &Browse Public Game Lobby &Navegar no Lobby de Salas Públicas - + &Create Room &Criar Sala - + &Leave Room &Sair da Sala - + &Direct Connect to Room Conectar &Diretamente Numa Sala - + &Show Current Room Exibir &Sala Atual - + F&ullscreen T&ela cheia - + &Restart &Reiniciar - + Load/Remove &Amiibo... Carregar/Remover &Amiibo... - + &Report Compatibility &Reportar compatibilidade - + Open &Mods Page Abrir Página de &Mods - + Open &Quickstart Guide Abrir &guia de início rápido - + &FAQ &Perguntas frequentes - + &Capture Screenshot &Captura de Tela - + &Album - + &Set Nickname and Owner &Definir apelido e proprietário - + &Delete Game Data &Remover dados do jogo - + &Restore Amiibo &Recuperar Amiibo - + &Format Amiibo &Formatar Amiibo - + &Mii Editor - + &Configure TAS... &Configurar TAS - + Configure C&urrent Game... Configurar jogo atual... - - + + &Start &Começar - + &Reset &Restaurar - - + + R&ecord G&ravar - + Open &Controller Menu Menu Abrir &Controles - + Install Decryption &Keys - + &Home Menu - + &Desktop - + &Application Menu - + &Root Data Folder - + &NAND Folder - + &SDMC Folder - + &Mod Folder - + &Log Folder - + From Folder - + From ZIP - + &Eden Dependencies - + &Data Manager - + &Tree View - + &Grid View - + Game Icon Size - - + + None - + Show Game &Name - + Show &Performance Overlay - + + &Carousel View + + + + Small (32x32) - + Standard (64x64) - + Large (128x128) - + Full Size (256x256) - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping - + Loading Web Applet... - - + + Disable Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. - + Unmute - + Mute - + Reset Volume - + &Clear Recent Files - + &Continue - + Warning: Outdated Game Format - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. - - + + Error while loading ROM! - + The ROM format is not supported. - + An error occurred initializing the video core. - + Eden has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-mirror.github.io/help/reference/log-files/'>How to Upload the Log File</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. - + (64-bit) - + (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit - + Closing software... - + Save Data - + Mod Data - + Error Opening %1 Folder - - + + Folder does not exist! - + Remove Installed Game Contents? - + Remove Installed Game Update? - + Remove Installed Game DLC? - + Remove Entry - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? - + Remove Cache Storage? - + Remove File - + Remove Play Time Data - + Reset play time? - - + + RomFS Extraction Failed! - + There was an error copying the RomFS files or the user cancelled the operation. - + Full - + Skeleton - + Select RomFS Dump Mode - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... - - + + Cancel - + RomFS Extraction Succeeded! - + The operation completed successfully. - + Error Opening %1 - + Select Directory - + Properties - + The game properties could not be loaded. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. - + Load File - + Open Extracted ROM Directory - + Invalid Directory Selected - + The directory you have selected does not contain a 'main' file. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files - + %n file(s) remaining - + Installing file "%1"... - - + + Install Results - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application - + System Archive - + System Application Update - + Firmware Package (Type A) - + Firmware Package (Type B) - + Game - + Game Update - + Game DLC - + Delta Title - + Select NCA Install Type... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) - + Failed to Install - + The title type you selected for the NCA is invalid. - + File not found - + File "%1" not found - + OK - + Function Disabled - + Compatibility list reporting is currently disabled. Check back later! - + Error opening URL - + Unable to open the URL "%1". - + TAS Recording - + Overwrite file of player 1? - + Invalid config detected - + Handheld controller can't be used on docked mode. Pro controller will be selected. - - + + Amiibo - - + + The current amiibo has been removed - + Error - - + + The current game is not looking for amiibos - + Amiibo File (%1);; All Files (*.*) - + Load Amiibo - + Error loading Amiibo data - + The selected file is not a valid amiibo - + The selected file is already on use - + An unknown error occurred - - - Keys not installed - - - - - - Install decryption keys and restart Eden before attempting to install firmware. - - - - - Select Dumped Firmware Source Location - - - - - Select Dumped Firmware ZIP - - - - - Zipped Archives (*.zip) - - - - - Firmware cleanup failed - - - - - Failed to clean up extracted firmware cache. -Check write permissions in the system temp directory and try again. -OS reported error: %1 - - - - + No firmware available - + Firmware Corrupted - + Unknown applet - + Applet doesn't map to a known value. - + Record not found - + Applet not found. Please reinstall firmware. - + Capture Screenshot - + PNG Image (*.png) - + TAS state: Running %1/%2 - + TAS state: Recording %1 - + TAS state: Idle %1/%2 - + TAS State: Invalid - + &Stop Running - + Stop R&ecording - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor - + Speed: %1% / %2% - + Speed: %1% - + Game: %1 FPS - + Frame: %1 ms - - - FSR - - - - + NO AA - + VOLUME: MUTE - + VOLUME: %1% Volume percentage (e.g. 50%) - + Derivation Components Missing - + Decryption keys are missing. Install them now? - + Wayland Detected! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -7952,74 +7946,74 @@ Would you like to force it for future launches? - + Use X11 - + Continue with Wayland - + Don't show again - + Restart Required - + Restart Eden to apply the X11 backend. - + Slow - + Turbo - + Unlocked - + Select RomFS Dump Target - + Please select which RomFS you would like to dump. - + Are you sure you want to close Eden? - - - + + + Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? @@ -8085,6 +8079,11 @@ Would you like to bypass this and exit anyway? ScaleForce + + + FSR + + Area @@ -8096,52 +8095,62 @@ Would you like to bypass this and exit anyway? - + + SGSR + + + + + SGSR EdgeDir + + + + Docked - + Handheld - + Fast - + Balanced - + Accurate - + Vulkan - + OpenGL GLSL - + OpenGL SPIRV - + OpenGL GLASM - + Null @@ -8174,7 +8183,7 @@ If you wish to clean up the files which were left in the old data location, you - + Data was migrated successfully. @@ -9029,47 +9038,47 @@ p, li { white-space: pre-wrap; } %1 está jogando %2 - + Play Time: %1 - + Never Played - + Version: %1 - + Version: 1.0.0 - + Installed SD Titles Títulos SD instalados - + Installed NAND Titles Títulos NAND instalados - + System Titles Títulos do sistema - + Add New Game Directory Adicionar novo diretório de jogos - + Favorites Favoritos @@ -9190,253 +9199,286 @@ p, li { white-space: pre-wrap; } QtCommon::Content - + Game Requires Firmware - + The game you are trying to launch requires firmware to boot or to get past the opening menu. Please <a href='https://yuzu-mirror.github.io/help/quickstart'>dump and install firmware</a>, or press "OK" to launch anyways. - + Installing Firmware... - - - - - + + + + + Cancel - + Firmware Install Failed - + Firmware Install Succeeded - + Firmware integrity verification failed! - - + + Verification failed for the following files: %1 - - + + Verifying integrity... + + + + Integrity verification succeeded! + + - Integrity verification succeeded! - - - - - The operation completed successfully. - - + + Integrity verification failed! - + File contents may be corrupt or missing. - + Integrity verification couldn't be performed - + Firmware installation cancelled, firmware may be in a bad state or corrupted. File contents could not be checked for validity. - + Select Dumped Keys Location - + Decryption Keys install succeeded - + Decryption Keys install failed - + Orphaned Profiles Detected! - + UNEXPECTED BAD THINGS MAY HAPPEN IF YOU DON'T READ THIS!<br>Eden has detected the following save directories with no attached profile:<br>%1<br><br>The following profiles are valid:<br>%2<br><br>Click "OK" to open your save folder and fix up your profiles.<br>Hint: copy the contents of the largest or last-modified folder elsewhere, delete all orphaned profiles, and move your copied contents to the good profile.<br><br>Still confused? See the <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/Orphaned.md'>help page</a>.<br> - + Really clear data? - + Important data may be lost! - + Are you REALLY sure? - + Once deleted, your data will NOT come back! Only do this if you're 100% sure you want to delete this data. - + Clearing... - + Select Export Location - + %1.zip - - + + + Zipped Archives (*.zip) - + Exporting data. This may take a while... - + Exporting - + Exported Successfully - + Data was exported successfully. - + Export Cancelled - + Export was cancelled by the user. - + Export Failed - + Ensure you have write permissions on the targeted directory and try again. - + Select Import Location - + Import Warning - + All previous data in this directory will be deleted. Are you sure you wish to proceed? - + Importing data. This may take a while... - + Importing - + Imported Successfully - + Data was imported successfully. - + Import Cancelled - + Import was cancelled by the user. - + Import Failed - + Ensure you have read permissions on the targeted directory and try again. + + + Keys not installed + + + + + Install decryption keys and restart Eden before attempting to install firmware. + + + + + Select Dumped Firmware Source Location + + + + + Select Dumped Firmware ZIP + + + + + Firmware cleanup failed + + + + + Failed to clean up extracted firmware cache. +Check write permissions in the system temp directory and try again. +OS reported error: %1 + + QtCommon::FS @@ -9680,72 +9722,72 @@ Would you like to manually select a portable folder to use? - + Create Shortcut - + Do you want to launch the game in fullscreen? - + Shortcut Created - + Successfully created a shortcut to %1 - + Shortcut may be Volatile! - + This will create a shortcut to the current AppImage. This may not work well if you update. Continue? - + Failed to Create Shortcut - + Failed to create a shortcut to %1 - + Create Icon - + Cannot create icon file. Path "%1" does not exist and cannot be created. - + No firmware available - + Please install firmware to use the home menu. - + Home Menu Applet - + Home Menu is not available. Please reinstall firmware. @@ -9753,55 +9795,55 @@ Would you like to manually select a portable folder to use? QtCommon::Mod - + Mod Name - + What should this mod be called? - + RomFS - + ExeFS/Patch - + Cheat - + Mod Type - + Could not detect mod type automatically. Please manually specify the type of mod you downloaded. Most mods are RomFS mods, but patches (.pchtxt) are typically ExeFS mods. - - + + Mod Extract Failed - + Failed to create temporary directory %1 - + Zip file %1 is empty @@ -10495,65 +10537,65 @@ By selecting "From Eden", previous save data stored in Ryujinx will be - + New Version Location - + All Files (*.*) - - - + + + Failed to save file - + Could not open file %1 for writing. - + Downloading... - + Cancel - + Could not write to file %1. - + Could not commit to file %1. - + Failed to download file - + Could not download from %1%2 Error code: %3 - + Download Complete - + Successfully downloaded %1. Would you like to open it? diff --git a/dist/languages/ru_RU.ts b/dist/languages/ru_RU.ts index 063804860b..b1d1c25fc6 100644 --- a/dist/languages/ru_RU.ts +++ b/dist/languages/ru_RU.ts @@ -723,8 +723,8 @@ Options lower than 1X can cause artifacts. - Determines how sharpened the image will look using FSR's dynamic contrast. - Определяет степень резкости изображения при использовании динамического контраста FSR. + Determines how sharpened the image will look using FSR's or SGSR's dynamic contrast. + Определяет степень резкости изображения при использовании динамического контраста FSR или SGSR. @@ -780,24 +780,24 @@ Disabling it is only intended for debugging. Позволяет сохранять шейдеры на диск для более быстрой загрузки при последующем запуске игры. Отключение этой функции предназначено только для отладки. - + Use asynchronous GPU emulation Использовать асинхронную эмуляцию GPU - + Uses an extra CPU thread for rendering. This option should always remain enabled. Использует дополнительный поток CPU для рендеринга. Эта опция всегда должна оставаться включенной. - + NVDEC emulation: Эмуляция NVDEC: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. @@ -806,12 +806,12 @@ In most cases, GPU decoding provides the best performance. В большинстве случаев декодирование с использованием ГП обеспечивает лучшую производительность. - + ASTC Decoding Method: Метод декодирования ASTC: - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -823,12 +823,12 @@ GPU: использовать вычислительные шейдеры ГП Асинхронный CPU: использовать ЦП для декодирования текстур ASTC по мере необходимости. Устраняет фризы при декодировании ASTC, но может вызывать артефакты. - + ASTC Recompression Method: Метод пересжатия ASTC: - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. @@ -836,44 +836,56 @@ BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, BC1/BC3: промежуточный формат будет перекодирован в BC1 или BC3, экономя видеопамять, но снижая качество изображения. - + Frame Pacing Mode (Vulkan only) Режим стабилизации кадров (только Vulkan) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. Определяет способ управления выводом кадров для уменьшения подергиваний и повышения плавности и стабильности частоты кадров. - + VRAM Usage Mode: Режим VRAM: - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. Определяет, должен ли эмулятор экономить память или использовать всю доступную видеопамять для повышения производительности. Агрессивный режим может негативно сказаться на производительности других приложений, например программ записи. - + Skip CPU Inner Invalidation Пропуск внутренней инвалидизации CPU - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. Пропускает некоторые операции сброса кэша при обновлении памяти, снижая нагрузку на ЦП и уменьшая задержку. Может вызывать сбои в работе игр. - + + Anti-Flicker + Анти-мерцание + + + + Forces GPU fence callbacks to wait for submitted GPU work. +Use with Fast GPU mode, to avoid flicker with lower performance impact. + Заставляет обратные вызовы GPU-синхронизации ожидать завершения отправленных задач GPU. +Используйте вместе с режимом «Производительность» (Fast) для устранения мерцания с меньшим влиянием на производительность. + + + VSync Mode: Режим VSync: - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -884,12 +896,12 @@ Mailbox может обеспечивать меньшую задержку, ч Immediate (без синхронизации) выводит все доступные кадры и может иметь разрывы. - + Sync Memory Operations Синхронизация операций памяти - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. @@ -898,102 +910,102 @@ Unreal Engine 4 games often see the most significant changes thereof. Игры на Unreal Engine 4 чаще всего демонстрируют заметные изменения. - + Enable asynchronous presentation (Vulkan only) Включить асинхронный вывод (только Vulkan) - + Slightly improves performance by moving presentation to a separate CPU thread. Немного повышает производительность, перенося вывод кадра в отдельный поток CPU. - + Force maximum clocks (Vulkan only) Принудительно зафиксировать максимальную тактовую частоту (только для Vulkan) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. Выполняет фоновые операции во время ожидания графических команд, предотвращая снижение тактовой частоты GPU. - + Anisotropic Filtering: Анизотропная фильтрация: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. Управляет качеством отображения текстур под наклонными углами. На большинстве GPU безопасно использовать значение 16x. - + GPU Mode: Режим GPU: - + Controls the GPU emulation mode. Most games render fine with Fast or Balanced modes, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. Управляет режимами эмуляции GPU. -Большинство игр работают без проблем в режимах «Производительность» или «Баланс», но для некоторых нужна «Точность». +Большинство игр работают без проблем в режимах «Производительность» (Fast) или «Баланс» (Balanced), но для некоторых нужна «Точность» (Accurate). Эффекты частиц обычно корректны только в режиме «Точность». - + DMA Accuracy: Точность DMA: - + Controls the DMA precision accuracy. Safe precision fixes issues in some games but may degrade performance. Управляет точностью DMA. Безопасный режим исправляет ошибки в некоторых играх, но может снижать производительность. - + Enable asynchronous shader compilation Включить асинхронную компиляцию шейдеров - + May reduce shader stutter. Может уменьшить фризы, вызванные компиляцией шейдеров. - + Fast GPU Time Fast GPU Time - + Overclocks the emulated GPU to increase dynamic resolution and render distance. Use 256 for maximal performance and 512 for maximal graphics fidelity. Разгоняет эмулируемый GPU для увеличения динамического разрешения и дальности прорисовки. Используйте значение 256 для максимальной производительности и 512 для максимального качества графики. - + GPU Unswizzle GPU-десвиззлинг (GPU Unswizzle) - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. Ускоряет декодирование 3D-текстур BCn с использованием вычислений GPU. Отключите при возникновении сбоев или графических артефактов. - + GPU Unswizzle Max Texture Size Максимальный размер текстур для GPU-десвиззлинга - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. @@ -1002,48 +1014,48 @@ Adjust this to find the balance between GPU acceleration and CPU overhead. - + GPU Unswizzle Stream Size Размер потока GPU-десвиззлинга - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. Задает максимальный объем данных текстур (в МиБ), обрабатываемый за кадр. Более высокие значения могут уменьшить подергивания при загрузке текстур, но это может повлиять на стабильность кадров. - + GPU Unswizzle Chunk Size Размер блока GPU-десвиззлинга - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. Определяет количество слоев глубины, обрабатываемых за один вызов. Увеличение этого значения может повысить пропускную способность на производительных GPU, но на слабом оборудовании способно вызвать TDR или тайм-ауты драйвера. - + Use Vulkan pipeline cache Использовать кэш конвейеров Vulkan - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. Включает кэш конвейеров, зависящий от производителя GPU. Эта опция может значительно ускорить загрузку шейдеров в тех случаях, когда драйвер Vulkan не сохраняет такие файлы кэша внутри себя. - + Enable Compute Pipelines (Intel Vulkan Only) Включить вычислительные конвейеры (только для Intel Vulkan) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. @@ -1052,184 +1064,194 @@ Compute pipelines are always enabled on all other drivers. На остальных драйверах вычислительные конвейеры всегда включены. - + Enable Reactive Flushing Включить реактивный сброс - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. Использует реактивный сброс (Reactive Flushing) вместо прогнозирующего, обеспечивая более точную синхронизацию памяти. - + Sync to framerate of video playback Синхронизировать с частотой кадров видео - + Run the game at normal speed during video playback, even when the framerate is unlocked. Поддерживать нормальную скорость игры во время воспроизведения видео, даже если частота кадров разблокирована. - + Barrier feedback loops Циклы обратной связи барьеров - + Improves rendering of transparency effects in specific games. Улучшает отрисовку эффектов прозрачности в некоторых играх. - + Enable buffer history Включить историю буфера - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. Предоставляет доступ к предыдущим состояниям буфера. В некоторых играх может повысить качество рендеринга и стабильность производительности. - + Fix bloom effects Исправить эффекты bloom - + Removes bloom in Burnout. Удаляет эффект bloom в игре Burnout. - + Enable Legacy Rescale Pass Включить устаревший режим масштабирования - + May fix rescale issues in some games by relying on behavior from the previous implementation. Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. Может исправить проблемы масштабирования в некоторых играх, используя поведение предыдущей реализации. Это обходное решение устраняет артефакты линий на GPU AMD и Intel, а также мерцание серых текстур на GPU Nvidia в Luigis Mansion 3. - + Extended Dynamic State Расширенное динамическое состояние (Extended Dynamic State) - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. Определяет количество функций, доступных в Extended Dynamic State. Более высокие значения позволяют использовать больше функций и иногда повышают производительность, но могут вызвать дополнительные графические проблемы. - + Vertex Input Dynamic State Динамическое состояние ввода вершин (Vertex Input Dynamic State) - + Enables vertex input dynamic state feature for better quality and performance. Включает функцию динамического состояния ввода вершин для улучшения качества и производительности. - + Sample Shading Множественное затенение (Sample Shading) - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. Позволяет фрагментному шейдеру выполняться для каждого сэмпла в мультисемплированном фрагменте вместо одного раза. Повышает качество графики за счет снижения производительности. Более высокие значения повышают качество, но сильнее снижают производительность. - + RNG Seed Сид RNG - + Controls the seed of the random number generator. Mainly used for speedrunning. Задает начальное значение генератора случайных чисел. В основном используется для спидранов. - + Device Name Название устройства - + The name of the console. Имя консоли. - + + Homebrew Args + Аргументы Homebrew + + + + Command-line arguments passed to homebrew at launch (e.g. -noglsl). + Аргументы командной строки при запуске homebrew (например, -noglsl). + + + Custom RTC Date: Пользовательская RTC-дата: - + This option allows to change the clock of the console. Can be used to manipulate time in games. Позволяет изменить системные часы консоли. Может использоваться для изменения времени в играх. - + The number of seconds from the current unix time Количество секунд от текущего UNIX-времени. - + Language: Язык: - + This option can be overridden when region setting is auto-select Эта настройка может быть переопределена при автоматическом выборе региона. - + Region: Регион: - + The region of the console. Регион консоли. - + Time Zone: Часовой пояс: - + The time zone of the console. Часовой пояс консоли. - + Sound Output Mode: Режим вывода звука: - + Console Mode: Консольный режим: - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. @@ -1238,1031 +1260,1041 @@ Setting to Handheld can help improve performance for low end systems. Установка в портативный режим может повысить производительность на слабых системах. - + Prompt for user profile on boot Запрашивать профиль пользователя при запуске игры - + Useful if multiple people use the same PC. Полезно, если одним ПК пользуются несколько человек. - + Pause when not in focus Приостанавливать, если окно не активно - + Pauses emulation when focusing on other windows. Приостанавливает эмуляцию при переключении на другие окна. - + Confirm before stopping emulation Запрашивать подтверждение перед остановкой эмуляции - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. Убирает запросы на подтверждение остановки эмуляции. При включении этой настройки эмуляция завершается сразу, без подтверждения. - + Hide mouse on inactivity Скрывать курсор при бездействии - + Hides the mouse after 2.5s of inactivity. Скрывает курсор мыши после 2,5 секунд бездействия. - + Disable controller applet Отключить апплет контроллера - + Forcibly disables the use of the controller applet in emulated programs. When a program attempts to open the controller applet, it is immediately closed. Принудительно отключает использование апплета контроллера в эмулируемых программах. При попытке открыть апплет он сразу закрывается. - + Check for updates Проверять обновления - + Whether or not to check for updates upon startup. Проверять обновления при запуске. - + Enable Gamemode Включить режим игры - + Force X11 as Graphics Backend Принудительно использовать X11 как графический бэкенд - + Custom frontend Собственный интерфейс эмулятора - + Real applet Приложение из прошивки - + Never Никогда - + On Load При загрузке - + Always Всегда - + CPU CPU - + GPU GPU - + CPU Asynchronous Асинхронный CPU - + Uncompressed (Best quality) Без сжатия (наилучшее качество) - + BC1 (Low quality) BC1 (низкое качество) - + BC3 (Medium quality) BC3 (среднее качество) - - + + Auto Авто - + 30 FPS 30 FPS - + 60 FPS 60 FPS - + 90 FPS 90 FPS - + 120 FPS 120 FPS - + Conservative Консервативный - + Aggressive Агрессивный - + Vulkan Vulkan - + OpenGL GLSL OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) OpenGL GLASM (ассемблерные шейдеры, только для NVIDIA) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) OpenGL SPIR-V (экспериментальный, только для AMD/Mesa) - + Null Null - + Fast Производительность - + Balanced Баланс - - + + Accurate Точность - - + + Default По умолчанию - + Unsafe (fast) Небезопасно (быстро) - + Safe (stable) Безопасно (стабильно) - + Unsafe Небезопасно - + Paranoid (disables most optimizations) Параноик (отключает большинство оптимизаций) - + Debugging Отладка - + Dynarmic Dynarmic - + NCE NCE - + Borderless Windowed Безрамочный режим - + Exclusive Fullscreen Эксклюзивный полноэкранный - + No Video Output Отсутствие видеовыхода - + CPU Video Decoding Декодирование видео на CPU - + GPU Video Decoding (Default) Декодирование видео на GPU (По умолчанию) - + 0.25X (180p/270p) [EXPERIMENTAL] 0.25X (180p/270p) [Экспериментально] - + 0.5X (360p/540p) [EXPERIMENTAL] 0.5X (360p/540p) [Экспериментально] - + 0.75X (540p/810p) [EXPERIMENTAL] 0.75X (540p/810p) [Экспериментально] - + 1X (720p/1080p) 1X (720p/1080p) - + 1.25X (900p/1350p) [EXPERIMENTAL] 1.25X (900p/1350p) [ЭКПЕРИМЕНТАЛЬНО] - + 1.5X (1080p/1620p) [EXPERIMENTAL] 1.5X (1080p/1620p) [Экспериментально] - + 2X (1440p/2160p) 2X (1440p/2160p) - + 3X (2160p/3240p) 3X (2160p/3240p) - + 4X (2880p/4320p) 4X (2880p/4320p) - + 5X (3600p/5400p) 5X (3600p/5400p) - + 6X (4320p/6480p) 6X (4320p/6480p) - + 7X (5040p/7560p) 7X (5040p/7560p) - + 8X (5760p/8640p) 8X (5760p/8640p) - + Nearest Neighbor Ближайший сосед (Nearest Neighbor) - + Bilinear Билинейный - + Bicubic Бикубический - + Gaussian По Гауссу - + Lanczos Lanczos - + ScaleForce ScaleForce - + AMD FidelityFX Super Resolution AMD FidelityFX™️ Super Resolution - + Area Область (Area) - + MMPX MMPX - + Zero-Tangent Zero-Tangent - + B-Spline B-Spline - + Mitchell Mitchell - + Spline-1 Spline-1 - - + + Snapdragon Game Super Resolution + Snapdragon Game Super Resolution + + + + Snapdragon Game Super Resolution EdgeDir + Snapdragon Game Super Resolution EdgeDir + + + + None Никакой - + FXAA FXAA - + SMAA SMAA - + Default (16:9) Стандартное (16:9) - + Force 4:3 Принудительно 4:3 - + Force 21:9 Принудительно 21:9 - + Force 16:10 Принудительно 16:10 - + Stretch to Window Растянуть до окна - + Automatic Автоматически - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x - + 32x 32x - + 64x 64x - + Japanese (日本語) Японский (日本語) - + American English Американский английский - + French (français) Французский (français) - + German (Deutsch) Немецкий (Deutsch) - + Italian (italiano) Итальянский (italiano) - + Spanish (español) Испанский (español) - + Chinese Китайский - + Korean (한국어) Корейский (한국어) - + Dutch (Nederlands) Голландский (Nederlands) - + Portuguese (português) Португальский (português) - + Russian (Русский) Русский - + Taiwanese Тайваньский - + British English Британский английский - + Canadian French Канадский французский - + Latin American Spanish Латиноамериканский испанский - + Simplified Chinese Упрощённый китайский - + Traditional Chinese (正體中文) Традиционный китайский (正體中文) - + Brazilian Portuguese (português do Brasil) Бразильский португальский (português do Brasil) - + Polish (polska) Польский (polska) - + Thai (แบบไทย) Тайский (แบบไทย) - - + + Japan Япония - + USA США - + Europe Европа - + Australia Австралия - + China Китай - + Korea Корея - + Taiwan Тайвань - + Auto (%1) Auto select time zone Авто (%1) - + Default (%1) Default time zone По умолчанию (%1) - + CET CET - + CST6CDT CST6CDT - + Cuba Куба - + EET EET - + Egypt Египет - + Eire Эйре - + EST EST - + EST5EDT EST5EDT - + GB GB - + GB-Eire GB-Эйре - + GMT GMT - + GMT+0 GMT+0 - + GMT-0 GMT-0 - + GMT0 GMT0 - + Greenwich Гринвич - + Hongkong Гонконг - + HST HST - + Iceland Исландия - + Iran Иран - + Israel Израиль - + Jamaica Ямайка - + Kwajalein Кваджалейн - + Libya Ливия - + MET MET - + MST MST - + MST7MDT MST7MDT - + Navajo Навахо - + NZ NZ - + NZ-CHAT NZ-CHAT - + Poland Польша - + Portugal Португалия - + PRC PRC - + PST8PDT PST8PDT - + ROC ROC - + ROK ROK - + Singapore Сингапур - + Turkey Турция - + UCT UCT - + Universal Универсальный - + UTC UTC - + W-SU W-SU - + WET WET - + Zulu Зулу - + Mono Моно - + Stereo Стерео - + Surround Объемный звук - + 4GB DRAM (Default) 4 ГБ ОЗУ (По умолчанию) - + 6GB DRAM (Unsafe) 6 ГБ ОЗУ (Небезопасно) - + 8GB DRAM 8 ГБ ОЗУ - + 10GB DRAM (Unsafe) 10 ГБ ОЗУ (Небезопасно) - + 12GB DRAM (Unsafe) 12 ГБ ОЗУ (Небезопасно) - + Docked В док-станции - + Handheld Портативный - - + + Off Отключено - + Boost (1700MHz) Boost (1700MHz) - + Fast (2000MHz) Fast (2000MHz) - + Always ask (Default) Всегда спрашивать (По умолчанию) - + Only if game specifies not to stop Только если игра указывает не останавливать - + Never ask Никогда не спрашивать - - + + Medium (256) Средний (256) - - + + High (512) Высокий (512) - + Very Small (16 MB) Очень маленький (16 МБ) - + Small (32 MB) Маленький (32 МБ) - + Normal (128 MB) Нормальный (128 МБ) - + Large (256 MB) Большой (256 МБ) - + Very Large (512 MB) Очень большой (512 МБ) - + Very Low (4 MB) Очень низкий (4 МБ) - + Low (8 MB) Низкий (8 МБ) - + Normal (16 MB) Нормальный (16 МБ) - + Medium (32 MB) Средний (32 МБ) - + High (64 MB) Высокий (64 МБ) - + Very Low (32) Очень низкий (32) - + Low (64) Низкий (64) - + Normal (128) Нормальный (128) - + Disabled Отключено - + ExtendedDynamicState 1 ExtendedDynamicState 1 - + ExtendedDynamicState 2 ExtendedDynamicState 2 - + ExtendedDynamicState 3 ExtendedDynamicState 3 - + Tree View Древовидный - + Grid View Сетчатый @@ -3329,33 +3361,33 @@ Would you like to delete the old save data? Цвет фона: - + % - FSR sharpening percentage (e.g. 50%) + FSR/SGSR sharpening percentage (e.g. 50%) % - + Off Отключена - + VSync Off VSync Off - + Recommended Рекомендуется - + On Включена - + VSync On VSync On @@ -4484,7 +4516,7 @@ To invert the axes, first move your joystick vertically, and then horizontally.< - + Configure Настроить @@ -4515,7 +4547,7 @@ To invert the axes, first move your joystick vertically, and then horizontally.< - + Test Тест @@ -4530,77 +4562,77 @@ To invert the axes, first move your joystick vertically, and then horizontally.< Удалить сервер - + %1:%2 %1:%2 - - - - - - + + + + + + Eden Eden - + Port number has invalid characters Номер порта содержит недопустимые символы - + Port has to be in range 0 and 65353 Порт должен быть в районе от 0 до 65353 - + IP address is not valid IP-адрес недействителен - + This UDP server already exists Этот UDP сервер уже существует - + Unable to add more than 8 servers Невозможно добавить более 8 серверов - + Testing Тестирование - + Configuring Настройка - + Test Successful Тест успешен - + Successfully received data from the server. Успешно получена информация с сервера - + Test Failed Тест провален - + Could not receive valid data from the server.<br>Please verify that the server is set up correctly and the address and port are correct. Не удалось получить действительные данные с сервера.<br>Убедитесь, что сервер правильно настроен, а также проверьте адрес и порт. - + UDP Test or calibration configuration is in progress.<br>Please wait for them to finish. Тест UDP или калибрация в процессе.<br>Пожалуйста, подождите завершения. @@ -4785,57 +4817,57 @@ Current values are %1% and %2% respectively. Некоторые настройки доступны только тогда, когда игра не запущена. - + Add-Ons Дополнения - + System Система - + CPU ЦП - + Graphics Графика - + Adv. Graphics Расш. графика - + Ext. Graphics Доп. графика - + Audio Звук - + Input Profiles Профили управления - + Network Сеть - + Applets Апплеты - + Properties Параметры игры @@ -5852,7 +5884,7 @@ Drag points to change position, or double-click table cells to edit values.Импортирует данные для этого каталога. Это может занять некоторое время и приведет к удалению ВСЕХ СУЩЕСТВУЮЩИХ ДАННЫХ! - + Calculating... Подсчитываем... @@ -6056,50 +6088,50 @@ Please go to Configure -> System -> Network and make a selection. GRenderWindow - - + + OpenGL not available! OpenGL не доступен! - + OpenGL shared contexts are not supported. Общие контексты OpenGL не поддерживаются. - + Eden has not been compiled with OpenGL support. Eden не был скомпилирован с поддержкой OpenGL. - - - + + + Error while initializing OpenGL! Ошибка при инициализации OpenGL! - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. Ваш ГП может не поддерживать OpenGL, или у вас установлен устаревший графический драйвер. - + Error while initializing OpenGL 4.6! Ошибка при инициализации OpenGL 4.6! - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 Ваш ГП может не поддерживать OpenGL 4.6, или у вас установлен устаревший графический драйвер.<br><br>Рендерер GL:<br>%1 - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 Ваш ГП может не поддерживать одно или несколько требуемых расширений OpenGL. Пожалуйста, убедитесь в том, что у вас установлен последний графический драйвер.<br><br>Рендерер GL:<br>%1<br><br>Неподдерживаемые расширения:<br>%2 - + This build doesn't have OpenGL support. В этой сборке отсутствует поддержка OpenGL. @@ -6107,279 +6139,282 @@ Please go to Configure -> System -> Network and make a selection. GameList - + &Add New Game Directory &Добавить новую папку с играми - + Favorite Избранное - + Start Game Запустить игру - + Start Game without Custom Configuration Запустить игру без пользовательской настройки - + Open Save Data Location Открыть папку сохранений - + Open Mod Data Location Открыть папку модов - + Open Transferable Pipeline Cache Открыть переносной кэш конвейера - + Link to Ryujinx Ссылка на Ryujinx - + Remove Удалить - + Remove Installed Update Удалить установленное обновление - + Remove All Installed DLC Удалить все установленные DLC - + Remove Custom Configuration Удалить пользовательскую настройку - + Remove Cache Storage Удалить кэш-хранилище - + Remove OpenGL Pipeline Cache Удалить кэш конвейера OpenGL - + Remove Vulkan Pipeline Cache Удалить кэш конвейера Vulkan - + Remove All Pipeline Caches Удалить весь кэш конвейеров - + Remove All Installed Contents Удалить все установленное содержимое - + Manage Play Time Manage Play Time - + Edit Play Time Data Изменить дату Игрового времени - + Remove Play Time Data Удалить данные игрового времени - - + + Dump RomFS Дамп RomFS - + Dump RomFS to SDMC Сдампить RomFS в SDMC - + Verify Integrity Проверить целостность - + Copy Title ID to Clipboard Скопировать ID приложения в буфер обмена - + Navigate to GameDB entry Открыть отчет о совместимости - + Create Shortcut Создать ярлык - + Add to Desktop На рабочий стол - + Add to Applications Menu В меню приложений - + Configure Game Настроить игру - + Scan Subfolders Сканировать подпапки - + Remove Game Directory Удалить папку с играми - + ▲ Move Up ▲ Переместить вверх - + ▼ Move Down ▼ Переместить вниз - + Open Directory Location Открыть расположение папки - + Clear Очистить - - - Name - Имя - - - - Compatibility - Совместимость - - - - Add-ons - Дополнения - - - - File type - Тип файла - - - - Size - Размер - - - - Play time - Время игры - GameListItemCompat - + Ingame В игре - + Game starts, but crashes or major glitches prevent it from being completed. Игра запускается, но вылеты или серьезные баги не позволяют пройти ее до конца. - + Perfect Идеально - + Game can be played without issues. Игра полностью работает без проблем. - + Playable Играбельно - + Game functions with minor graphical or audio glitches and is playable from start to finish. Игра проходима от начала до конца, возможны лишь мелкие графические или звуковые ошибки. - + Intro/Menu Вступление/Меню - + Game loads, but is unable to progress past the Start Screen. Игра загружается, но не проходит дальше стартового экрана. - + Won't Boot Не запускается - + The game crashes when attempting to startup. Игра вылетает при запуске. - + Not Tested Не проверено - + The game has not yet been tested. Игру еще не проверяли на совместимость. + + GameListModel + + + Name + Название + + + + Compatibility + Совместимость + + + + Add-ons + Дополнения + + + + File type + Тип файла + + + + Size + Размер + + + + Play time + Время игры + + GameListPlaceholder - + Double-click to add a new folder to the game list Нажмите дважды, чтобы добавить новую папку в список игр @@ -6387,17 +6422,17 @@ Please go to Configure -> System -> Network and make a selection. GameListSearchField - + %1 of %n result(s) %1 из %n результат(ов)%1 из %n результат(ов)%1 из %n результат(ов)%1 из %n результат(ов) - + Filter: Поиск: - + Enter pattern to filter Введите текст для поиска @@ -6893,772 +6928,777 @@ Debug Message: &Вид списка игр - + Game &Icon Size Размер &иконок - + Reset Window Size to &720p Сбросить размер окна до &720p - + Reset Window Size to 720p Сбросить размер окна до 720p - + Reset Window Size to &900p Сбросить размер окна до &900p - + Reset Window Size to 900p Сбросить размер окна до 900p - + Reset Window Size to &1080p Сбросить размер окна до &1080p - + Reset Window Size to 1080p Сбросить размер окна до 1080p - + &Multiplayer &Мультиплеер - + &Tools &Инструменты - + Am&iibo Amiibo - + Launch &Applet &Апплеты - + &TAS &TAS - + &Create Home Menu Shortcut Создать &ярлык Home Menu - + Install &Firmware Установить &прошивку - + &Help &Помощь - + &Install Files to NAND... &Установить файлы в NAND... - + L&oad File... Загрузить &файл... - + Load &Folder... Загрузить &папку... - + E&xit &Выход - - + + &Pause &Пауза - + &Stop &Стоп - + &Verify Installed Contents Провер&ить установленное содержимое - + &About Eden &О Eden - + Single &Window Mode &Режим одного окна - + Con&figure... Параметр&ы... - + Ctrl+, Ctrl+, - + Enable Overlay Display Applet Включить отображение апплета оверлея - + Show &Filter Bar Показать п&анель поиска - + Show &Status Bar Показать панель с&татуса - + Show Status Bar Показать панель статуса - + &Browse Public Game Lobby &Просмотреть публичные игровые лобби - + &Create Room &Создать комнату - + &Leave Room Покинуть &комнату - + &Direct Connect to Room П&рямое подключение к комнате - + &Show Current Room Показать &текущую комнату - + F&ullscreen &Полноэкранный - + &Restart П&ерезапустить - + Load/Remove &Amiibo... &Загрузить/Удалить Amiibo... - + &Report Compatibility &Сообщить о совместимости - + Open &Mods Page Открыть страницу &модов - + Open &Quickstart Guide Открыть руководство &пользователя - + &FAQ &FAQ - + &Capture Screenshot &Сделать скриншот - + &Album Открыть &Альбом - + &Set Nickname and Owner Установить &никнейм и владельца - + &Delete Game Data &Удалить данные игры - + &Restore Amiibo &Восстановить Amiibo - + &Format Amiibo &Форматировать Amiibo - + &Mii Editor Открыт&ь Mii Editor - + &Configure TAS... &Настройка TAS... - + Configure C&urrent Game... &Настроить текущую игру... - - + + &Start &Запустить - + &Reset &Сбросить - - + + R&ecord З&апись - + Open &Controller Menu Открыть &Меню контроллеров - + Install Decryption &Keys &Установить ключи дешифровки - + &Home Menu Отк&рыть Home Menu - + &Desktop &Рабочий стол - + &Application Menu &Меню приложений - + &Root Data Folder &Корневая папка - + &NAND Folder П&апка NAND - + &SDMC Folder Па&пка SDMC - + &Mod Folder Папка &модов - + &Log Folder Папка &журнала - + From Folder Из &папки - + From ZIP &Из ZIP - + &Eden Dependencies &Зависимости Eden - + &Data Manager Упра&вление данными - + &Tree View &Древовидный - + &Grid View &Сетчатый - + Game Icon Size Размер иконки игры - - + + None Никакой - + Show Game &Name Показать игру - + Show &Performance Overlay &Оверлей производительности - + + &Carousel View + + + + Small (32x32) Маленький (32х32) - + Standard (64x64) Стандартный (64х64) - + Large (128x128) Большой (128х128) - + Full Size (256x256) Полноразмерный (256х256) - + Broken Vulkan Installation Detected Обнаружена поврежденная установка Vulkan - + Vulkan initialization failed during boot. Не удалось выполнить инициализацию Vulkan во время загрузки. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping Запущена игра - + Loading Web Applet... Загрузка веб-апплета... - - + + Disable Web Applet Отключить веб-апплет - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) Отключение веб-апплета может привести к неожиданному поведению и должно использоваться только, если вы хотите запустить Super Mario 3D All-Stars. Вы уверены, что хотите отключить веб-апплет? (Его можно снова включить в настройках отладки.) - + The amount of shaders currently being built Количество создаваемых шейдеров на данный момент - + The current selected resolution scaling multiplier. Текущий выбранный множитель масштабирования разрешения. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. Текущая скорость эмуляции. Значения выше или ниже 100% указывают на то, что эмуляция идет быстрее или медленнее, чем на Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Количество кадров в секунду в данный момент. Значение будет различаться между играми и сценами. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Время, которое нужно для эмуляции одного кадра Switch, не принимая во внимание ограничение FPS или вертикальную синхронизацию. Для эмуляции в полной скорости значение должно быть не больше 16,67 мс. - + Unmute Включить звук - + Mute Выключить звук - + Reset Volume Сбросить громкость - + &Clear Recent Files &Очистить недавние файлы - + &Continue &Продолжить - + Warning: Outdated Game Format Предупреждение: устаревший формат игры - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. Для этой игры вы используете разархивированный формат ROM'а, который является устаревшим и был заменен другими, такими как NCA, NAX, XCI или NSP. В разархивированных каталогах ROM'а отсутствуют иконки, метаданные и поддержка обновлений.<br><br>Для получения информации о различных форматах Switch, поддерживаемых Eden, ознакомьтесь с руководством пользователя. Это сообщение больше не будет отображаться. - - + + Error while loading ROM! Ошибка при загрузке ROM'а! - + The ROM format is not supported. Формат ROM'а не поддерживается. - + An error occurred initializing the video core. Произошла ошибка при инициализации видеоядра. - + Eden has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-mirror.github.io/help/reference/log-files/'>How to Upload the Log File</a>. Eden столкнулся с ошибкой при работе видеоядра. Обычно это вызвано устаревшими драйверами GPU, включая интегрированные. Проверьте журнал для получения подробностей. Информацию о том, как получить доступ к журналу, см. на странице: <a href='https://yuzu-mirror.github.io/help/reference/log-files/'>Как загрузить файл журнала</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. Ошибка при загрузке ROM'а! %1 - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. %1<br>Пожалуйста, пересоздайте дампы ваших файлов или обратитесь за помощью в Discord/Stoat. - + An unknown error occurred. Please see the log for more details. Произошла неизвестная ошибка. Пожалуйста, проверьте журнал для подробностей. - + (64-bit) (64-bit) - + (32-bit) (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Closing software... Закрываем программу... - + Save Data Сохранения - + Mod Data Данные модов - + Error Opening %1 Folder Ошибка при открытии папки %1 - - + + Folder does not exist! Папка не существует! - + Remove Installed Game Contents? Удалить установленное содержимое игр? - + Remove Installed Game Update? Удалить установленное обновление игры? - + Remove Installed Game DLC? Удалить установленные DLC игры? - + Remove Entry Удалить запись - + Delete OpenGL Transferable Shader Cache? Удалить переносной кэш шейдеров OpenGL? - + Delete Vulkan Transferable Shader Cache? Удалить переносной кэш шейдеров Vulkan? - + Delete All Transferable Shader Caches? Удалить весь переносной кэш шейдеров? - + Remove Custom Game Configuration? Удалить пользовательскую настройку игры? - + Remove Cache Storage? Удалить кэш-хранилище? - + Remove File Удалить файл - + Remove Play Time Data Удалить данные игрового времени - + Reset play time? Сбросить время игры? - - + + RomFS Extraction Failed! Не удалось извлечь RomFS! - + There was an error copying the RomFS files or the user cancelled the operation. Произошла ошибка при копировании файлов RomFS или пользователь отменил операцию. - + Full Полный - + Skeleton Скелет - + Select RomFS Dump Mode Выберите режим дампа RomFS - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. Пожалуйста, выберите, как вы хотите выполнить дамп RomFS. <br>Полный скопирует все файлы в новую папку, в то время как <br>скелет создаст только структуру папок. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root В %1 недостаточно свободного места для извлечения RomFS. Пожалуйста, освободите место или выберите другую папку для дампа в Эмуляция > Параметры > Система > Файловая система > Корень дампа - + Extracting RomFS... Извлечение RomFS... - - + + Cancel Отмена - + RomFS Extraction Succeeded! Извлечение RomFS прошло успешно! - + The operation completed successfully. Операция выполнена успешно. - + Error Opening %1 Ошибка при открытии %1 - + Select Directory Выбрать папку - + Properties Свойства - + The game properties could not be loaded. Не удалось загрузить свойства игры. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Исполняемый файл Switch (%1);;Все файлы (*.*) - + Load File Загрузить файл - + Open Extracted ROM Directory Открыть папку извлеченного ROM'а - + Invalid Directory Selected Выбрана недопустимая папка - + The directory you have selected does not contain a 'main' file. Папка, которую вы выбрали, не содержит файла 'main'. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) Устанавливаемый файл Switch (*.nca *.nsp *.xci);;Архив контента Nintendo (*.nca);;Пакет подачи Nintendo (*.nsp);;Образ картриджа NX (*.xci) - + Install Files Установить файлы - + %n file(s) remaining %n файл(ов) осталось%n файл(ов) осталось%n файл(ов) осталось%n файл(ов) осталось - + Installing file "%1"... Установка "%1"... - - + + Install Results Результаты - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. Чтобы избежать возможных конфликтов, мы не рекомендуем пользователям устанавливать игры в NAND. Пожалуйста, используйте эту функцию только для установки обновлений и DLC. - + %n file(s) were newly installed %n файл(ов) были установлены недавно @@ -7668,7 +7708,7 @@ Please, only use this feature to install updates and DLC. - + %n file(s) were overwritten %n файл(ов) были перезаписаны @@ -7678,7 +7718,7 @@ Please, only use this feature to install updates and DLC. - + %n file(s) failed to install %n файл(ов) не удалось установить @@ -7688,361 +7728,314 @@ Please, only use this feature to install updates and DLC. - + System Application Системное приложение - + System Archive Системный архив - + System Application Update Обновление системного приложения - + Firmware Package (Type A) Пакет прошивки (Тип А) - + Firmware Package (Type B) Пакет прошивки (Тип Б) - + Game Игра - + Game Update Обновление игры - + Game DLC DLC игры - + Delta Title Дельта-титул - + Select NCA Install Type... Выберите тип установки NCA... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) Пожалуйста, выберите тип приложения, который вы хотите установить для этого NCA: (В большинстве случаев, подходит стандартный выбор «Игра».) - + Failed to Install Ошибка установки - + The title type you selected for the NCA is invalid. Тип приложения, который вы выбрали для NCA, недействителен. - + File not found Файл не найден - + File "%1" not found Файл "%1" не найден - + OK ОК - + Function Disabled Функция отключена - + Compatibility list reporting is currently disabled. Check back later! Отправка отчетов о совместимости временно отключена. Доступ будет восстановлен позднее! - + Error opening URL Ошибка при открытии URL - + Unable to open the URL "%1". Не удалось открыть URL: "%1". - + TAS Recording Запись TAS - + Overwrite file of player 1? Перезаписать файл игрока 1? - + Invalid config detected Обнаружена недопустимая конфигурация - + Handheld controller can't be used on docked mode. Pro controller will be selected. Портативный контроллер не может быть использован в режиме док-станции. Будет выбран контроллер Pro. - - + + Amiibo Amiibo - - + + The current amiibo has been removed Текущий amiibo был удален - + Error Ошибка - - + + The current game is not looking for amiibos Текущая игра не ищет amiibo - + Amiibo File (%1);; All Files (*.*) Файл Amiibo (%1);; Все Файлы (*.*) - + Load Amiibo Загрузить Amiibo - + Error loading Amiibo data Ошибка загрузки данных Amiibo - + The selected file is not a valid amiibo Выбранный файл не является допустимым amiibo - + The selected file is already on use Выбранный файл уже используется - + An unknown error occurred Произошла неизвестная ошибка - - - Keys not installed - Ключи не установлены - - - - - Install decryption keys and restart Eden before attempting to install firmware. - Установите ключи дешифрования и перезапустите Eden перед установкой прошивки. - - - - Select Dumped Firmware Source Location - Выберите дамп распакованной прошивки - - - - Select Dumped Firmware ZIP - Выберите дамп прошивки в ZIP-архиве - - - - Zipped Archives (*.zip) - Архивы ZIP (*.zip) - - - - Firmware cleanup failed - Не удалось очистить прошивку - - - - Failed to clean up extracted firmware cache. -Check write permissions in the system temp directory and try again. -OS reported error: %1 - Не удалось очистить кэш распакованной прошивки. -Проверьте права на запись во временный системный каталог и повторите попытку. -Ошибка ОС: %1 - - - + No firmware available Прошивка недоступна - + Firmware Corrupted Прошивка повреждена - + Unknown applet Неизвестный апплет - + Applet doesn't map to a known value. Апплет не соответствует ни одному известному значению. - + Record not found Запись не найдена - + Applet not found. Please reinstall firmware. Апплет не найден. Пожалуйста, переустановите прошивку. - + Capture Screenshot Сделать скриншот - + PNG Image (*.png) Изображение PNG (*.png) - + TAS state: Running %1/%2 Состояние TAS: Выполняется %1/%2 - + TAS state: Recording %1 Состояние TAS: Записывается %1 - + TAS state: Idle %1/%2 Состояние TAS: Простой %1/%2 - + TAS State: Invalid Состояние TAS: Неверное - + &Stop Running &Остановка - + Stop R&ecording &Закончить запись - + Building: %n shader(s) Компиляция %n шейдер(ов)Компиляция %n шейдер(ов)Компиляция %n шейдер(ов)Компиляция %n шейдер(ов) - + Scale: %1x %1 is the resolution scaling factor Масштаб: %1x - + Speed: %1% / %2% Скорость: %1% / %2% - + Speed: %1% Скорость: %1% - + Game: %1 FPS Игра: %1 FPS - + Frame: %1 ms Кадр: %1 мс - - - FSR - FSR - - - + NO AA БЕЗ СГЛАЖИВАНИЯ - + VOLUME: MUTE ГРОМКОСТЬ: ЗАГЛУШЕНА - + VOLUME: %1% Volume percentage (e.g. 50%) ГРОМКОСТЬ: %1% - + Derivation Components Missing Отсутствуют необходимые компоненты - + Decryption keys are missing. Install them now? Ключи дешифрования отсутствуют. Хотите установить их? - + Wayland Detected! Обнаружен Wayland! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -8053,74 +8046,74 @@ Would you like to force it for future launches? Хотите включить X11 для последующих запусков? - + Use X11 Использовать X11 - + Continue with Wayland Продолжить с Wayland - + Don't show again Больше не показывать - + Restart Required Требуется перезапуск - + Restart Eden to apply the X11 backend. Перезапустите Eden, чтобы применить X11. - + Slow Замедлено - + Turbo Турбо - + Unlocked Разблокировано - + Select RomFS Dump Target Выбор цели для дампа RomFS - + Please select which RomFS you would like to dump. Выберите RomFS, который вы хотите извлечь. - + Are you sure you want to close Eden? Вы уверены, что хотите закрыть Eden? - - - + + + Eden Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. Вы уверены, что хотите остановить эмуляцию? Любой несохраненный прогресс будет потерян. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? @@ -8188,6 +8181,11 @@ Would you like to bypass this and exit anyway? ScaleForce ScaleForce + + + FSR + FSR + Area @@ -8199,52 +8197,62 @@ Would you like to bypass this and exit anyway? MMPX - + + SGSR + SGSR + + + + SGSR EdgeDir + SGSR EdgeDir + + + Docked В док-станции - + Handheld Портативный - + Fast Производительность - + Balanced Баланс - + Accurate Точность - + Vulkan Vulkan - + OpenGL GLSL OpenGL GLSL - + OpenGL SPIRV OpenGL SPIRV - + OpenGL GLASM OpenGL GLASM - + Null Null @@ -8287,7 +8295,7 @@ If you wish to clean up the files which were left in the old data location, you %1 - + Data was migrated successfully. Данные успешно перенесены. @@ -9145,47 +9153,47 @@ p, li { white-space: pre-wrap; } %1 играет в %2 - + Play Time: %1 Время игры: %1 - + Never Played Не запускалась - + Version: %1 Версия: %1 - + Version: 1.0.0 Версия: 1.0.0 - + Installed SD Titles Установленные SD игры - + Installed NAND Titles Установленные NAND игры - + System Titles Системные игры - + Add New Game Directory Добавить новую папку с играми - + Favorites Избранные @@ -9306,47 +9314,47 @@ p, li { white-space: pre-wrap; } QtCommon::Content - + Game Requires Firmware Для игры требуется прошивка - + The game you are trying to launch requires firmware to boot or to get past the opening menu. Please <a href='https://yuzu-mirror.github.io/help/quickstart'>dump and install firmware</a>, or press "OK" to launch anyways. Игра, которую вы пытаетесь запустить, требует прошивку для запуска или прохождения начального меню. Пожалуйста, <a href='https://yuzu-mirror.github.io/help/quickstart'>сделайте дамп и установите прошивку</a>, или нажмите «OK», чтобы запустить игру в любом случае. - + Installing Firmware... Установка прошивки... - - - - - + + + + + Cancel Отмена - + Firmware Install Failed Не удалось установить прошивку - + Firmware Install Succeeded Прошивка успешно установлена - + Firmware integrity verification failed! Сбой проверки целостности прошивки! - - + + Verification failed for the following files: %1 @@ -9355,207 +9363,242 @@ p, li { white-space: pre-wrap; } %1 - - + + Verifying integrity... Проверка целостности... - - + + Integrity verification succeeded! Проверка целостности прошла успешно! - - + + The operation completed successfully. Операция выполнена успешно. - - + + Integrity verification failed! Проверка целостности не удалась! - + File contents may be corrupt or missing. Содержимое файла может быть повреждено или отсутствовать. - + Integrity verification couldn't be performed Проверка целостности не может быть выполнена - + Firmware installation cancelled, firmware may be in a bad state or corrupted. File contents could not be checked for validity. Установка прошивки отменена, прошивка может быть повреждена. Содержимое файла не удалось проверить на корректность. - + Select Dumped Keys Location Выберите местоположение дампнутых ключей - + Decryption Keys install succeeded Установка ключей дешифровки прошла успешно. - + Decryption Keys install failed Ошибка установки ключей дешифровки - + Orphaned Profiles Detected! Обнаружены «сиротские» профили! - + UNEXPECTED BAD THINGS MAY HAPPEN IF YOU DON'T READ THIS!<br>Eden has detected the following save directories with no attached profile:<br>%1<br><br>The following profiles are valid:<br>%2<br><br>Click "OK" to open your save folder and fix up your profiles.<br>Hint: copy the contents of the largest or last-modified folder elsewhere, delete all orphaned profiles, and move your copied contents to the good profile.<br><br>Still confused? See the <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/Orphaned.md'>help page</a>.<br> МОГУТ ПРОИЗОЙТИ НЕПРЕДВИДЕННЫЕ ПРОБЛЕМЫ, ЕСЛИ ВЫ НЕ ПРОЧИТАЕТЕ ЭТО!<br>Eden обнаружил следующие папки сохранений без привязанного профиля:<br>%1<br><br>Найдены следующие корректные профили:<br>%2<br><br>Нажмите «OK», чтобы открыть папку сохранений и исправить профили.<br>Совет: скопируйте содержимое самой большой или последней измененной папки в другое место, удалите все «сиротские» профили и переместите скопированные данные в правильный профиль.<br><br>Если есть вопросы, прочитайте <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/Orphaned.md'>справку</a>.<br> - + Really clear data? Действительно очистить данные? - + Important data may be lost! Важные данные могут быть потеряны! - + Are you REALLY sure? Вы уверены? - + Once deleted, your data will NOT come back! Only do this if you're 100% sure you want to delete this data. Данные будут безвозвратно потеряны после удаления! Делайте это только если вы абсолютно уверены, что хотите их удалить. - + Clearing... Очистка... - + Select Export Location Выберите папку для экспорта - + %1.zip %1.zip - - + + + Zipped Archives (*.zip) Архивы ZIP (*.zip) - + Exporting data. This may take a while... Идет экспорт данных. Это может занять некоторое время... - + Exporting Экспорт - + Exported Successfully Экспорт выполнен успешно - + Data was exported successfully. Данные успешно экспортированы. - + Export Cancelled Экспорт отменен - + Export was cancelled by the user. Экспорт был отменен пользователем. - + Export Failed Не удалось экспортировать - + Ensure you have write permissions on the targeted directory and try again. Проверьте, есть ли права на запись в выбранную папку, и повторите попытку. - + Select Import Location Выберите папку для импорта - + Import Warning Предупреждение при импорте - + All previous data in this directory will be deleted. Are you sure you wish to proceed? Все существующие данные в этой папке будут удалены. Вы уверены, что хотите продолжить? - + Importing data. This may take a while... Идет импорт данных. Это может занять некоторое время... - + Importing Импорт - + Imported Successfully Импорт успешно выполнен - + Data was imported successfully. Данные успешно импортированы. - + Import Cancelled Импорт отменен - + Import was cancelled by the user. Импорт был отменен пользователем. - + Import Failed Не удалось импортировать - + Ensure you have read permissions on the targeted directory and try again. Проверьте, есть ли права на чтение в выбранной папке, и повторите попытку. + + + Keys not installed + Ключи не установлены + + + + Install decryption keys and restart Eden before attempting to install firmware. + Установите ключи дешифрования и перезапустите Eden перед установкой прошивки. + + + + Select Dumped Firmware Source Location + Выберите дамп распакованной прошивки + + + + Select Dumped Firmware ZIP + Выберите дамп прошивки в ZIP-архиве + + + + Firmware cleanup failed + Не удалось очистить прошивку + + + + Failed to clean up extracted firmware cache. +Check write permissions in the system temp directory and try again. +OS reported error: %1 + Не удалось очистить кэш распакованной прошивки. +Проверьте права на запись во временный системный каталог и повторите попытку. +Ошибка ОС: %1 + QtCommon::FS @@ -9804,72 +9847,72 @@ Would you like to manually select a portable folder to use? Кэш метаданных не может быть удален. Возможно, он используется или отсутствует. - + Create Shortcut Создать ярлык - + Do you want to launch the game in fullscreen? Вы хотите запустить игру в полноэкранном режиме? - + Shortcut Created Ярлык создан - + Successfully created a shortcut to %1 Успешно создан ярлык в %1 - + Shortcut may be Volatile! Ярлык может быть нестабильным! - + This will create a shortcut to the current AppImage. This may not work well if you update. Continue? Это создаст ярлык для текущего AppImage. Он может не работать после обновлений. Продолжить? - + Failed to Create Shortcut Не удалось создать ярлык - + Failed to create a shortcut to %1 Не удалось создать ярлык для %1 - + Create Icon Создать иконку - + Cannot create icon file. Path "%1" does not exist and cannot be created. Невозможно создать файл иконки. Путь "%1" не существует и не может быть создан. - + No firmware available Прошивка недоступна - + Please install firmware to use the home menu. Пожалуйста, установите прошивку, чтобы использовать Home Menu. - + Home Menu Applet Приложение Home Menu - + Home Menu is not available. Please reinstall firmware. Home Menu недоступно. Переустановите прошивку. @@ -9877,37 +9920,37 @@ Would you like to manually select a portable folder to use? QtCommon::Mod - + Mod Name Название мода - + What should this mod be called? Как назвать этот мод? - + RomFS RomFS - + ExeFS/Patch ExeFS/Patch - + Cheat Чит - + Mod Type Тип мода - + Could not detect mod type automatically. Please manually specify the type of mod you downloaded. Most mods are RomFS mods, but patches (.pchtxt) are typically ExeFS mods. @@ -9916,18 +9959,18 @@ Most mods are RomFS mods, but patches (.pchtxt) are typically ExeFS mods. - - + + Mod Extract Failed Ошибка извлечения мода - + Failed to create temporary directory %1 Не удалось создать временную папку %1 - + Zip file %1 is empty ZIP-файл %1 пуст @@ -10629,66 +10672,66 @@ By selecting "From Eden", previous save data stored in Ryujinx will be %1 доступна для загрузки. - + New Version Location Местоположение новой версии - + All Files (*.*) Все файлы (*.*) - - - + + + Failed to save file Не удалось сохранить файл - + Could not open file %1 for writing. Не удалось открыть файл %1 для записи. - + Downloading... Загрузка... - + Cancel Отмена - + Could not write to file %1. Не удалось записать в файл %1. - + Could not commit to file %1. Не удалось сохранить файл %1. - + Failed to download file Не удалось загрузить файл - + Could not download from %1%2 Error code: %3 Не удалось загрузить из %1%2 Код ошибки: %3 - + Download Complete Загрузка завершена - + Successfully downloaded %1. Would you like to open it? %1 был успешно загружен. Хотите открыть? diff --git a/dist/languages/sv.ts b/dist/languages/sv.ts index 3865e56ee0..31fb9d69fe 100644 --- a/dist/languages/sv.ts +++ b/dist/languages/sv.ts @@ -725,8 +725,8 @@ Alternativ lägre än 1X kan orsaka artefakter. - Determines how sharpened the image will look using FSR's dynamic contrast. - Bestämmer hur skarp bilden ska se ut med hjälp av FSR:s dynamiska kontrast. + Determines how sharpened the image will look using FSR's or SGSR's dynamic contrast. + @@ -783,24 +783,24 @@ Disabling it is only intended for debugging. Att inaktivera detta är endast avsett för felsökning. - + Use asynchronous GPU emulation Använd asynkron GPU-emulering - + Uses an extra CPU thread for rendering. This option should always remain enabled. Använder en extra CPU-tråd för rendering. Detta alternativ bör alltid vara aktiverat. - + NVDEC emulation: NVDEC-emulering: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. @@ -809,12 +809,12 @@ Den kan antingen använda CPU eller GPU för avkodning, eller inte utföra någo I de flesta fall ger GPU-avkodning bäst prestanda. - + ASTC Decoding Method: ASTC-avkodningsmetod: - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -827,12 +827,12 @@ CPU asynkront: Använd CPU:n för att avkoda ASTC-texturer vid behov. Eliminerar men kan ge artefakter. - + ASTC Recompression Method: ASTC-återkomprimeringsmetod: - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. @@ -841,44 +841,55 @@ BC1/BC3: Det mellanliggande formatet kommer att komprimeras om till BC1- eller B vilket sparar VRAM men försämrar bildkvaliteten. - + Frame Pacing Mode (Vulkan only) Frame Pacing Mode (endast Vulkan) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. Styr hur emulatorn hanterar bildhastigheten för att minska hackighet och göra bildfrekvensen jämnare och mer konsekvent. - + VRAM Usage Mode: VRAM-användningsläge: - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. Väljer om emulatorn ska prioritera att spara minne eller utnyttja tillgängligt videominne maximalt för prestanda. Aggressivt läge kan påverka prestandan hos andra program, till exempel inspelningsprogram. - + Skip CPU Inner Invalidation Hoppa över CPU:ns interna ogiltigförklaring - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. Hoppar över vissa cache-ogiltigförklaringar under minnesuppdateringar, vilket minskar CPU-användningen och förbättrar latensen. Detta kan orsaka mjuka krascher. - + + Anti-Flicker + + + + + Forces GPU fence callbacks to wait for submitted GPU work. +Use with Fast GPU mode, to avoid flicker with lower performance impact. + + + + VSync Mode: VSync-läge: - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -889,12 +900,12 @@ Mailbox kan ha lägre latens än FIFO och uppvisar inte tearing, men kan tappa b Immediate (ingen synkronisering) visar allt som är tillgängligt och kan uppvisa tearing. - + Sync Memory Operations Synkronisera minnesoperationer - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. @@ -905,32 +916,32 @@ Det här alternativet åtgärdar problem i spel, men kan försämra prestandan. Unreal Engine 4-spel upplever ofta de mest betydande förändringarna av detta. - + Enable asynchronous presentation (Vulkan only) Aktivera asynkron presentation (endast Vulkan) - + Slightly improves performance by moving presentation to a separate CPU thread. Förbättrar prestandan något genom att flytta presentationen till en separat CPU-tråd. - + Force maximum clocks (Vulkan only) Tvinga fram maximal klockfrekvens (endast Vulkan) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. Körs i bakgrunden i väntan på grafikkommandon för att förhindra att GPU:n sänker sin klockhastighet. - + Anisotropic Filtering: Anisotropisk filtrering: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. Kontrollerar kvaliteten på texturrendering vid sneda vinklar. @@ -938,12 +949,12 @@ Safe to set at 16x on most GPUs. Säker att ställa in på 16x på de flesta GPU:er. - + GPU Mode: GPU-läge: - + Controls the GPU emulation mode. Most games render fine with Fast or Balanced modes, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. @@ -952,56 +963,56 @@ De flesta spel renderas bra med lägena Snabb eller Balanserad, men för vissa k Partiklar tenderar att endast renderas korrekt med läget Noggrann. - + DMA Accuracy: DMA-noggrannhet: - + Controls the DMA precision accuracy. Safe precision fixes issues in some games but may degrade performance. Kontrollerar DMA-precisionens noggrannhet. Säker precision åtgärdar problem i vissa spel men kan försämra prestandan. - + Enable asynchronous shader compilation Aktivera asynkron shaderkompilering - + May reduce shader stutter. Kan minska shader-hackighet. - + Fast GPU Time Snabb GPU-tid - + Overclocks the emulated GPU to increase dynamic resolution and render distance. Use 256 for maximal performance and 512 for maximal graphics fidelity. Överklockar den emulerade GPU:n för att öka den dynamiska upplösningen och renderingsavståndet. Använd 256 för maximal prestanda och 512 för maximal grafisk trohet. - + GPU Unswizzle GPU Unswizzle - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. Accelererar avkodning av BCn 3D-texturer med hjälp av GPU-beräkningar. Inaktivera om du upplever krascher eller grafiska fel. - + GPU Unswizzle Max Texture Size Maximal texturstorlek för GPU Unswizzle - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. @@ -1010,48 +1021,48 @@ GPU är snabbare för medelstora och stora texturer, men CPU kan vara effektivar Justera detta för att hitta balansen mellan GPU-acceleration och CPU-överbelastning. - + GPU Unswizzle Stream Size Strömstorlek för GPU Unswizzle - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. Ställer in den maximala mängden texturdata (i MiB) som bearbetas per bildruta. Högre värden kan minska hackighet under texturinläsning men kan påverka bildrutans konsistens. - + GPU Unswizzle Chunk Size Chunk-storlek för GPU Unswizzle - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. Bestämmer antalet djupskivor som bearbetas i en enda sändning. Att öka detta kan förbättra genomströmningen på avancerade GPU:er, men kan orsaka TDR eller drivrutinstidsgränser på svagare hårdvara. - + Use Vulkan pipeline cache Använda Vulkan pipeline-cache - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. Aktiverar GPU-leverantörsspecifik pipeline-cache. Det här alternativet kan förbättra laddningstiden för shaders avsevärt i fall där Vulkan-drivrutinen inte lagrar pipeline-cache-filer internt. - + Enable Compute Pipelines (Intel Vulkan Only) Aktivera compute pipelines (endast Intel Vulkan) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. @@ -1060,184 +1071,194 @@ Denna inställning finns endast för Intels egna drivrutiner och kan orsaka kras Beräkningspipelines är alltid aktiverade på alla andra drivrutiner. - + Enable Reactive Flushing Aktivera Reactive Flushing - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. Använder reaktiv rensning i stället för prediktiv rensning, vilket ger mer exakt minnessynkning. - + Sync to framerate of video playback Synkronisera med bildfrekvensen för videouppspelning - + Run the game at normal speed during video playback, even when the framerate is unlocked. Kör spelet i normal hastighet under videouppspelning, även när bildfrekvensen är upplåst. - + Barrier feedback loops Återkopplingsloopar för barriärer - + Improves rendering of transparency effects in specific games. Förbättrar renderingen av transparenseffekter i vissa spel. - + Enable buffer history Aktivera bufferthistorik - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. Aktiverar åtkomst till tidigare bufferttillstånd. Det här alternativet kan förbättra renderingskvaliteten och prestandakonsistensen i vissa spel. - + Fix bloom effects Korrigera bloom-effekter - + Removes bloom in Burnout. Tar bort bloom i Burnout. - + Enable Legacy Rescale Pass Aktivera äldre omskalningspass - + May fix rescale issues in some games by relying on behavior from the previous implementation. Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. Kan åtgärda skalningsproblem i vissa spel genom att förlita sig på beteendet från den tidigare implementeringen. Äldre beteende som åtgärdar linjeartefakter på AMD- och Intel-GPU:er och grå texturflimmer på Nvidia-GPU:er i Luigis Mansion 3. - + Extended Dynamic State Utökad dynamisk status - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. Kontrollerar antalet funktioner som kan användas i utökat dynamiskt tillstånd. Högre tillstånd möjliggör fler funktioner och kan öka prestandan, men kan orsaka ytterligare grafiska problem. - + Vertex Input Dynamic State Dynamiskt tillstånd för vertexinmatning - + Enables vertex input dynamic state feature for better quality and performance. Aktiverar funktionen för dynamiskt tillstånd för vertexinmatning för bättre kvalitet och prestanda. - + Sample Shading Provskuggning - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. Tillåter fragment-shadern att exekveras per prov i ett multisamplade fragment istället för en gång per fragment. Förbättrar grafikens kvalitet på bekostnad av prestanda. Högre värden förbättrar kvaliteten men försämrar prestandan. - + RNG Seed RNG-frö - + Controls the seed of the random number generator. Mainly used for speedrunning. Att kontrollera fröet till slumptalsgeneratorn. Används främst för speedrunning. - + Device Name Enhetsnamn - + The name of the console. Konsolens namn. - + + Homebrew Args + + + + + Command-line arguments passed to homebrew at launch (e.g. -noglsl). + + + + Custom RTC Date: Anpassat RTC-datum: - + This option allows to change the clock of the console. Can be used to manipulate time in games. Med det här alternativet kan du ändra klockan på konsolen. Kan användas för att manipulera tiden i spel. - + The number of seconds from the current unix time Antalet sekunder från aktuell Unix-tid - + Language: Språk: - + This option can be overridden when region setting is auto-select Det här alternativet kan åsidosättas när regioninställningen är automatiskt vald. - + Region: Region: - + The region of the console. Konsolens region. - + Time Zone: Tidszon: - + The time zone of the console. Konsolens tidszon. - + Sound Output Mode: Ljudutmatningsläge: - + Console Mode: Konsolläge: - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. @@ -1246,1031 +1267,1041 @@ Spel ändrar upplösning, detaljer och stödda kontroller beroende på denna ins Inställningen Handhållen kan förbättra prestandan för enklare system. - + Prompt for user profile on boot Fråga efter användarprofil vid uppstart - + Useful if multiple people use the same PC. Användbart om flera personer använder samma dator. - + Pause when not in focus Pausa när inte i fokus - + Pauses emulation when focusing on other windows. Pausar emulering när fokus är på andra fönster. - + Confirm before stopping emulation Bekräfta innan emuleringen stoppas - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. Åsidosätter frågor om att bekräfta att emuleringen ska avslutas. Om du aktiverar den hoppar du över sådana uppmaningar och avslutar emuleringen direkt. - + Hide mouse on inactivity Dölj musen vid inaktivitet - + Hides the mouse after 2.5s of inactivity. Döljer musen efter 2,5 sekunders inaktivitet. - + Disable controller applet Inaktivera kontroller-appleten - + Forcibly disables the use of the controller applet in emulated programs. When a program attempts to open the controller applet, it is immediately closed. Inaktiverar med tvång användningen av kontrollerappletten i emulerade program. När ett program försöker öppna kontrollerappletten stängs den omedelbart. - + Check for updates Leta efter uppdateringar - + Whether or not to check for updates upon startup. Om uppdateringar ska sökas vid start eller inte. - + Enable Gamemode Aktivera Gamemode - + Force X11 as Graphics Backend Tvinga X11 som grafikbackend - + Custom frontend Anpassad frontend - + Real applet Verklig applet - + Never Aldrig - + On Load Vid inläsning - + Always Alltid - + CPU CPU - + GPU GPU - + CPU Asynchronous CPU asynkron - + Uncompressed (Best quality) Okomprimerad (bästa kvalitet) - + BC1 (Low quality) BC1 (låg kvalitet) - + BC3 (Medium quality) BC3 (medelhög kvalitet) - - + + Auto Auto - + 30 FPS 30 bilder/s - + 60 FPS 60 bilder/s - + 90 FPS 90 bilder/s - + 120 FPS 120 bilder/s - + Conservative Konservativ - + Aggressive Aggressiv - + Vulkan Vulkan - + OpenGL GLSL OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) OpenGL GLASM (Assembly Shaders, endast NVIDIA) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) OpenGL SPIR-V (Experimentell, endast AMD/Mesa) - + Null Null - + Fast Snabb - + Balanced Balanserad - - + + Accurate Exakt - - + + Default Standard - + Unsafe (fast) Osäker (snabb) - + Safe (stable) Säker (stabil) - + Unsafe Inte säker - + Paranoid (disables most optimizations) Paranoid (inaktiverar de flesta optimeringar) - + Debugging Felsökning - + Dynarmic Dynarmisk - + NCE NCE - + Borderless Windowed Ramlöst fönsterläge - + Exclusive Fullscreen Exklusiv helskärm - + No Video Output Ingen videoutgång - + CPU Video Decoding CPU-videoavkodning - + GPU Video Decoding (Default) GPU videoavkodning (standard) - + 0.25X (180p/270p) [EXPERIMENTAL] 0.25X (180p/270p) [EXPERIMENTELL] - + 0.5X (360p/540p) [EXPERIMENTAL] 0.5X (360p/540p) [EXPERIMENTELL] - + 0.75X (540p/810p) [EXPERIMENTAL] 0.75X (540p/810p) [EXPERIMENTELL] - + 1X (720p/1080p) 1X (720p/1080p) - + 1.25X (900p/1350p) [EXPERIMENTAL] 1.25X (900p/1350p) [EXPERIMENTELL] - + 1.5X (1080p/1620p) [EXPERIMENTAL] 1.5X (1080p/1620p) [EXPERIMENTELL] - + 2X (1440p/2160p) 2X (1440p/2160p) - + 3X (2160p/3240p) 3X (2160p/3240p) - + 4X (2880p/4320p) 4X (2880p/4320p) - + 5X (3600p/5400p) 5X (3600p/5400p) - + 6X (4320p/6480p) 6X (4320p/6480p) - + 7X (5040p/7560p) 7X (5040p/7560p) - + 8X (5760p/8640p) 8X (5760p/8640p) - + Nearest Neighbor Närmsta granne - + Bilinear Bilinjär - + Bicubic Bikubisk - + Gaussian Gaussisk - + Lanczos Lanczos - + ScaleForce ScaleForce - + AMD FidelityFX Super Resolution AMD FidelityFX Super Resolution - + Area Område - + MMPX MMPX - + Zero-Tangent Zero-Tangent - + B-Spline B-Spline - + Mitchell Mitchell - + Spline-1 Spline-1 - - + + Snapdragon Game Super Resolution + + + + + Snapdragon Game Super Resolution EdgeDir + + + + + None Ingen - + FXAA FXAA - + SMAA SMAA - + Default (16:9) Standard (16:9) - + Force 4:3 Tvinga 4:3 - + Force 21:9 Tvinga 21:9 - + Force 16:10 Tvinga 16:10 - + Stretch to Window Sträck ut till fönster - + Automatic Automatiskt - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x - + 32x 32x - + 64x 64x - + Japanese (日本語) Japanska (日本語) - + American English Amerikansk engelska - + French (français) Franska (français) - + German (Deutsch) Tyska (Deutsch) - + Italian (italiano) Italienska (italiano) - + Spanish (español) Spanska (español) - + Chinese Kinesiska - + Korean (한국어) Koreanska (한국어) - + Dutch (Nederlands) Nederländska (Nederlands) - + Portuguese (português) Portugisiska (português) - + Russian (Русский) Ryska (Русский) - + Taiwanese Taiwanesiska - + British English Brittisk engelska - + Canadian French Kanadensisk franska - + Latin American Spanish Latinamerikansk spanska - + Simplified Chinese Förenklad kinesiska - + Traditional Chinese (正體中文) Traditionell kinesiska (正體中文) - + Brazilian Portuguese (português do Brasil) Brasiliansk portugisiska (português do Brasil) - + Polish (polska) Polska (polska) - + Thai (แบบไทย) Thai (แบบไทย) - - + + Japan Japan - + USA USA - + Europe Europa - + Australia Australien - + China Kina - + Korea Korea - + Taiwan Taiwan - + Auto (%1) Auto select time zone Auto (%1) - + Default (%1) Default time zone Standard (%1) - + CET CET - + CST6CDT CST6CDT - + Cuba Kuba - + EET EET - + Egypt Egypten - + Eire Irland - + EST EST - + EST5EDT EST5EDT - + GB GB - + GB-Eire GB-Irland - + GMT GMT - + GMT+0 GMT+0 - + GMT-0 GMT-0 - + GMT0 GMT0 - + Greenwich Greenwich - + Hongkong Hongkong - + HST HST - + Iceland Island - + Iran Iran - + Israel Israel - + Jamaica Jamaica - + Kwajalein Kwajalein - + Libya Libyen - + MET MET - + MST MST - + MST7MDT MST7MDT - + Navajo Navajo - + NZ NZ - + NZ-CHAT NZ-CHAT - + Poland Polen - + Portugal Portugal - + PRC PRC - + PST8PDT PST8PDT - + ROC ROC - + ROK ROK - + Singapore Singapore - + Turkey Turkiet - + UCT UCT - + Universal Universal - + UTC UTC - + W-SU W-SU - + WET VÅT - + Zulu Zulu - + Mono Mono - + Stereo Stereo - + Surround Surround - + 4GB DRAM (Default) 4 GB DRAM (standard) - + 6GB DRAM (Unsafe) 6 GB DRAM (osäker) - + 8GB DRAM 8 GB DRAM - + 10GB DRAM (Unsafe) 10 GB DRAM (osäker) - + 12GB DRAM (Unsafe) 12 GB DRAM (osäker) - + Docked Dockad - + Handheld Handhållen - - + + Off Av - + Boost (1700MHz) Boost (1700MHz) - + Fast (2000MHz) Snabb (2000 MHz) - + Always ask (Default) Fråga alltid (standard) - + Only if game specifies not to stop Endast om spelet anger att det inte ska stoppas - + Never ask Fråga aldrig - - + + Medium (256) Medium (256) - - + + High (512) Hög (512) - + Very Small (16 MB) Mycket liten (16 MB) - + Small (32 MB) Liten (32 MB) - + Normal (128 MB) Normal (128 MB) - + Large (256 MB) Stor (256 MB) - + Very Large (512 MB) Mycket stor (512 MB) - + Very Low (4 MB) Mycket låg (4 MB) - + Low (8 MB) Låg (8 MB) - + Normal (16 MB) Normal (16 MB) - + Medium (32 MB) Medium (32 MB) - + High (64 MB) Hög (64 MB) - + Very Low (32) Mycket låg (32) - + Low (64) Låg (64) - + Normal (128) Normal (128) - + Disabled Inaktiverad - + ExtendedDynamicState 1 ExtendedDynamicState 1 - + ExtendedDynamicState 2 ExtendedDynamicState 2 - + ExtendedDynamicState 3 ExtendedDynamicState 3 - + Tree View Trädvy - + Grid View Rutnätsvy @@ -3337,33 +3368,33 @@ Vill du ta bort gammalt sparat data? Bakgrundsfärg: - + % - FSR sharpening percentage (e.g. 50%) - % + FSR/SGSR sharpening percentage (e.g. 50%) + - + Off Av - + VSync Off VSync Av - + Recommended Rekommenderad - + On - + VSync On VSync På @@ -4492,7 +4523,7 @@ Om du vill invertera axlarna för du joysticken först vertikalt och sedan horis - + Configure Konfigurera @@ -4523,7 +4554,7 @@ Om du vill invertera axlarna för du joysticken först vertikalt och sedan horis - + Test Testa @@ -4538,77 +4569,77 @@ Om du vill invertera axlarna för du joysticken först vertikalt och sedan horis Ta bort server - + %1:%2 %1:%2 - - - - - - + + + + + + Eden Eden - + Port number has invalid characters Portnumret har ogiltiga tecken - + Port has to be in range 0 and 65353 Port måste ligga inom intervallet 0 och 65353 - + IP address is not valid IP-adressen är inte giltig - + This UDP server already exists Denna UDP-server finns redan - + Unable to add more than 8 servers Det går inte att lägga till fler än 8 servrar - + Testing Testar - + Configuring Konfigurerar - + Test Successful Testet lyckades - + Successfully received data from the server. Tog emot data från servern. - + Test Failed Testet misslyckades - + Could not receive valid data from the server.<br>Please verify that the server is set up correctly and the address and port are correct. Kunde inte ta emot giltiga data från servern.<br>Kontrollera att servern är korrekt konfigurerad och att adress och port är korrekta. - + UDP Test or calibration configuration is in progress.<br>Please wait for them to finish. UDP Test- eller kalibreringskonfiguration pågår.<br>Vänta tills de är klara. @@ -4793,57 +4824,57 @@ Nuvarande värden är %1% respektive %2%. Vissa inställningar är endast tillgängliga när ett spel inte är igång. - + Add-Ons Tillägg - + System System - + CPU CPU - + Graphics Grafik - + Adv. Graphics Avancerad grafik - + Ext. Graphics Ext. grafik - + Audio Ljud - + Input Profiles Inmatningsprofiler - + Network Nätverk - + Applets Appletar - + Properties Egenskaper @@ -5859,7 +5890,7 @@ Dra punkterna för att ändra position, eller dubbelklicka på tabellcellerna f Importera data för denna katalog. Detta kan ta en stund och kommer att ta bort ALLT BEFINTLIGT DATA! - + Calculating... Beräknar... @@ -6063,50 +6094,50 @@ Gå till Konfigurera -> System -> Nätverk och gör ett val. GRenderWindow - - + + OpenGL not available! OpenGL är inte tillgängligt! - + OpenGL shared contexts are not supported. Delade OpenGL-kontexter stöds inte. - + Eden has not been compiled with OpenGL support. Eden har inte kompilerats med OpenGL-stöd. - - - + + + Error while initializing OpenGL! Fel vid initiering av OpenGL! - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. Din GPU kanske inte stöder OpenGL, eller så har du inte den senaste grafikdrivrutinen. - + Error while initializing OpenGL 4.6! Fel vid initiering av OpenGL 4.6! - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 Din GPU kanske inte stöder OpenGL 4.6, eller så har du inte den senaste grafikdrivrutinen.<br><br>GL Renderer:<br>%1 - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 Din GPU kanske inte stöder ett eller flera av de nödvändiga OpenGL-tilläggen. Se till att du har den senaste grafikdrivrutinen.<br><br>GL Renderer:<br>%1<br><br>Tillägg som inte stöds:<br>%2 - + This build doesn't have OpenGL support. @@ -6114,279 +6145,282 @@ Gå till Konfigurera -> System -> Nätverk och gör ett val. GameList - + &Add New Game Directory &Lägg till ny spelkatalog - + Favorite Favorit - + Start Game Starta spel - + Start Game without Custom Configuration Starta spelet utan anpassad konfiguration - + Open Save Data Location Öppna plats för sparat data - + Open Mod Data Location Öppna plats för moddata - + Open Transferable Pipeline Cache Öppna cache för överförbar pipeline - + Link to Ryujinx Länka till Ryujinx - + Remove Ta bort - + Remove Installed Update Ta bort installerad uppdatering - + Remove All Installed DLC Ta bort alla installerade DLC - + Remove Custom Configuration Ta bort anpassad konfiguration - + Remove Cache Storage Ta bort cache-lagring - + Remove OpenGL Pipeline Cache Ta bort OpenGL Pipeline Cache - + Remove Vulkan Pipeline Cache Ta bort Vulkan Pipeline Cache - + Remove All Pipeline Caches Ta bort alla pipeline-cacher - + Remove All Installed Contents Ta bort allt installerat innehåll - + Manage Play Time Hantera speltid - + Edit Play Time Data Redigera data för speltid - + Remove Play Time Data Ta bort data om speltid - - + + Dump RomFS Dumpa RomFS - + Dump RomFS to SDMC Dumpa RomFS till SDMC - + Verify Integrity Verifiera integritet - + Copy Title ID to Clipboard Kopiera titel-id till urklipp - + Navigate to GameDB entry Navigera till GameDB-post - + Create Shortcut Skapa genväg - + Add to Desktop Lägg till på skrivbordet - + Add to Applications Menu Lägg till i programmenyn - + Configure Game Konfigurera spelet - + Scan Subfolders Sök igenom undermappar - + Remove Game Directory Ta bort spelkatalog - + ▲ Move Up ▲ Flytta upp - + ▼ Move Down ▼ Flytta ner - + Open Directory Location Öppna katalogplats - + Clear Rensa - - - Name - Namn - - - - Compatibility - Kompatibilitet - - - - Add-ons - Tillägg - - - - File type - Filtyp - - - - Size - Storlek - - - - Play time - Speltid - GameListItemCompat - + Ingame Spelproblem - + Game starts, but crashes or major glitches prevent it from being completed. Spelet startar men kraschar eller större fel gör att det inte kan slutföras. - + Perfect Perfekt - + Game can be played without issues. Spelet kan spelas utan problem. - + Playable Spelbart - + Game functions with minor graphical or audio glitches and is playable from start to finish. Spelet fungerar med små grafiska eller ljudmässiga fel och är spelbart från början till slut. - + Intro/Menu Intro/Meny - + Game loads, but is unable to progress past the Start Screen. Spelet läses in men det går inte att komma förbi startskärmen. - + Won't Boot Startar inte - + The game crashes when attempting to startup. Spelet kraschar när det försöker starta. - + Not Tested Inte testat - + The game has not yet been tested. Spelet har ännu inte testats. + + GameListModel + + + Name + + + + + Compatibility + + + + + Add-ons + + + + + File type + + + + + Size + + + + + Play time + + + GameListPlaceholder - + Double-click to add a new folder to the game list Dubbelklicka för att lägga till en ny mapp i spellistan @@ -6394,17 +6428,17 @@ Gå till Konfigurera -> System -> Nätverk och gör ett val. GameListSearchField - + %1 of %n result(s) %1 av %n resultat%1 av %n resultat - + Filter: Filtrera: - + Enter pattern to filter Ange mönster för att filtrera @@ -6900,772 +6934,777 @@ Felsökningsmeddelande: Läge för s&pellista - + Game &Icon Size Storlek för spel&ikon - + Reset Window Size to &720p Återställ fönsterstorlek till &720p - + Reset Window Size to 720p Återställ fönsterstorlek till 720p - + Reset Window Size to &900p Återställ fönsterstorlek till &900p - + Reset Window Size to 900p Återställ fönsterstorleken till 900p - + Reset Window Size to &1080p Återställ fönsterstorlek till &1080p - + Reset Window Size to 1080p Återställ fönsterstorleken till 1080p - + &Multiplayer &Flera spelare - + &Tools Ver&ktyg - + Am&iibo Am&iibo - + Launch &Applet Starta &applet - + &TAS &TAS - + &Create Home Menu Shortcut S&kapa genväg till startmenyn - + Install &Firmware Installera &firmware - + &Help &Hjälp - + &Install Files to NAND... &Installera filer till NAND... - + L&oad File... Läs &in fil... - + Load &Folder... Läs in &mapp... - + E&xit A&vsluta - - + + &Pause &Paus - + &Stop &Stoppa - + &Verify Installed Contents &Verifiera installerat innehåll - + &About Eden &Om Eden - + Single &Window Mode Enkelt &fönsterläge - + Con&figure... Kon&figurera... - + Ctrl+, Ctrl+, - + Enable Overlay Display Applet Aktivera applet för överläggsvisning - + Show &Filter Bar Visa &filterfält - + Show &Status Bar Visa &statusrad - + Show Status Bar Visa statusrad - + &Browse Public Game Lobby &Bläddra i offentlig spellobby - + &Create Room &Skapa rum - + &Leave Room &Lämna rummet - + &Direct Connect to Room &Direkt anslutning till rum - + &Show Current Room &Visa aktuellt rum - + F&ullscreen H&elskärm - + &Restart Starta o&m - + Load/Remove &Amiibo... Läs in/ta bort &Amiibo... - + &Report Compatibility &Rapportera kompatibilitet - + Open &Mods Page Öppna &Mods-sidan - + Open &Quickstart Guide Öppna &snabbstartsguide - + &FAQ &Frågor och svar - + &Capture Screenshot &Ta skärmdump - + &Album &Album - + &Set Nickname and Owner &Ange smeknamn och ägare - + &Delete Game Data &Radera speldata - + &Restore Amiibo Å&terställ Amiibo - + &Format Amiibo &Formatera Amiibo - + &Mii Editor &Mii-redigerare - + &Configure TAS... &Konfigurera TAS... - + Configure C&urrent Game... Konfigurera a&ktuellt spel... - - + + &Start &Starta - + &Reset Starta &om - - + + R&ecord Spela &in - + Open &Controller Menu Öppna &kontrollermenyn - + Install Decryption &Keys Installera avkrypteringsn&ycklar - + &Home Menu &Hemmeny - + &Desktop S&krivbord - + &Application Menu &Applikationsmeny - + &Root Data Folder &Rotdatamapp - + &NAND Folder &NAND-mapp - + &SDMC Folder &SDMC-mapp - + &Mod Folder &Mod-mapp - + &Log Folder &Loggmapp - + From Folder Från mapp - + From ZIP Från ZIP - + &Eden Dependencies &Edens beroenden - + &Data Manager &Datahanterare - + &Tree View &Trädvy - + &Grid View &Rutnätsvy - + Game Icon Size Storlek för spelikon - - + + None Ingen - + Show Game &Name Visa spel&namn - + Show &Performance Overlay Visa &prestandaöverlägg - + + &Carousel View + + + + Small (32x32) Liten (32x32) - + Standard (64x64) Standard (64x64) - + Large (128x128) Stor (128x128) - + Full Size (256x256) Full storlek (256x256) - + Broken Vulkan Installation Detected Felaktig Vulkan-installation upptäcktes - + Vulkan initialization failed during boot. Vulkan-initialiseringen misslyckades under uppstarten. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping Kör ett spel - + Loading Web Applet... Läser in webbapplet... - - + + Disable Web Applet Inaktivera webbapplet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) Att inaktivera webbappletten kan leda till odefinierat beteende och bör endast användas med Super Mario 3D All-Stars. Är du säker på att du vill inaktivera webbappletten? (Detta kan återaktiveras i felsökningsinställningarna.) - + The amount of shaders currently being built Antalet shaders som för närvarande byggs - + The current selected resolution scaling multiplier. Den aktuella valda multiplikatorn för upplösningsskalning. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. Aktuell emuleringshastighet. Värden högre eller lägre än 100% indikerar att emuleringen körs snabbare eller långsammare än en Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Hur många bildrutor per sekund spelet för närvarande visar. Detta varierar från spel till spel och från scen till scen. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Tid som krävs för att emulera en Switch-bildruta, exklusive bildbegränsning eller v-synkronisering. För emulering i full hastighet bör detta vara högst 16,67 ms. - + Unmute Aktivera ljud - + Mute Tyst - + Reset Volume Återställ volym - + &Clear Recent Files &Töm tidigare filer - + &Continue &Fortsätt - + Warning: Outdated Game Format Varning: Föråldrat spelformat - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. Du använder det dekonstruerade ROM-katalogformatet för detta spel, vilket är ett föråldrat format som har ersatts av andra format såsom NCA, NAX, XCI eller NSP. Dekonstruerade ROM-kataloger saknar ikoner, metadata och uppdateringsstöd.<br> För en förklaring av de olika Switch-format som Eden har stöd för, se vår användarhandbok. Detta meddelande kommer inte att visas igen. - - + + Error while loading ROM! Fel vid inläsning av ROM! - + The ROM format is not supported. ROM-formatet stöds inte. - + An error occurred initializing the video core. Ett fel uppstod vid initialiseringen av videokärnan. - + Eden has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-mirror.github.io/help/reference/log-files/'>How to Upload the Log File</a>. Eden har stött på ett fel vid körning av videokärnan. Detta orsakas vanligtvis av föråldrade GPU-drivrutiner, inklusive integrerade sådana. Se loggen för mer information. För mer information om hur du kommer åt loggen, se följande sida: <a href='https://yuzu-mirror.github.io/help/reference/log-files/'>Hur man laddar upp loggfilen</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. Fel vid inläsning av ROM! %1 - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. %1<br>Dumpa dina filer igen eller fråga på Discord/Stoat för hjälp. - + An unknown error occurred. Please see the log for more details. Ett okänt fel har uppstått. Se loggen för mer information. - + (64-bit) (64-bitar) - + (32-bit) (32-bitar) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Closing software... Stänger programvara... - + Save Data Sparat data - + Mod Data Mod-data - + Error Opening %1 Folder Fel vid öppning av mappen %1 - - + + Folder does not exist! Mappen finns inte! - + Remove Installed Game Contents? Ta bort installerat spelinnehåll? - + Remove Installed Game Update? Ta bort installerad speluppdatering? - + Remove Installed Game DLC? Ta bort installerat spel-DLC? - + Remove Entry Ta bort post - + Delete OpenGL Transferable Shader Cache? Ta bort OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? Ta bort Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? Ta bort alla Transferable Shader Caches? - + Remove Custom Game Configuration? Ta bort anpassad spelkonfiguration? - + Remove Cache Storage? Ta bort cachelagring? - + Remove File Ta bort fil - + Remove Play Time Data Ta bort data om speltid - + Reset play time? Nollställ speltid? - - + + RomFS Extraction Failed! Extrahering av RomFS misslyckades! - + There was an error copying the RomFS files or the user cancelled the operation. Det uppstod ett fel vid kopieringen av RomFS-filerna eller så avbröt användaren åtgärden. - + Full Fullständigt - + Skeleton Skelett - + Select RomFS Dump Mode Välj dumpläge för RomFS - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. Välj hur du vill att RomFS ska dumpas. <br>Fullständigt kopierar alla filer till den nya katalogen, medan <br>skelett endast skapar katalogstrukturen. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root Det finns inte tillräckligt med ledigt utrymme på %1 för att extrahera RomFS. Frigör utrymme eller välj en annan dumpkatalog under Emulering > Konfigurera > System > Filsystem > Dumprot. - + Extracting RomFS... Extraherar RomFS... - - + + Cancel Avbryt - + RomFS Extraction Succeeded! Extrahering av RomFS lyckades! - + The operation completed successfully. Operationen slutfördes. - + Error Opening %1 Fel vid öppning av %1 - + Select Directory Välj katalog - + Properties Egenskaper - + The game properties could not be loaded. Spelegenskaperna kunde inte läsas in. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Körbar Switch-fil (%1);;Alla filer (*.*) - + Load File Läs in fil - + Open Extracted ROM Directory Öppna katalog för extraherad ROM - + Invalid Directory Selected Ogiltig katalog valdes - + The directory you have selected does not contain a 'main' file. Den katalog du har valt innehåller ingen ”main”-fil. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) Installerbar Switch-fil (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files Installera filer - + %n file(s) remaining %n fil återstår%n filer återstår - + Installing file "%1"... Installerar filen "%1"... - - + + Install Results Installationsresultat - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. För att undvika eventuella konflikter avråder vi användare från att installera basspel på NAND. Använd endast denna funktion för att installera uppdateringar och DLC. - + %n file(s) were newly installed %n ny fil installerades @@ -7673,7 +7712,7 @@ Använd endast denna funktion för att installera uppdateringar och DLC. - + %n file(s) were overwritten %n fil skrevs över @@ -7681,7 +7720,7 @@ Använd endast denna funktion för att installera uppdateringar och DLC. - + %n file(s) failed to install %n fil gick inte att installera @@ -7689,361 +7728,314 @@ Använd endast denna funktion för att installera uppdateringar och DLC. - + System Application Systemapplikation - + System Archive Systemarkiv - + System Application Update Uppdatering för systemapplikation - + Firmware Package (Type A) Firmware-paket (Type A) - + Firmware Package (Type B) Firmware-paket (Type B) - + Game Spel - + Game Update Speluppdatering - + Game DLC DLC för spel - + Delta Title Deltatitel - + Select NCA Install Type... Välj NCA-installationstyp... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) Välj vilken typ av titel du vill installera denna NCA som: (I de flesta fall är standardinställningen ”Spel” tillräcklig.) - + Failed to Install Misslyckades med att installera - + The title type you selected for the NCA is invalid. Den titeltypen du valt för NCA är ogiltig. - + File not found Filen hittades inte - + File "%1" not found Filen "%1" hittades inte - + OK Ok - + Function Disabled Funktion inaktiverad - + Compatibility list reporting is currently disabled. Check back later! Rapportering till kompatibilitetslistan är för närvarande inaktiverad. Kom tillbaka senare! - + Error opening URL Fel vid öppning av URL - + Unable to open the URL "%1". Det går inte att öppna URL:en ”%1”. - + TAS Recording TAS-inspelning - + Overwrite file of player 1? Skriv över fil för spelare 1? - + Invalid config detected Ogiltig konfiguration upptäcktes - + Handheld controller can't be used on docked mode. Pro controller will be selected. Handhållen kontroller kan inte användas i dockat läge. Pro-kontrollern kommer att väljas. - - + + Amiibo Amiibo - - + + The current amiibo has been removed Den aktuella amiibo har tagits bort. - + Error Fel - - + + The current game is not looking for amiibos Det aktuella spelet letar inte efter amiibos - + Amiibo File (%1);; All Files (*.*) Amiibo-fil (%1);; Alla filer (*.*) - + Load Amiibo Läs in Amiibo - + Error loading Amiibo data Fel vid läsning av Amiibo-data - + The selected file is not a valid amiibo Den valda filen är inte en giltig amiibo. - + The selected file is already on use Den valda filen används redan - + An unknown error occurred Ett okänt fel uppstod - - - Keys not installed - Nycklar inte installerade - - - - - Install decryption keys and restart Eden before attempting to install firmware. - Installera avkrypteringsnycklar och starta om Eden innan du försöker installera firmware. - - - - Select Dumped Firmware Source Location - Välj plats för dumpad firmware-källa - - - - Select Dumped Firmware ZIP - Välj dumpad firmware-ZIP - - - - Zipped Archives (*.zip) - Zippade arkiv (*.zip) - - - - Firmware cleanup failed - Uppstädning av firmware misslyckades - - - - Failed to clean up extracted firmware cache. -Check write permissions in the system temp directory and try again. -OS reported error: %1 - Det gick inte att rensa den extraherade firmware-cachen. -Kontrollera skrivbehörigheten i systemets temporära katalog och försök igen. -OS rapporterade fel: %1 - - - + No firmware available Ingen firmware tillgänglig - + Firmware Corrupted Firmware är skadat - + Unknown applet Okänd applet - + Applet doesn't map to a known value. Appleten mappar inte till ett känt värde. - + Record not found Posten hittades inte - + Applet not found. Please reinstall firmware. Appleten hittades inte. Installera om fast programvara. - + Capture Screenshot Ta skärmbild - + PNG Image (*.png) PNG-bild (*.png) - + TAS state: Running %1/%2 TAS-tillstånd: Kör %1/%2 - + TAS state: Recording %1 TAS-tillstånd: Spelar in %1 - + TAS state: Idle %1/%2 TAS-tillstånd: Overksam %1/%2 - + TAS State: Invalid TAS-tillstånd: Ogiltig - + &Stop Running &Stoppa körning - + Stop R&ecording Stoppa i&nspelning - + Building: %n shader(s) Bygger: %n shaderBygger: %n shaders - + Scale: %1x %1 is the resolution scaling factor Skala: %1x - + Speed: %1% / %2% Hastighet: %1% / %2% - + Speed: %1% Hastighet: %1% - + Game: %1 FPS Spel: %1 bilder/s - + Frame: %1 ms Bildruta: %1 ms - - - FSR - FSR - - - + NO AA NO AA - + VOLUME: MUTE VOLYM: TYST - + VOLUME: %1% Volume percentage (e.g. 50%) VOLYM: %1% - + Derivation Components Missing Deriveringskomponenter saknas - + Decryption keys are missing. Install them now? Avkrypteringsnycklar saknas. Installera dem nu? - + Wayland Detected! Wayland upptäcktes! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -8054,74 +8046,74 @@ Det rekommenderas att använda X11 istället. Vill du tvinga det för framtida starter? - + Use X11 Använd X11 - + Continue with Wayland Fortsätt med Wayland - + Don't show again Visa inte igen - + Restart Required Omstart krävs - + Restart Eden to apply the X11 backend. Starta om Eden för att tillämpa X11-backend. - + Slow Långsam - + Turbo Turbo - + Unlocked Upplåst - + Select RomFS Dump Target Välj RomFS-dumpmål - + Please select which RomFS you would like to dump. Välj vilken RomFS du vill dumpa. - + Are you sure you want to close Eden? Är du säker på att du vill stänga Eden? - - - + + + Eden Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. Är du säker på att du vill stoppa emuleringen? Alla osparade framsteg kommer att gå förlorade. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? @@ -8189,6 +8181,11 @@ Vill du kringgå detta och stänga ändå? ScaleForce ScaleForce + + + FSR + FSR + Area @@ -8200,52 +8197,62 @@ Vill du kringgå detta och stänga ändå? MMPX - + + SGSR + + + + + SGSR EdgeDir + + + + Docked Dockad - + Handheld Handhållen - + Fast Snabb - + Balanced Balanserad - + Accurate Noggrann - + Vulkan Vulkan - + OpenGL GLSL OpenGL GLSL - + OpenGL SPIRV OpenGL SPIRV - + OpenGL GLASM OpenGL GLASM - + Null Null @@ -8288,7 +8295,7 @@ Om du vill rensa upp bland de filer som låg kvar på den gamla dataplatsen kan %1 - + Data was migrated successfully. Datamigrering lyckades. @@ -9146,47 +9153,47 @@ p, li { white-space: pre-wrap; } %1 spelar %2 - + Play Time: %1 Speltid: %1 - + Never Played Aldrig spelat - + Version: %1 Version: %1 - + Version: 1.0.0 Version: 1.0.0 - + Installed SD Titles Installerade SD-titlar - + Installed NAND Titles Installerade NAND-titlar - + System Titles Systemtitlar - + Add New Game Directory Lägg till ny spelkatalog - + Favorites Favoriter @@ -9307,47 +9314,47 @@ p, li { white-space: pre-wrap; } QtCommon::Content - + Game Requires Firmware Spelet kräver firmware - + The game you are trying to launch requires firmware to boot or to get past the opening menu. Please <a href='https://yuzu-mirror.github.io/help/quickstart'>dump and install firmware</a>, or press "OK" to launch anyways. Spelet som du försöker starta kräver firmware för att starta eller komma förbi startmenyn. <a href='https://yuzu-mirror.github.io/help/quickstart'>Dumpa och installera firmware</a> eller tryck på ”OK” för att starta ändå. - + Installing Firmware... Installerar firmware... - - - - - + + + + + Cancel Avbryt - + Firmware Install Failed Installation av firmware misslyckades - + Firmware Install Succeeded Installation av firmware lyckades - + Firmware integrity verification failed! Verifieringen av firmwareintegriteten misslyckades! - - + + Verification failed for the following files: %1 @@ -9356,207 +9363,240 @@ p, li { white-space: pre-wrap; } %1 - - + + Verifying integrity... Verifierar integritet... - - + + Integrity verification succeeded! Integritetsverifieringen lyckades! - - + + The operation completed successfully. Operationen slutfördes utan problem. - - + + Integrity verification failed! Integritetsverifieringen misslyckades! - + File contents may be corrupt or missing. Filens innehåll kan vara skadat eller saknas. - + Integrity verification couldn't be performed Integritetsverifiering kunde inte utföras - + Firmware installation cancelled, firmware may be in a bad state or corrupted. File contents could not be checked for validity. Firmwareinstallationen avbruten, firmware kan vara i dåligt skick eller skadad. Filens innehåll kunde inte kontrolleras för giltighet. - + Select Dumped Keys Location Välj plats för dumpade nycklar - + Decryption Keys install succeeded Installation av avkrypteringsnycklar lyckades - + Decryption Keys install failed Installationen av avkrypteringsnycklar misslyckades - + Orphaned Profiles Detected! Föräldralösa profiler upptäcktes! - + UNEXPECTED BAD THINGS MAY HAPPEN IF YOU DON'T READ THIS!<br>Eden has detected the following save directories with no attached profile:<br>%1<br><br>The following profiles are valid:<br>%2<br><br>Click "OK" to open your save folder and fix up your profiles.<br>Hint: copy the contents of the largest or last-modified folder elsewhere, delete all orphaned profiles, and move your copied contents to the good profile.<br><br>Still confused? See the <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/Orphaned.md'>help page</a>.<br> OVÄNTADE PROBLEM KAN UPPSTÅ OM DU INTE LÄSER DETTA! <br>Eden har upptäckt följande sparningskataloger utan bifogade profiler:<br>%1<br><br>Följande profiler är giltiga:<br>%2<br><br>Klicka på ”OK” för att öppna din sparningsmapp och fixa dina profiler.<br>Tips: kopiera innehållet i den största eller senast ändrade mappen till en annan plats, ta bort alla övergivna profiler och flytta det kopierade innehållet till den giltiga profilen.<br><br>Fortfarande förvirrad? Se hjälpsidan<a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/Orphaned.md'>.<br> - + Really clear data? Verkligen tömma data? - + Important data may be lost! Viktig data kan gå förlorad! - + Are you REALLY sure? Är du VERKLIGEN säker? - + Once deleted, your data will NOT come back! Only do this if you're 100% sure you want to delete this data. När dina data har raderats kan de INTE återställas! Gör detta endast om du är 100% säker på att du vill radera dessa data. - + Clearing... Tömmer... - + Select Export Location Välj exportplats - + %1.zip %1.zip - - + + + Zipped Archives (*.zip) Zippade arkiv (*.zip) - + Exporting data. This may take a while... Exporterar data. Detta kan ta en stund... - + Exporting Exporterar - + Exported Successfully Exporten lyckades - + Data was exported successfully. Data har exporterats. - + Export Cancelled Exporten avbröts - + Export was cancelled by the user. Exporten avbröts av användaren. - + Export Failed Exporten misslyckades - + Ensure you have write permissions on the targeted directory and try again. Kontrollera att du har skrivbehörighet till den aktuella katalogen och försök igen. - + Select Import Location Välj importplats - + Import Warning Importvarning - + All previous data in this directory will be deleted. Are you sure you wish to proceed? Alla tidigare data i denna katalog kommer att raderas. Är du säker på att du vill fortsätta? - + Importing data. This may take a while... Importerar data. Detta kan ta en stund... - + Importing Importerar - + Imported Successfully Importen lyckades - + Data was imported successfully. Data har importerats. - + Import Cancelled Importen avbröts - + Import was cancelled by the user. Importen avbröts av användaren. - + Import Failed Importen misslyckades - + Ensure you have read permissions on the targeted directory and try again. Kontrollera att du har läsbehörighet till den aktuella katalogen och försök igen. + + + Keys not installed + + + + + Install decryption keys and restart Eden before attempting to install firmware. + + + + + Select Dumped Firmware Source Location + + + + + Select Dumped Firmware ZIP + + + + + Firmware cleanup failed + + + + + Failed to clean up extracted firmware cache. +Check write permissions in the system temp directory and try again. +OS reported error: %1 + + QtCommon::FS @@ -9805,72 +9845,72 @@ Vill du manuellt välja en portabel mapp att använda? Metadatacachen kunde inte tas bort. Den kan vara i bruk eller finns inte. - + Create Shortcut Skapa genväg - + Do you want to launch the game in fullscreen? Vill du starta spelet i helskärm? - + Shortcut Created Genväg skapad - + Successfully created a shortcut to %1 Skapade en genväg till %1 - + Shortcut may be Volatile! Genvägen kan vara instabil! - + This will create a shortcut to the current AppImage. This may not work well if you update. Continue? Detta skapar en genväg till den aktuella AppImage. Detta kanske inte fungerar bra om du uppdaterar. Vill du fortsätta? - + Failed to Create Shortcut Misslyckades med att skapa genväg - + Failed to create a shortcut to %1 Misslyckades med att skapa en genväg till %1 - + Create Icon Skapa ikon - + Cannot create icon file. Path "%1" does not exist and cannot be created. Det går inte att skapa ikonfilen. Sökvägen ”%1” finns inte och kan inte skapas. - + No firmware available Inget firmware tillgängligt - + Please install firmware to use the home menu. Installera firmware för att använda hemmenyn. - + Home Menu Applet Applet för hemmeny - + Home Menu is not available. Please reinstall firmware. Hemmenyn är inte tillgänglig. Installera om firmware. @@ -9878,37 +9918,37 @@ Vill du manuellt välja en portabel mapp att använda? QtCommon::Mod - + Mod Name Modnamn - + What should this mod be called? Vad ska denna mod heta? - + RomFS RomFS - + ExeFS/Patch ExeFS/Patch - + Cheat Fusk - + Mod Type Modtyp - + Could not detect mod type automatically. Please manually specify the type of mod you downloaded. Most mods are RomFS mods, but patches (.pchtxt) are typically ExeFS mods. @@ -9917,18 +9957,18 @@ Most mods are RomFS mods, but patches (.pchtxt) are typically ExeFS mods. - - + + Mod Extract Failed Mod-extrahering misslyckades - + Failed to create temporary directory %1 Det gick inte att skapa den tillfälliga katalogen %1 - + Zip file %1 is empty Zip-filen %1 är tom @@ -10630,65 +10670,65 @@ Om du väljer ”Från Eden” tas tidigare sparade data bort som lagrats i Ryuj %1 finns tillgänglig för hämtning. - + New Version Location - + All Files (*.*) Alla filer (*.*) - - - + + + Failed to save file Misslyckades med att spara filen - + Could not open file %1 for writing. Kunde inte öppna filen %1 för skrivning. - + Downloading... Hämtar ner... - + Cancel Avbryt - + Could not write to file %1. - + Could not commit to file %1. - + Failed to download file - + Could not download from %1%2 Error code: %3 - + Download Complete - + Successfully downloaded %1. Would you like to open it? diff --git a/dist/languages/tr_TR.ts b/dist/languages/tr_TR.ts index 5cc62a47d8..256e45ccaf 100644 --- a/dist/languages/tr_TR.ts +++ b/dist/languages/tr_TR.ts @@ -719,8 +719,8 @@ Options lower than 1X can cause artifacts. - Determines how sharpened the image will look using FSR's dynamic contrast. - FSR'ın dinamik kontrast teknolojisini kullanarak, görüntünün ne kadar keskinleştirileceğini belirler. + Determines how sharpened the image will look using FSR's or SGSR's dynamic contrast. + @@ -772,35 +772,35 @@ Disabling it is only intended for debugging. Gölgelendiriclerin sonraki oyun açılışlarında daha hızlı yüklenmesi için depolama alanına kaydedilmesine olanak tanır. Devre dışı bırakılması yalnızca hata ayıklama amaçlıdır. - + Use asynchronous GPU emulation - + Uses an extra CPU thread for rendering. This option should always remain enabled. - + NVDEC emulation: NVDEC emülasyonu: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. Videoların nasıl çözüleceğini belirtir. Kod çözme için CPU veya GPU kullanabilir veya hiç kod çözme işlemi yapmayabilir (videolarda siyah ekran). Çoğu durumda GPU ile kod çözme en iyi performansı sağlar. - + ASTC Decoding Method: ASTC Kod Çözme Yöntemi - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -809,55 +809,66 @@ stuttering but may present artifacts. Bu seçenek ASTC dokularının nasıl çözüleceğini kontrol eder. CPU: Kod çözme için işlemciyi kullanır. GPU: ASTC dokularını çözmek için GPU'nun hesaplama gölgelendiricilerini kullanır (önerilir). CPU Asenkron: ASTC dokularını talep üzerine çözmek için işlemciyi kullanır. ASTC kod çözme kaynaklı takılmaları giderir ancak görsel bozulmalara neden olabilir. - + ASTC Recompression Method: ASTC Yeniden Sıkıştırma Yöntemi - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. Çoğu GPU, ASTC dokuları için doğrudan desteğe sahip değildir ve bir ara formata (RGBA8) açılmalıdır. BC1/BC3: Ara format BC1 veya BC3 formatında yeniden sıkıştırılarak VRAM tasarrufu sağlar ancak görüntü kalitesini düşürür. - + Frame Pacing Mode (Vulkan only) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. - + VRAM Usage Mode: VRAM Kullanım Modu - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. Emülatörün belleği korumayı mı yoksa performans için mevcut video belleğini maksimum düzeyde kullanmayı mı tercih edeceğini seçer. Agresif mod, kayıt yazılımları gibi diğer uygulamaların performansını etkileyebilir. - + Skip CPU Inner Invalidation CPU Geçersiz Kılma'yı Atla - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. Bellek güncellemeleri sırasında belirli önbellek geçersiz kılma işlemlerini atlayarak işlemci kullanımını azaltır ve gecikmeyi iyileştirir. Bu, hafif çökmelere neden olabilir. - + + Anti-Flicker + + + + + Forces GPU fence callbacks to wait for submitted GPU work. +Use with Fast GPU mode, to avoid flicker with lower performance impact. + + + + VSync Mode: VSync Modu: - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -865,1362 +876,1382 @@ Immediate (no synchronization) presents whatever is available and can exhibit te FIFO (VSync) kare düşürmez veya yırtılma göstermez ancak ekran yenileme hızıyla sınırlıdır. FIFO Relaxed, yavaşlamadan toparlanırken yırtılmaya izin verir. Mailbox, FIFO'dan daha düşük gecikmeye sahip olabilir ve yırtılma yapmaz ancak kare düşürebilir. Immediate (senkronizasyon yok), mevcut olanı anında sunar ve yırtılmalara neden olabilir. - + Sync Memory Operations Bellek İşlemlerini Senkronize Et - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. Hesaplama ve bellek işlemleri arasında veri tutarlılığı sağlar. Bu seçenek oyunlardaki sorunları giderir ancak performansı düşürebilir. Unreal Engine 4 oyunları genellikle bundan en önemli ölçüde etkilenenlerdir. - + Enable asynchronous presentation (Vulkan only) Asenkron sunumu etkinleştir (Yalnızca Vulkan) - + Slightly improves performance by moving presentation to a separate CPU thread. Sunum işlemini ayrı bir işlemci iş parçacığına taşıyarak performansı biraz artırır. - + Force maximum clocks (Vulkan only) En yüksek hızı zorla (Yalnızca Vulkan için) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. Grafik komutlarını beklerken GPU'nun hızının düşmesini engellemek için arka planda görev yürütür - + Anisotropic Filtering: Anisotropic Filtering: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. Eğik açılardaki doku oluşturma kalitesini kontrol eder. Çoğu grafik kartında 16x olarak ayarlanması güvenlidir. - + GPU Mode: Grafik Kartı Modu - + Controls the GPU emulation mode. Most games render fine with Fast or Balanced modes, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. Grafik kartı emülasyon modunu kontrol eder. Çoğu oyun Hızlı veya Dengeli modlarda sorunsuz çalışır, ancak bazıları için hala Doğru modu gereklidir. Parçacıklar genellikle yalnızca Doğru modda düzgün görüntülenir. - + DMA Accuracy: DMA Doğruluğu: - + Controls the DMA precision accuracy. Safe precision fixes issues in some games but may degrade performance. DMA'in hassasiyet doğruluğunu yönetir. Güvenli hassasiyet, bazı oyunlardaki sorunları giderir, fakat performansı düşürebilir. - + Enable asynchronous shader compilation Asenkron gölgelendirici derlemeyi etkinleştir - + May reduce shader stutter. Gölgelendirici/shader takılmalarını azaltabilir. - + Fast GPU Time Hızlı Grafik Kartı Süresi - + Overclocks the emulated GPU to increase dynamic resolution and render distance. Use 256 for maximal performance and 512 for maximal graphics fidelity. Dinamik çözünürlüğü ve çizim mesafesini artırmak için emüle edilen grafik kartına hız aşırtma uygular. Maksimum performans için 256, maksimum grafik doğruluğu için 512 kullanın. - + GPU Unswizzle - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. - + GPU Unswizzle Max Texture Size - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. - + GPU Unswizzle Stream Size - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. - + GPU Unswizzle Chunk Size - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. - + Use Vulkan pipeline cache Vulkan pipeline önbelleği kullan - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. Grafik kartı üreticisine özel işlem hattı önbelleğini etkinleştirir. Bu seçenek, Vulkan sürücüsünün işlem hattı önbellek dosyalarını dahili olarak saklamadığı durumlarda gölgelendirici yükleme süresini önemli ölçüde iyileştirebilir. - + Enable Compute Pipelines (Intel Vulkan Only) Hesaplama İşlem Hatlarını Etkinleştir (Yalnızca Intel Vulkan) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. Bazı oyunlar için gereklidir. Bu ayar yalnızca Intel'in tescilli sürücüleri için mevcuttur ve etkinleştirilirse çökmeye neden olabilir. Hesaplama işlem hatları diğer tüm sürücülerde her zaman etkindir. - + Enable Reactive Flushing Reaktif Temizlemeyi Etkinleştir - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. Tahminli temizleme yerine reaktif temizleme kullanarak daha doğru bellek senkronizasyonu sağlar. - + Sync to framerate of video playback Video oynatma kare hızına senkronize et - + Run the game at normal speed during video playback, even when the framerate is unlocked. Kare hızı kilidi açık olsa bile video oynatımı sırasında oyunu normal hızda çalıştırır. - + Barrier feedback loops Bariyer geri besleme döngüleri - + Improves rendering of transparency effects in specific games. Belirli oyunlarda şeffaflık efektlerinin oluşturulmasını iyileştirir. - + Enable buffer history - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. - + Fix bloom effects - + Removes bloom in Burnout. - + Enable Legacy Rescale Pass - + May fix rescale issues in some games by relying on behavior from the previous implementation. Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. - + Extended Dynamic State Genişletilmiş Dinamik Durum - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. Genişletilmiş Dinamik Durumda kullanılabilecek özelliklerin sayısını kontrol eder. Daha yüksek durumlar daha fazla özelliğe izin verir ve performansı artırabilir, ancak ek grafik sorunlarına neden olabilir. - + Vertex Input Dynamic State Vertex Dinamik Durumu - + Enables vertex input dynamic state feature for better quality and performance. Daha iyi kalite ve performans için Vertex dinamik durum özelliğini etkinleştirir. - + Sample Shading Örnek Gölgelendirme - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. Parça gölgelendiricinin, her parça için bir kez yerine çoklu örneklenmiş bir parçadaki her örnek için yürütülmesine olanak tanır. Performans pahasına grafik kalitesini artırır. Daha yüksek değerler kaliteyi artırır ancak performansı düşürür. - + RNG Seed RNG çekirdeği - + Controls the seed of the random number generator. Mainly used for speedrunning. Rastgele sayı üretecinin tohumunu kontrol eder. Esas olarak hızlı bitirme denemeleri için kullanılır. - + Device Name Cihaz İsmi - + The name of the console. Konsolun adı - + + Homebrew Args + + + + + Command-line arguments passed to homebrew at launch (e.g. -noglsl). + + + + Custom RTC Date: Özel RTC Tarihi - + This option allows to change the clock of the console. Can be used to manipulate time in games. Bu seçenek konsolun saatini değiştirmeye olanak tanır. Oyunlarda zamanı manipüle etmek için kullanılabilir. - + The number of seconds from the current unix time Mevcut unix zamanından itibaren saniye sayısı - + Language: Dil: - + This option can be overridden when region setting is auto-select Bölge ayarı otomatik seçim olduğunda bu seçenek geçersiz kılınabilir. - + Region: Bölge: - + The region of the console. Konsolun bölgesi - + Time Zone: Saat Dilimi: - + The time zone of the console. Konsolun saat dilimi - + Sound Output Mode: Ses Çıkış Modu: - + Console Mode: Konsol Modu: - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. Konsolun Yerleşik veya El Modunda olup olmadığını seçer. Oyunlar bu ayara bağlı olarak çözünürlüklerini, detaylarını ve desteklenen kontrolcülerini değiştirecektir. El Moduna ayarlamak, düşük seviyeli sistemler için performansı artırmaya yardımcı olabilir. - + Prompt for user profile on boot Açılışta kullanıcı profili için sor - + Useful if multiple people use the same PC. Aynı bilgisayarı birden fazla kişi kullanıyorsa yararlıdır. - + Pause when not in focus Odaklı değilken duraklat - + Pauses emulation when focusing on other windows. Diğer pencerelere odaklanıldığında emülasyonu duraklatır. - + Confirm before stopping emulation Emülasyonu durdurmadan önce onayla - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. Emülasyonu durdurma onayı isteklerini/istemlerini geçersiz kılar. Etkinleştirildiğinde, bu tür istekleri/istemleri atlar ve emülasyonu doğrudan/dirket olarak kapatır. - + Hide mouse on inactivity Hareketsizlik durumunda imleci gizle - + Hides the mouse after 2.5s of inactivity. 2,5 saniye hareketsizlikten sonra fareyi gizler. - + Disable controller applet Kontrolcü aplikasyonunu devre dışı bırak - + Forcibly disables the use of the controller applet in emulated programs. When a program attempts to open the controller applet, it is immediately closed. Emüle edilen programlarda kontrolcü aplikasyonunun kullanımını zorla devre dışı bırakır. Bir program kontrolcü aplikasyonunu açmaya çalıştığında, aplikasyon anında kapatılır. - + Check for updates Güncellemeleri Kontrol Et - + Whether or not to check for updates upon startup. Başlangıçta güncellemelerin kontrol edilip edilmeyeceği. - + Enable Gamemode Oyun Modunu/Gamemode Etkinleştir - + Force X11 as Graphics Backend Grafik arka ucu olarak X11'i zorla - + Custom frontend Özel ön yüz - + Real applet Gerçek aplikasyon - + Never Asla - + On Load Yüklemede - + Always Her zaman - + CPU CPU - + GPU GPU - + CPU Asynchronous Asenkron CPU - + Uncompressed (Best quality) Sıkıştırılmamış (En iyi kalite) - + BC1 (Low quality) BC1 (Düşük kalite) - + BC3 (Medium quality) BC3 (Orta kalite) - - + + Auto Otomatik - + 30 FPS - + 60 FPS - + 90 FPS - + 120 FPS - + Conservative Muhafazakar - + Aggressive Agresif - + Vulkan Vulkan - + OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) - + Null Boş - + Fast Hızlı - + Balanced Dengeli - - + + Accurate Doğru - - + + Default Varsayılan - + Unsafe (fast) Güvenli Değil (hızlı) - + Safe (stable) Güvenli (Stabil) - + Unsafe Güvensiz - + Paranoid (disables most optimizations) Paranoya (çoğu optimizasyonu kapatır) - + Debugging Hata ayıklama - + Dynarmic Dinamik - + NCE NCE - + Borderless Windowed Kenarlıksız Tam Ekran - + Exclusive Fullscreen Ayrılmış Tam Ekran - + No Video Output Video Çıkışı Yok - + CPU Video Decoding CPU Video Decoding - + GPU Video Decoding (Default) GPU Video Decoding (Varsayılan) - + 0.25X (180p/270p) [EXPERIMENTAL] 0.25X (180p/270p) [DENEYSEL] - + 0.5X (360p/540p) [EXPERIMENTAL] 0.5X (360p/540p) [DENEYSEL] - + 0.75X (540p/810p) [EXPERIMENTAL] 0.75X (540p/810p) [DENEYSEL] - + 1X (720p/1080p) 1X (720p/1080p) - + 1.25X (900p/1350p) [EXPERIMENTAL] 1.25X (900p/1350p) [DENEYSEL] - + 1.5X (1080p/1620p) [EXPERIMENTAL] 1.5X (1080p/1620p) [DENEYSEL] - + 2X (1440p/2160p) 2X (1440p/2160p) - + 3X (2160p/3240p) 3X (2160p/3240p) - + 4X (2880p/4320p) 4X (2880p/4320p) - + 5X (3600p/5400p) 5X (3600p/5400p) - + 6X (4320p/6480p) 6X (4320p/6480p) - + 7X (5040p/7560p) 7X (5040p/7560p) - + 8X (5760p/8640p) 8X (5760p/8640p) - + Nearest Neighbor En Yakın Komşu Algoritması - + Bilinear Bilinear - + Bicubic Bicubic - + Gaussian Gausyen - + Lanczos Lanczos - + ScaleForce ScaleForce - + AMD FidelityFX Super Resolution AMD FidelityFX Süper Çözünürlük - + Area Area - + MMPX MMPX - + Zero-Tangent Zero-Tangent - + B-Spline B-Spline - + Mitchell Mitchell - + Spline-1 Spline-1 - - + + Snapdragon Game Super Resolution + + + + + Snapdragon Game Super Resolution EdgeDir + + + + + None Yok - + FXAA FXAA - + SMAA SMAA - + Default (16:9) Varsayılan (16:9) - + Force 4:3 4:3'e Zorla - + Force 21:9 21:9'a Zorla - + Force 16:10 16:10'a Zorla - + Stretch to Window Ekrana Sığdır - + Automatic Otomatik - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x - + 32x 32x - + 64x 64x - + Japanese (日本語) Japonca (日本語) - + American English Amerikan İngilizcesi - + French (français) Fransızca (français) - + German (Deutsch) Almanca (Deutsch) - + Italian (italiano) İtalyanca (italiano) - + Spanish (español) İspanyolca (español) - + Chinese Çince - + Korean (한국어) Korece (한국어) - + Dutch (Nederlands) Flemenkçe (Nederlands) - + Portuguese (português) Portekizce (português) - + Russian (Русский) Rusça (Русский) - + Taiwanese Tayvanca - + British English İngiliz İngilizcesi - + Canadian French Kanada Fransızcası - + Latin American Spanish Latin Amerika İspanyolcası - + Simplified Chinese Basitleştirilmiş Çince - + Traditional Chinese (正體中文) Geleneksel Çince (正體中文) - + Brazilian Portuguese (português do Brasil) Brezilya Portekizcesi (português do Brasil) - + Polish (polska) - + Thai (แบบไทย) - - + + Japan Japonya - + USA ABD - + Europe Avrupa - + Australia Avustralya - + China Çin - + Korea Kore - + Taiwan Tayvan - + Auto (%1) Auto select time zone Otomatik (%1) - + Default (%1) Default time zone Varsayılan (%1) - + CET CET - + CST6CDT CST6CDT - + Cuba Küba - + EET EET - + Egypt Mısır - + Eire İrlanda - + EST EST - + EST5EDT EST5EDT - + GB GB - + GB-Eire GB-İrlanda - + GMT GMT - + GMT+0 GMT+0 - + GMT-0 MT-0 - + GMT0 GMT0 - + Greenwich Greenwich - + Hongkong Hong Kong - + HST HST - + Iceland İzlanda - + Iran İran - + Israel İsrail - + Jamaica Jamaika - + Kwajalein Kwajalein - + Libya Libya - + MET MET - + MST MST - + MST7MDT MST7MDT - + Navajo Navaho - + NZ Yeni Zelanda - + NZ-CHAT Chatham Adaları - + Poland Polonya - + Portugal Portekiz - + PRC PRC - + PST8PDT PST8PDT - + ROC ROC - + ROK ROK - + Singapore Singapur - + Turkey Türkiye - + UCT UCT - + Universal Evrensel - + UTC UTC - + W-SU W-SU - + WET WET - + Zulu Zulu - + Mono Mono - + Stereo Stereo - + Surround Surround - + 4GB DRAM (Default) 4GB DRAM (Varsayılan) - + 6GB DRAM (Unsafe) 6GB DRAM (Güvenli Değil) - + 8GB DRAM 8GB DRAM - + 10GB DRAM (Unsafe) 10GB DRAM (Güvenli Değil) - + 12GB DRAM (Unsafe) 12GB DRAM (Güvenli Değil) - + Docked Dock Modu Aktif - + Handheld Taşınabilir - - + + Off Kapalı - + Boost (1700MHz) Takviye (1700MHz) - + Fast (2000MHz) Hızlı (2000MHz) - + Always ask (Default) Her zaman sor (Varsayılan) - + Only if game specifies not to stop Sadece oyun durdurulmamasını belirtirse - + Never ask Asla sorma - - + + Medium (256) Orta (256) - - + + High (512) Yüksek (512) - + Very Small (16 MB) - + Small (32 MB) - + Normal (128 MB) - + Large (256 MB) - + Very Large (512 MB) - + Very Low (4 MB) - + Low (8 MB) - + Normal (16 MB) - + Medium (32 MB) - + High (64 MB) - + Very Low (32) - + Low (64) - + Normal (128) - + Disabled Devre Dışı - + ExtendedDynamicState 1 Genişletilmiş Dinamik Durum 1 - + ExtendedDynamicState 2 Genişletilmiş Dinamik Durum 2 - + ExtendedDynamicState 3 Genişletilmiş Dinamik Durum 3 - + Tree View - + Grid View @@ -3285,33 +3316,33 @@ Eski kayıt verilerini silmek ister misiniz? Arkaplan Rengi: - + % - FSR sharpening percentage (e.g. 50%) - % + FSR/SGSR sharpening percentage (e.g. 50%) + - + Off Kapalı - + VSync Off VSync Kapalı - + Recommended Önerilen - + On Açık - + VSync On Vsync Açık @@ -4440,7 +4471,7 @@ Eksenleri ters çevirmek için, önce joystickinizi dikey sonra yatay olarak har - + Configure Yapılandır @@ -4471,7 +4502,7 @@ Eksenleri ters çevirmek için, önce joystickinizi dikey sonra yatay olarak har - + Test Test @@ -4486,77 +4517,77 @@ Eksenleri ters çevirmek için, önce joystickinizi dikey sonra yatay olarak har Server'ı Kaldır - + %1:%2 %1:%2 - - - - - - + + + + + + Eden Eden - + Port number has invalid characters Port numarasında geçersiz karakterler var - + Port has to be in range 0 and 65353 Port 0 ila 65353 aralığında olmalıdır - + IP address is not valid IP adresi geçerli değil - + This UDP server already exists Bu UDP sunucusu zaten var - + Unable to add more than 8 servers 8'den fazla server eklenemez - + Testing Test Ediliyor - + Configuring Yapılandırılıyor - + Test Successful Test Başarılı - + Successfully received data from the server. Bilgi başarıyla sunucudan kaldırıldı. - + Test Failed Test Başarısız - + Could not receive valid data from the server.<br>Please verify that the server is set up correctly and the address and port are correct. Serverdan geçerli veri alınamadı.<br>Lütfen sunucunun doğru ayarlandığını ya da adres ve portun doğru olduğunu kontrol edin. - + UDP Test or calibration configuration is in progress.<br>Please wait for them to finish. UDP testi ya da yapılandırılması devrede.<br>Lütfen bitmesini bekleyin. @@ -4741,57 +4772,57 @@ Mevcut değerler sırasıyla %1 ve %2'dir. Bazı ayarlar yalnızca bir oyun çalışmadığında kullanılabilir. - + Add-Ons Eklentiler - + System Sistem - + CPU CPU - + Graphics Grafikler - + Adv. Graphics Gelişmiş Grafikler - + Ext. Graphics Ek Grafikler - + Audio Ses - + Input Profiles Kontrol Profilleri - + Network - + Applets - + Properties Özellikler @@ -5802,7 +5833,7 @@ Noktanın konumunu değiştirmek için sürükleyin ya da sayıların üstüne Bu dizin için veri içe aktarın. Bu biraz zaman alabilir ve MEVCUT TÜM VERİLERİ silecek! - + Calculating... Hesaplanıyor... @@ -6004,50 +6035,50 @@ Please go to Configure -> System -> Network and make a selection. GRenderWindow - - + + OpenGL not available! OpenGL kullanıma uygun değil! - + OpenGL shared contexts are not supported. OpenGL paylaşılan bağlam desteklenmiyor. - + Eden has not been compiled with OpenGL support. - - - + + + Error while initializing OpenGL! OpenGl başlatılırken bir hata oluştu! - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. GPU'nuz OpenGL desteklemiyor veya güncel bir grafik sürücüsüne sahip değilsiniz. - + Error while initializing OpenGL 4.6! OpenGl 4.6 başlatılırken bir hata oluştu! - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 GPU'nuz OpenGL 4.6'yı desteklemiyor veya güncel bir grafik sürücüsüne sahip değilsiniz.<br><br>GL Renderer:<br>%1 - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 GPU'nuz gereken bir yada daha fazla OpenGL eklentisini desteklemiyor Lütfen güncel bir grafik sürücüsüne sahip olduğunuzdan emin olun.<br><br>GL Renderer:<br>%1<br><br> Desteklenmeyen Eklentiler:<br>%2 - + This build doesn't have OpenGL support. @@ -6055,279 +6086,282 @@ Please go to Configure -> System -> Network and make a selection. GameList - + &Add New Game Directory - + Favorite Favori - + Start Game Oyunu Başlat - + Start Game without Custom Configuration Oyunu Özel Yapılandırma Olmadan Başlat - + Open Save Data Location Kayıt Dosyası Konumunu Aç - + Open Mod Data Location Mod Dosyası Konumunu Aç - + Open Transferable Pipeline Cache Transfer Edilebilir Pipeline Cache'ini Aç - + Link to Ryujinx - + Remove Kaldır - + Remove Installed Update Yüklenmiş Güncellemeleri Kaldır - + Remove All Installed DLC Yüklenmiş DLC'leri Kaldır - + Remove Custom Configuration Oyuna Özel Yapılandırmayı Kaldır - + Remove Cache Storage - + Remove OpenGL Pipeline Cache OpenGL Pipeline Cache'ini Kaldır - + Remove Vulkan Pipeline Cache Vulkan Pipeline Cache'ini Kaldır - + Remove All Pipeline Caches Bütün Pipeline Cache'lerini Kaldır - + Remove All Installed Contents Tüm Yüklenmiş İçeriği Kaldır - + Manage Play Time - + Edit Play Time Data - + Remove Play Time Data - - + + Dump RomFS RomFS Dump Et - + Dump RomFS to SDMC RomFS'i SDMC'ye çıkar. - + Verify Integrity - + Copy Title ID to Clipboard Title ID'yi Panoya Kopyala - + Navigate to GameDB entry GameDB sayfasına yönlendir - + Create Shortcut Kısayol Oluştur - + Add to Desktop Masaüstüne Ekle - + Add to Applications Menu Uygulamalar Menüsüne Ekl - + Configure Game - + Scan Subfolders Alt Klasörleri Tara - + Remove Game Directory Oyun Konumunu Kaldır - + ▲ Move Up ▲Yukarı Git - + ▼ Move Down ▼Aşağı Git - + Open Directory Location Oyun Dosyası Konumunu Aç - + Clear Temizle - - - Name - İsim - - - - Compatibility - Uyumluluk - - - - Add-ons - Eklentiler - - - - File type - Dosya türü - - - - Size - Boyut - - - - Play time - - GameListItemCompat - + Ingame Oyunda - + Game starts, but crashes or major glitches prevent it from being completed. Oyun başlatılabiliyor, fakat bariz hatalardan veya çökme sorunlarından dolayı bitirilemiyor. - + Perfect Mükemmel - + Game can be played without issues. Oyun sorunsuz bir şekilde oynanabiliyor. - + Playable Oynanabilir - + Game functions with minor graphical or audio glitches and is playable from start to finish. Oyun küçük grafik veya ses hatalarıyla çalışıyor ve baştan sona kadar oynanabilir. - + Intro/Menu İntro/Menü - + Game loads, but is unable to progress past the Start Screen. Oyun açılıyor, fakat ana menüden ileri gidilemiyor. - + Won't Boot Açılmıyor - + The game crashes when attempting to startup. Oyun açılmaya çalışıldığında çöküyor. - + Not Tested Test Edilmedi - + The game has not yet been tested. Bu oyun henüz test edilmedi. + + GameListModel + + + Name + + + + + Compatibility + + + + + Add-ons + + + + + File type + + + + + Size + + + + + Play time + + + GameListPlaceholder - + Double-click to add a new folder to the game list Oyun listesine yeni bir klasör eklemek için çift tıklayın. @@ -6335,17 +6369,17 @@ Please go to Configure -> System -> Network and make a selection. GameListSearchField - + %1 of %n result(s) %n sonucun %1'i%n sonucun %1'i - + Filter: Filtre: - + Enter pattern to filter Filtrelemek için bir düzen giriniz @@ -6840,1139 +6874,1099 @@ Debug Message: - + Game &Icon Size - + Reset Window Size to &720p Pencere Boyutunu &720p'ye Sıfırla - + Reset Window Size to 720p Pencere Boyutunu 720p'ye Sıfırla - + Reset Window Size to &900p Pencere Boyutunu &900p'ye Sıfırla - + Reset Window Size to 900p Pencere Boyutunu 900p'ye Sıfırla - + Reset Window Size to &1080p Pencere Boyutunu &1080p'ye Sıfırla - + Reset Window Size to 1080p Pencere Boyutunu 1080p'ye Sıfırla - + &Multiplayer &Çok Oyunculu - + &Tools &Aletler - + Am&iibo - + Launch &Applet - + &TAS &TAS - + &Create Home Menu Shortcut - + Install &Firmware - + &Help &Yardım - + &Install Files to NAND... &NAND'e Dosya Kur... - + L&oad File... &Dosyayı Yükle... - + Load &Folder... &Klasörü Yükle... - + E&xit &Çıkış - - + + &Pause &Duraklat - + &Stop Du&rdur - + &Verify Installed Contents &Kurulu İçerikleri Onayla - + &About Eden - + Single &Window Mode &Tek Pencere Modu - + Con&figure... &Yapılandır... - + Ctrl+, - + Enable Overlay Display Applet - + Show &Filter Bar &Filtre Çubuğu'nu Göster - + Show &Status Bar &Durum Çubuğu'nu Göster - + Show Status Bar Durum Çubuğunu Göster - + &Browse Public Game Lobby &Herkese Açık Oyun Lobilerine Göz At - + &Create Room &Oda Oluştur - + &Leave Room &Odadan Ayrıl - + &Direct Connect to Room &Odaya Direkt Bağlan - + &Show Current Room &Şu Anki Odayı Göster - + F&ullscreen &Tam Ekran - + &Restart &Yeniden Başlat - + Load/Remove &Amiibo... &Amiibo Yükle/Kaldır - + &Report Compatibility &Uyumluluk Bildir - + Open &Mods Page &Mod Sayfasını Aç - + Open &Quickstart Guide &Hızlı Başlangıç Kılavuzunu Aç - + &FAQ &SSS - + &Capture Screenshot &Ekran Görüntüsü Al - + &Album - + &Set Nickname and Owner - + &Delete Game Data - + &Restore Amiibo - + &Format Amiibo - + &Mii Editor - + &Configure TAS... &TAS'i Ayarla... - + Configure C&urrent Game... &Geçerli Oyunu Yapılandır... - - + + &Start B&aşlat - + &Reset &Sıfırla - - + + R&ecord K&aydet - + Open &Controller Menu - + Install Decryption &Keys - + &Home Menu - + &Desktop - + &Application Menu - + &Root Data Folder - + &NAND Folder - + &SDMC Folder - + &Mod Folder - + &Log Folder - + From Folder - + From ZIP - + &Eden Dependencies - + &Data Manager - + &Tree View - + &Grid View - + Game Icon Size - - + + None - + Show Game &Name - + Show &Performance Overlay - + + &Carousel View + + + + Small (32x32) - + Standard (64x64) - + Large (128x128) - + Full Size (256x256) - + Broken Vulkan Installation Detected Bozuk Vulkan Kurulumu Algılandı - + Vulkan initialization failed during boot. Açılış sırasında Vulkan başlatma işlemi başarısız oldu. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping Bir oyun çalıştırılıyor - + Loading Web Applet... Web Uygulaması Yükleniyor... - - + + Disable Web Applet Web Uygulamasını Devre Dışı Bırak - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. - + Unmute Sesi aç - + Mute Sessize al - + Reset Volume Sesi Sıfırla - + &Clear Recent Files &Son Dosyaları Temizle - + &Continue - + Warning: Outdated Game Format - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. - - + + Error while loading ROM! - + The ROM format is not supported. - + An error occurred initializing the video core. - + Eden has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-mirror.github.io/help/reference/log-files/'>How to Upload the Log File</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. - + (64-bit) - + (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit - + Closing software... - + Save Data - + Mod Data - + Error Opening %1 Folder - - + + Folder does not exist! - + Remove Installed Game Contents? - + Remove Installed Game Update? - + Remove Installed Game DLC? - + Remove Entry - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? - + Remove Cache Storage? - + Remove File - + Remove Play Time Data - + Reset play time? - - + + RomFS Extraction Failed! - + There was an error copying the RomFS files or the user cancelled the operation. - + Full - + Skeleton - + Select RomFS Dump Mode - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... - - + + Cancel - + RomFS Extraction Succeeded! - + The operation completed successfully. - + Error Opening %1 - + Select Directory - + Properties - + The game properties could not be loaded. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. - + Load File - + Open Extracted ROM Directory - + Invalid Directory Selected - + The directory you have selected does not contain a 'main' file. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files - + %n file(s) remaining - + Installing file "%1"... - - + + Install Results - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application - + System Archive - + System Application Update - + Firmware Package (Type A) - + Firmware Package (Type B) - + Game - + Game Update - + Game DLC - + Delta Title - + Select NCA Install Type... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) - + Failed to Install - + The title type you selected for the NCA is invalid. - + File not found - + File "%1" not found - + OK - + Function Disabled - + Compatibility list reporting is currently disabled. Check back later! - + Error opening URL URL açılırken hata oluştu - + Unable to open the URL "%1". - + TAS Recording TAS İşlemi Kaydı - + Overwrite file of player 1? - + Invalid config detected - + Handheld controller can't be used on docked mode. Pro controller will be selected. - - + + Amiibo Amiibo - - + + The current amiibo has been removed Mevcut Amiibo kaldırıldı - + Error - - + + The current game is not looking for amiibos - + Amiibo File (%1);; All Files (*.*) - + Load Amiibo - + Error loading Amiibo data - + The selected file is not a valid amiibo - + The selected file is already on use - + An unknown error occurred - - - Keys not installed - - - - - - Install decryption keys and restart Eden before attempting to install firmware. - - - - - Select Dumped Firmware Source Location - - - - - Select Dumped Firmware ZIP - - - - - Zipped Archives (*.zip) - - - - - Firmware cleanup failed - - - - - Failed to clean up extracted firmware cache. -Check write permissions in the system temp directory and try again. -OS reported error: %1 - - - - + No firmware available - + Firmware Corrupted Donanım Yazılımı/Firmware Bozuk - + Unknown applet - + Applet doesn't map to a known value. - + Record not found - + Applet not found. Please reinstall firmware. - + Capture Screenshot - + PNG Image (*.png) - + TAS state: Running %1/%2 TAS durumu: %1/%2 Çalışıyor - + TAS state: Recording %1 - + TAS state: Idle %1/%2 - + TAS State: Invalid - + &Stop Running - + Stop R&ecording - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor Ölçek: %1x - + Speed: %1% / %2% - + Speed: %1% - + Game: %1 FPS - + Frame: %1 ms - - - FSR - - - - + NO AA - + VOLUME: MUTE - + VOLUME: %1% Volume percentage (e.g. 50%) - + Derivation Components Missing - + Decryption keys are missing. Install them now? - + Wayland Detected! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -7980,74 +7974,74 @@ Would you like to force it for future launches? - + Use X11 - + Continue with Wayland - + Don't show again - + Restart Required - + Restart Eden to apply the X11 backend. - + Slow - + Turbo - + Unlocked - + Select RomFS Dump Target - + Please select which RomFS you would like to dump. - + Are you sure you want to close Eden? - - - + + + Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? @@ -8113,6 +8107,11 @@ Would you like to bypass this and exit anyway? ScaleForce + + + FSR + + Area @@ -8124,52 +8123,62 @@ Would you like to bypass this and exit anyway? - + + SGSR + + + + + SGSR EdgeDir + + + + Docked - + Handheld - + Fast - + Balanced - + Accurate - + Vulkan - + OpenGL GLSL - + OpenGL SPIRV - + OpenGL GLASM - + Null @@ -8202,7 +8211,7 @@ If you wish to clean up the files which were left in the old data location, you - + Data was migrated successfully. @@ -9057,47 +9066,47 @@ p, li { white-space: pre-wrap; } %1 %2'yi oynuyor - + Play Time: %1 - + Never Played - + Version: %1 - + Version: 1.0.0 - + Installed SD Titles Yüklenmiş SD Oyunları - + Installed NAND Titles Yüklenmiş NAND Oyunları - + System Titles Sistemde Yüklü Oyunlar - + Add New Game Directory Yeni Oyun Konumu Ekle - + Favorites Favoriler @@ -9218,253 +9227,286 @@ p, li { white-space: pre-wrap; } QtCommon::Content - + Game Requires Firmware - + The game you are trying to launch requires firmware to boot or to get past the opening menu. Please <a href='https://yuzu-mirror.github.io/help/quickstart'>dump and install firmware</a>, or press "OK" to launch anyways. - + Installing Firmware... - - - - - + + + + + Cancel - + Firmware Install Failed - + Firmware Install Succeeded - + Firmware integrity verification failed! - - + + Verification failed for the following files: %1 - - + + Verifying integrity... + + + + Integrity verification succeeded! + + - Integrity verification succeeded! - - - - - The operation completed successfully. - - + + Integrity verification failed! - + File contents may be corrupt or missing. - + Integrity verification couldn't be performed - + Firmware installation cancelled, firmware may be in a bad state or corrupted. File contents could not be checked for validity. - + Select Dumped Keys Location - + Decryption Keys install succeeded - + Decryption Keys install failed - + Orphaned Profiles Detected! - + UNEXPECTED BAD THINGS MAY HAPPEN IF YOU DON'T READ THIS!<br>Eden has detected the following save directories with no attached profile:<br>%1<br><br>The following profiles are valid:<br>%2<br><br>Click "OK" to open your save folder and fix up your profiles.<br>Hint: copy the contents of the largest or last-modified folder elsewhere, delete all orphaned profiles, and move your copied contents to the good profile.<br><br>Still confused? See the <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/Orphaned.md'>help page</a>.<br> - + Really clear data? - + Important data may be lost! - + Are you REALLY sure? - + Once deleted, your data will NOT come back! Only do this if you're 100% sure you want to delete this data. - + Clearing... - + Select Export Location - + %1.zip - - + + + Zipped Archives (*.zip) - + Exporting data. This may take a while... - + Exporting - + Exported Successfully - + Data was exported successfully. - + Export Cancelled - + Export was cancelled by the user. - + Export Failed - + Ensure you have write permissions on the targeted directory and try again. - + Select Import Location - + Import Warning - + All previous data in this directory will be deleted. Are you sure you wish to proceed? - + Importing data. This may take a while... - + Importing - + Imported Successfully - + Data was imported successfully. - + Import Cancelled - + Import was cancelled by the user. - + Import Failed - + Ensure you have read permissions on the targeted directory and try again. + + + Keys not installed + + + + + Install decryption keys and restart Eden before attempting to install firmware. + + + + + Select Dumped Firmware Source Location + + + + + Select Dumped Firmware ZIP + + + + + Firmware cleanup failed + + + + + Failed to clean up extracted firmware cache. +Check write permissions in the system temp directory and try again. +OS reported error: %1 + + QtCommon::FS @@ -9708,72 +9750,72 @@ Would you like to manually select a portable folder to use? - + Create Shortcut - + Do you want to launch the game in fullscreen? - + Shortcut Created - + Successfully created a shortcut to %1 - + Shortcut may be Volatile! - + This will create a shortcut to the current AppImage. This may not work well if you update. Continue? - + Failed to Create Shortcut - + Failed to create a shortcut to %1 - + Create Icon - + Cannot create icon file. Path "%1" does not exist and cannot be created. - + No firmware available - + Please install firmware to use the home menu. - + Home Menu Applet - + Home Menu is not available. Please reinstall firmware. @@ -9781,55 +9823,55 @@ Would you like to manually select a portable folder to use? QtCommon::Mod - + Mod Name - + What should this mod be called? - + RomFS - + ExeFS/Patch - + Cheat - + Mod Type - + Could not detect mod type automatically. Please manually specify the type of mod you downloaded. Most mods are RomFS mods, but patches (.pchtxt) are typically ExeFS mods. - - + + Mod Extract Failed - + Failed to create temporary directory %1 - + Zip file %1 is empty @@ -10523,65 +10565,65 @@ By selecting "From Eden", previous save data stored in Ryujinx will be - + New Version Location - + All Files (*.*) - - - + + + Failed to save file - + Could not open file %1 for writing. - + Downloading... - + Cancel - + Could not write to file %1. - + Could not commit to file %1. - + Failed to download file - + Could not download from %1%2 Error code: %3 - + Download Complete - + Successfully downloaded %1. Would you like to open it? diff --git a/dist/languages/uk.ts b/dist/languages/uk.ts index fe0651bdb1..d899ecd39f 100644 --- a/dist/languages/uk.ts +++ b/dist/languages/uk.ts @@ -725,8 +725,8 @@ Options lower than 1X can cause artifacts. - Determines how sharpened the image will look using FSR's dynamic contrast. - Визначає, наскільки різким буде виглядати зображення при використанні динамічного контрасту FSR. + Determines how sharpened the image will look using FSR's or SGSR's dynamic contrast. + Визначає, наскільки різким буде виглядати зображення при використанні динамічного контрасту FSR або SGSR. @@ -783,24 +783,24 @@ Disabling it is only intended for debugging. Вимкнення цього налаштування задумане лише для зневадження. - + Use asynchronous GPU emulation Використовувати асинхронну емуляцію ГП - + Uses an extra CPU thread for rendering. This option should always remain enabled. Використовує додатковий потік ЦП для візуалізації. Це налаштування повинно завжди залишатися увімкненим. - + NVDEC emulation: Емуляція NVDEC: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. @@ -809,12 +809,12 @@ In most cases, GPU decoding provides the best performance. У більшості випадків декодування за допомогою ГП забезпечує найкращу продуктивність. - + ASTC Decoding Method: Метод декодування ASTC: - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -826,12 +826,12 @@ stuttering but may present artifacts. Асинхронно ЦП: Використання ЦП для декодування ASTC-текстур по мірі їх викликів. Повністю усуває затримки декодування ASTC ціною проблем з візуалізацією, поки текстури декодуються. - + ASTC Recompression Method: Метод перестиснення ASTC: - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. @@ -839,44 +839,56 @@ BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, BC1/BC3: Проміжний формат буде перепаковано у формат BC1 або BC3 для збереження відеопам’яті, але це негатривно вплине на якість зображення. - + Frame Pacing Mode (Vulkan only) Режим виведення кадрів (лише Vulkan) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. Керує тим, як емулятор виконує виведення кадрів, щоб зменшити затримки й забезпечити плавнішу й стабільнішу частоту кадрів. - + VRAM Usage Mode: Режим використання відеопам’яті: - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. Це налаштування вибирає, чи повинен емулятор надавати перевагу заощадженню пам’яті, чи по максимуму використовувати доступну відеопам’ять задля продуктивності. Режим «Агресивно» може вплинути на продуктивність інших застосунків, як-от засоби запису. - + Skip CPU Inner Invalidation Пропускати внутрішнє анулювання ЦП - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. Пропускає деякі анулювання кешу під час оновлень пам’яті, зменшуючи використання ЦП й виправляючи затримки. Це може спричинити збої. - + + Anti-Flicker + Антимерехтіння + + + + Forces GPU fence callbacks to wait for submitted GPU work. +Use with Fast GPU mode, to avoid flicker with lower performance impact. + Змушує механізм синхронізації чекати, доки ГП завершить подані завдання. +Використовуйте з режимом ГП «Швидко», щоб уникнути мерехтіння з меншими втратами продуктивності. + + + VSync Mode: Режим вертикальної синхронізації: - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -887,12 +899,12 @@ Mailbox може мати меншу затримку, ніж FIFO, і не ма Immediate (без синхронізації) показує всі кадри й може створювати розриви. - + Sync Memory Operations Синхронізувати операції з пам’яттю - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. @@ -901,44 +913,44 @@ Unreal Engine 4 games often see the most significant changes thereof. Ігри на Unreal Engine 4 часто зазнають найзначніших змін. - + Enable asynchronous presentation (Vulkan only) Увімкнути асинхронне подання (лише Vulkan) - + Slightly improves performance by moving presentation to a separate CPU thread. Трохи покращує продуктивність завдяки переміщенню подання на окремий потік ЦП. - + Force maximum clocks (Vulkan only) Примусово максимальна тактова частота (лише Vulkan) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. Виконує роботу у фоновому режимі в очікуванні графічних команд, не даючи змоги ГП знижувати тактову частоту. - + Anisotropic Filtering: Анізотропна фільтрація: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. Керує якістю візуалізації текстур під непрямими кутами. Для більшості ГП можна вільно вибирати 16x. - + GPU Mode: Режим ГП: - + Controls the GPU emulation mode. Most games render fine with Fast or Balanced modes, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. @@ -947,56 +959,56 @@ Particles tend to only render correctly with Accurate mode. Частинки зазвичай правильно візуалізуються лише з режимом «Точно». - + DMA Accuracy: Точність DMA: - + Controls the DMA precision accuracy. Safe precision fixes issues in some games but may degrade performance. Керує точністю DMA. Вища точність виправляє проблеми з деякими іграми, але може погіршити продуктивність. - + Enable asynchronous shader compilation Увімкнути асинхронну компіляцію шейдерів - + May reduce shader stutter. Може зменшити шейдерні затримки. - + Fast GPU Time Швидкий час роботи ГП - + Overclocks the emulated GPU to increase dynamic resolution and render distance. Use 256 for maximal performance and 512 for maximal graphics fidelity. Розганяє емульований ГП для збільшення динамічної роздільності та відстані візуалізації. Використовуйте 256 для максимальної продуктивності та 512 для максимальної точності графіки. - + GPU Unswizzle Розпакування за допомогою ГП - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. Прискорює декодування 3D-текстур BCn застосовуючи обчислення за допомогою ГП. Вимкніть у разі збоїв або проблем із графікою. - + GPU Unswizzle Max Texture Size Максимальний розмір текстур для розпакування за допомогою ГП - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. @@ -1005,48 +1017,48 @@ Adjust this to find the balance between GPU acceleration and CPU overhead. - + GPU Unswizzle Stream Size Розмір потоку розпакування за допомогою ГП - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. Встановлює максимальний обсяг даних текстур (у МіБ) для обробки на кадр. Вищі значення здатні зменшити затримки під час завантаження текстур, але можуть вплинути на стабільність кадрів. - + GPU Unswizzle Chunk Size Розмір блоків розпакування за допомогою ГП - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. Визначає кількість зрізів глибини, оброблених за одне відправлення. Збільшення здатне покращити пропускну здатність на потужних ГП, але може призвести до TDR або затримок драйвера зі слабшим устаткуванням. - + Use Vulkan pipeline cache Використовувати кеш конвеєра Vulkan - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. Вмикає особливий для різних виробників ГП кеш конвеєра. Це налаштування може значно зменшити час завантаження шейдерів у випадках, коли драйвер Vulkan не зберігає власний кеш конвеєра. - + Enable Compute Pipelines (Intel Vulkan Only) Увімкнути обчислювальні конвеєри (лише Intel Vulkan) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. @@ -1055,184 +1067,194 @@ Compute pipelines are always enabled on all other drivers. Обчислювальні конвеєри завжди увімкнені у всіх інших драйверах. - + Enable Reactive Flushing Увімкнути реактивне очищення - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. Використовує реактивне очищення замість прогнозованого, забезпечуючи точнішу синхронізацію пам’яті. - + Sync to framerate of video playback Синхронізувати частоту кадрів з відтворенням відео - + Run the game at normal speed during video playback, even when the framerate is unlocked. Відтворювати гру з нормальною швидкістю під час відтворення відео навіть при розблокованій частоті кадрів. - + Barrier feedback loops Бар’єрні цикли відгуку - + Improves rendering of transparency effects in specific games. Покращує візуалізацію ефектів прозорості в деяких іграх. - + Enable buffer history Увімкнути історію буфера - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. Вмикає доступ до попередніх станів буфера. Цей параметр може покращити якість візуалізації та стабільну продуктивність у деяких іграх. - + Fix bloom effects Виправити ефекти світіння - + Removes bloom in Burnout. Прибирає світіння в Burnout. - + Enable Legacy Rescale Pass Увімкнути застаріле масштабування - + May fix rescale issues in some games by relying on behavior from the previous implementation. Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. Може виправити проблеми з масштабуванням в іграх, покладаючись на поведінку з попередньої імплементації. Застаріле масштабування виправляє артефакти з лініями на ГП від AMD та Intel, а також сіре блимання текстур на ГП від Nvidia в Luigis Mansion 3. - + Extended Dynamic State Розширений динамічний стан - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. Керує кількістю функцій, які можна використовувати в «Розширеному динамічному стані». Вищі значення допускають більше функцій і можуть збільшити продуктивність, але можуть спричинити додаткові проблеми з графікою. - + Vertex Input Dynamic State Динамічний стан введення вершин - + Enables vertex input dynamic state feature for better quality and performance. Вмикає можливість динамічного стану введення вершин для кращих якості й продуктивності. - + Sample Shading Шейдинг зразків - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. Дозволяє виконувати фрагмент шейдера для кожного зразка в багатозразковому фрагменті замість одного разу для кожного фрагмента. Покращує якість графікі ціною втрати продуктивності. Вищі значення покращують якість, але погіршують продуктивність. - + RNG Seed Початкове значення RNG - + Controls the seed of the random number generator. Mainly used for speedrunning. Керує початковим значення генератора випадкових чисел. Зазвичай використовується в спідранах. - + Device Name Назва пристрою - + The name of the console. Назва консолі. - + + Homebrew Args + Параметри запуску Homebrew + + + + Command-line arguments passed to homebrew at launch (e.g. -noglsl). + Параметри командного рядка, що передаються Homebrew при запуску (як-от noglsl). + + + Custom RTC Date: Користувацька дата RTC: - + This option allows to change the clock of the console. Can be used to manipulate time in games. Це налаштування дозволяє змінити час годинника консолі. Можна використовувати для маніпуляцій із часом в іграх. - + The number of seconds from the current unix time Кількість секунд від поточного unix-часу. - + Language: Мова: - + This option can be overridden when region setting is auto-select Це налаштування може перевизначитися, якщо налаштування регіону вибирається автоматично - + Region: Регіон: - + The region of the console. Регіон консолі. - + Time Zone: Часовий пояс: - + The time zone of the console. Часовий пояс консолі. - + Sound Output Mode: Режим виведення звуку: - + Console Mode: Режим консолі: - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. @@ -1241,1031 +1263,1041 @@ Setting to Handheld can help improve performance for low end systems. Налаштування «Портативний» може покращити продуктивність на слабких системах. - + Prompt for user profile on boot Запитувати профіль користувача під час запуску - + Useful if multiple people use the same PC. Корисно, якщо одним комп’ютером користуються кілька користувачів. - + Pause when not in focus Призупиняти, якщо не у фокусі - + Pauses emulation when focusing on other windows. Призупиняє емуляцію при фокусування на інших вікнах. - + Confirm before stopping emulation Підтверджувати зупинку емуляції - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. Перевизначає запити на підтвердження зупинки емуляції. Увімкнення обходить такі запити й одразу зупиняє емуляцію. - + Hide mouse on inactivity Приховувати курсор миші при бездіяльності - + Hides the mouse after 2.5s of inactivity. Приховує курсор миші після 2,5 с її бездіяльності. - + Disable controller applet Вимкнути аплет контролера - + Forcibly disables the use of the controller applet in emulated programs. When a program attempts to open the controller applet, it is immediately closed. Примусово вимикає використання в емульованих програмах аплета контролера. Якщо програма спробує відкрити аплет контролера, він одразу закриється. - + Check for updates Перевіряти оновлення - + Whether or not to check for updates upon startup. Чи перевіряти оновлення при запуску. - + Enable Gamemode Увімкнути ігровий режим - + Force X11 as Graphics Backend Примусово використовувати X11 як графічний бекенд - + Custom frontend Користувацький фронтенд - + Real applet Справжній аплет - + Never Ніколи - + On Load При завантаженні - + Always Завжди - + CPU ЦП - + GPU ГП - + CPU Asynchronous Асинхронно ЦП - + Uncompressed (Best quality) Без стиснення (Найкраща якість) - + BC1 (Low quality) ВС1 (Низька якість) - + BC3 (Medium quality) ВС3 (Середня якість) - - + + Auto Автоматично - + 30 FPS 30 к/с - + 60 FPS 60 к/с - + 90 FPS 90 к/с - + 120 FPS 120 к/с - + Conservative Заощадження - + Aggressive Агресивно - + Vulkan Vulkan - + OpenGL GLSL OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) OpenGL GLASM (асемблерні шейдери, лише NVIDIA) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) OpenGL SPIR-V (експериментально, лише AMD/Mesa) - + Null Нічого - + Fast Швидко - + Balanced Збалансовано - - + + Accurate Точно - - + + Default Стандартно - + Unsafe (fast) Небезпечно (швидко) - + Safe (stable) Безпечно (стабільно) - + Unsafe Небезпечно - + Paranoid (disables most optimizations) Параноїк (вимикає більшість оптимізацій) - + Debugging Зневадження - + Dynarmic Динамічно - + NCE NCE - + Borderless Windowed Безрамкове вікно - + Exclusive Fullscreen Ексклюзивний повноекранний - + No Video Output Виведення відео відсутнє - + CPU Video Decoding Декодування відео на ЦП - + GPU Video Decoding (Default) Декодування відео на ГП (стандатно) - + 0.25X (180p/270p) [EXPERIMENTAL] 0.25X (180p/270p) [ЕКСПЕРИМЕНТАЛЬНО] - + 0.5X (360p/540p) [EXPERIMENTAL] 0.5X (360p/540p) [ЕКСПЕРИМЕНТАЛЬНО] - + 0.75X (540p/810p) [EXPERIMENTAL] 0.75X (540p/810p) [ЕКСПЕРИМЕНТАЛЬНО] - + 1X (720p/1080p) 1X (720p/1080p) - + 1.25X (900p/1350p) [EXPERIMENTAL] 1.25X (900p/1350p) [ЕКСПЕРИМЕНТАЛЬНО] - + 1.5X (1080p/1620p) [EXPERIMENTAL] 1.5X (1080p/1620p) [ЕКСПЕРИМЕНТАЛЬНО] - + 2X (1440p/2160p) 2X (1440p/2160p) - + 3X (2160p/3240p) 3X (2160p/3240p) - + 4X (2880p/4320p) 4X (2880p/4320p) - + 5X (3600p/5400p) 5X (3600p/5400p) - + 6X (4320p/6480p) 6X (4320p/6480p) - + 7X (5040p/7560p) 7X (5040p/7560p) - + 8X (5760p/8640p) 8X (5760p/8640p) - + Nearest Neighbor Найближчий сусід - + Bilinear Білінійний - + Bicubic Бікубічний - + Gaussian Ґаусса - + Lanczos Ланцоша - + ScaleForce ScaleForce - + AMD FidelityFX Super Resolution AMD FidelityFX Super Resolution - + Area Області - + MMPX MMPX - + Zero-Tangent Нульовий тангенс - + B-Spline B-Spline - + Mitchell Мітчелла - + Spline-1 Spline-1 - - + + Snapdragon Game Super Resolution + Snapdragon Game Super Resolution + + + + Snapdragon Game Super Resolution EdgeDir + Snapdragon Game Super Resolution EdgeDir + + + + None Немає - + FXAA FXAA - + SMAA SMAA - + Default (16:9) Стандартно (16:9) - + Force 4:3 Примусово 4:3 - + Force 21:9 Примусово 21:9 - + Force 16:10 Примусово 16:10 - + Stretch to Window Розтягнути до вікна - + Automatic Автоматично - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x - + 32x 32x - + 64x 64x - + Japanese (日本語) Японська (日本語) - + American English Американська англійська - + French (français) Французька (français) - + German (Deutsch) Німецька (Deutsch) - + Italian (italiano) Італійська (italiano) - + Spanish (español) Іспанська (español) - + Chinese Китайська - + Korean (한국어) Корейська (한국어) - + Dutch (Nederlands) Нідерландська (Nederlands) - + Portuguese (português) Португальська (português) - + Russian (Русский) Російська (Русский) - + Taiwanese Тайванська - + British English Британська англійська - + Canadian French Канадська французька - + Latin American Spanish Латиноамериканська іспанська - + Simplified Chinese Спрощена китайська - + Traditional Chinese (正體中文) Традиційна китайська (正體中文) - + Brazilian Portuguese (português do Brasil) Бразильська португальська (português do Brasil) - + Polish (polska) Польська (polska) - + Thai (แบบไทย) Тайська (แบบไทย) - - + + Japan Японія - + USA США - + Europe Європа - + Australia Австралія - + China Китай - + Korea Корея - + Taiwan Тайвань - + Auto (%1) Auto select time zone Автоматично (%1) - + Default (%1) Default time zone Стандартно (%1) - + CET CET - + CST6CDT CST6CDT - + Cuba Куба - + EET EET - + Egypt Єгипет - + Eire Ейре - + EST EST - + EST5EDT EST5EDT - + GB GB - + GB-Eire GB-Eire - + GMT GMT - + GMT+0 GMT+0 - + GMT-0 GMT-0 - + GMT0 GMT0 - + Greenwich Гринвіч - + Hongkong Гонконг - + HST HST - + Iceland Ісландія - + Iran Іран - + Israel Ізраїль - + Jamaica Ямайка - + Kwajalein Кваджалейн - + Libya Лівія - + MET MET - + MST MST - + MST7MDT MST7MDT - + Navajo Навахо - + NZ NZ - + NZ-CHAT NZ-CHAT - + Poland Польща - + Portugal Португалія - + PRC PRC - + PST8PDT PST8PDT - + ROC ROC - + ROK ROK - + Singapore Сінгапур - + Turkey Туреччина - + UCT UCT - + Universal Універсальний - + UTC UTC - + W-SU W-SU - + WET WET - + Zulu Зулу - + Mono Моно - + Stereo Стерео - + Surround Об’ємний - + 4GB DRAM (Default) 4GB DRAM (стандартно) - + 6GB DRAM (Unsafe) 6GB DRAM (небезпечно) - + 8GB DRAM 8GB DRAM - + 10GB DRAM (Unsafe) 10GB DRAM (небезпечно) - + 12GB DRAM (Unsafe) 12GB DRAM (небезпечно) - + Docked У докстанції - + Handheld Портативний - - + + Off Вимкнено - + Boost (1700MHz) Підвищення (1700 МГц) - + Fast (2000MHz) Швидко (2000 МГц) - + Always ask (Default) Завжди запитувати (стандартно) - + Only if game specifies not to stop Лише якщо гра вказує не зупиняти - + Never ask Ніколи не запитувати - - + + Medium (256) Середньо (256) - - + + High (512) Високо (512) - + Very Small (16 MB) Дуже малий (16 МБ) - + Small (32 MB) Малий (32 МБ) - + Normal (128 MB) Нормальний (128 МБ) - + Large (256 MB) Великий (256 МБ) - + Very Large (512 MB) Дуже великий (512 МБ) - + Very Low (4 MB) Дуже низький (4 МБ) - + Low (8 MB) Низький (8 МБ) - + Normal (16 MB) Нормальний (16 МБ) - + Medium (32 MB) Середній (32 МБ) - + High (64 MB) Високий (64 МБ) - + Very Low (32) Дуже низький (32) - + Low (64) Низький (64) - + Normal (128) Нормальний (128) - + Disabled Вимкнено - + ExtendedDynamicState 1 Розширений динамічний стан 1 - + ExtendedDynamicState 2 Розширений динамічний стан 2 - + ExtendedDynamicState 3 Розширений динамічний стан 3 - + Tree View Дерево вибору - + Grid View Таблиця @@ -3332,33 +3364,33 @@ Would you like to delete the old save data? Колір тла: - + % - FSR sharpening percentage (e.g. 50%) + FSR/SGSR sharpening percentage (e.g. 50%) % - + Off Вимкнено - + VSync Off Вертикальну синхронізацію вимкнено - + Recommended Рекомендовано - + On Увімкнено - + VSync On Вертикальну синхронізацію увімкнено @@ -4487,7 +4519,7 @@ To invert the axes, first move your joystick vertically, and then horizontally.< - + Configure Налаштувати @@ -4518,7 +4550,7 @@ To invert the axes, first move your joystick vertically, and then horizontally.< - + Test Тест @@ -4533,77 +4565,77 @@ To invert the axes, first move your joystick vertically, and then horizontally.< Вилучити сервер - + %1:%2 %1:%2 - - - - - - + + + + + + Eden Eden - + Port number has invalid characters Номер порту містить неправильні символи - + Port has to be in range 0 and 65353 Порт повинен бути в дівпазоні 0–65353 - + IP address is not valid Неправильна IP-адреса - + This UDP server already exists Цей UDP-сервер уже існує - + Unable to add more than 8 servers Неможливо додати більше 8 серверів - + Testing Тестування - + Configuring Налаштування - + Test Successful Тест успішний - + Successfully received data from the server. Успішно отримано інформацію із сервера - + Test Failed Тест провалено - + Could not receive valid data from the server.<br>Please verify that the server is set up correctly and the address and port are correct. Не вдалося отримати правильні дані з сервера.<br>Переконайтеся, що сервер правильно налаштований і вказані правильні адреса й порт. - + UDP Test or calibration configuration is in progress.<br>Please wait for them to finish. Відбувається налаштування калібрування або тестування UDP.<br>Дочекайтеся завершення. @@ -4788,57 +4820,57 @@ Current values are %1% and %2% respectively. Деякі налаштування доступні лише коли гра не запущена. - + Add-Ons Додатки - + System Система - + CPU ЦП - + Graphics Графіка - + Adv. Graphics Графіка (дод.) - + Ext. Graphics Графіка (дод.) - + Audio Звук - + Input Profiles Профілі введення - + Network Мережа - + Applets Аплети - + Properties Властивості @@ -5854,7 +5886,7 @@ Drag points to change position, or double-click table cells to edit values.Імпортувати дані до цієї теки. Це може тривати певний час і видалить УСІ НАЯВНІ ДАНІ! - + Calculating... Обчислення... @@ -6058,50 +6090,50 @@ Please go to Configure -> System -> Network and make a selection. GRenderWindow - - + + OpenGL not available! OpenGL недоступний! - + OpenGL shared contexts are not supported. Спільні контексти OpenGL не підтримуються. - + Eden has not been compiled with OpenGL support. Eden не скомпільовано з підтримкою OpenGL. - - - + + + Error while initializing OpenGL! Помилка під час ініціалізації OpenGL! - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. Ваш ГП може не підтримувати OpenGL або у вас встановлено застарілий графічний драйвер. - + Error while initializing OpenGL 4.6! Помилка під час ініціалізації OpenGL 4.6! - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 Ваш ГП може не підтримувати OpenGL 4.6 або у вас встановлено застарілий графічний драйвер.<br><br>Візуалізатор GL:<br>%1 - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 Ваш ГП може не підтримувати одне або кілька розширень, необхідних для OpenGL. Переконайтеся, що у вас встановлено останній графічний драйвер.<br><br>Візуалізатор GL:<br>%1<br><br>Непідтримувані розширення:<br>%2 - + This build doesn't have OpenGL support. Ця збірка не підтримує OpenGL. @@ -6109,279 +6141,282 @@ Please go to Configure -> System -> Network and make a selection. GameList - + &Add New Game Directory [&A] Додати нову теку з іграми - + Favorite Улюблені - + Start Game Запустити гру - + Start Game without Custom Configuration Запустити гру без користувацького налаштування - + Open Save Data Location Відкрити теку з даними збережень - + Open Mod Data Location Відкрити теку модів - + Open Transferable Pipeline Cache Відкрити переміщуваний кеш конвеєра - + Link to Ryujinx Під’єднати до Ryujinx - + Remove Вилучити - + Remove Installed Update Вилучити встановлене оновлення - + Remove All Installed DLC Вилучити всі доповнення - + Remove Custom Configuration Вилучити користувацьке налаштування - + Remove Cache Storage Вилучити сховище кешу - + Remove OpenGL Pipeline Cache Вилучити кеш конвеєра OpenGL - + Remove Vulkan Pipeline Cache Вилучити кеш конвеєра Vulkan - + Remove All Pipeline Caches Вилучити всі кеші конвеєра - + Remove All Installed Contents Вилучити весь встановлений вміст - + Manage Play Time Керувати награним часом - + Edit Play Time Data Редагувати награний час - + Remove Play Time Data Вилучити дані награного часу - - + + Dump RomFS Створити дамп RomFS - + Dump RomFS to SDMC Створити дамп RomFS у SDMC - + Verify Integrity Перевірити цілісність - + Copy Title ID to Clipboard Скопіювати ID проєкту до буфера обміну - + Navigate to GameDB entry Перейти до запису GameDB - + Create Shortcut Створити ярлик - + Add to Desktop Додати до стільниці - + Add to Applications Menu Додати до меню застосунків - + Configure Game Налаштувати гру - + Scan Subfolders Сканувати підтеки - + Remove Game Directory Вилучити теку гри - + ▲ Move Up ▲ Перемістити вверх - + ▼ Move Down ▼ Перемістити вниз - + Open Directory Location Відкрити розташування теки - + Clear Очистити - - - Name - Назва - - - - Compatibility - Сумісність - - - - Add-ons - Додатки - - - - File type - Тип файлу - - - - Size - Розмір - - - - Play time - Награний час - GameListItemCompat - + Ingame Запускається - + Game starts, but crashes or major glitches prevent it from being completed. Гра запускається, але збої або серйозні баги перешкоджають її успішному проходженню. - + Perfect Ідеально - + Game can be played without issues. У гру можна грати без проблем. - + Playable Придатна до гри - + Game functions with minor graphical or audio glitches and is playable from start to finish. Гра має незначні графічні або звукові проблеми, але її можна пройти від початку до кінця. - + Intro/Menu Вступ/меню - + Game loads, but is unable to progress past the Start Screen. Гра завантажується, але не може просунутися далі стартового екрана. - + Won't Boot Не запускається - + The game crashes when attempting to startup. Під час спроби запуску гри відбувається збій. - + Not Tested Не протестовано - + The game has not yet been tested. Гру ще не протестовано. + + GameListModel + + + Name + Назва + + + + Compatibility + Сумісність + + + + Add-ons + Додатки + + + + File type + Тип файлу + + + + Size + Розмір + + + + Play time + Награний час + + GameListPlaceholder - + Double-click to add a new folder to the game list Натисніть двічі, щоб додати нову теку до переліку ігор @@ -6389,17 +6424,17 @@ Please go to Configure -> System -> Network and make a selection. GameListSearchField - + %1 of %n result(s) %1 із %n результату%1 із %n результатів%1 із %n результатів%1 із %n результатів - + Filter: Фільтр: - + Enter pattern to filter Введіть шаблон для фільтрування @@ -6895,772 +6930,777 @@ Debug Message: [&G] Режим переліку ігор - + Game &Icon Size [&I] Розмір значків ігор - + Reset Window Size to &720p [&7] Скинути розмір вікна до 720p - + Reset Window Size to 720p Скинути розмір вікна до 720p - + Reset Window Size to &900p [&9] Скинути розмір вікна до 900p - + Reset Window Size to 900p Скинути розмір вікна до 900p - + Reset Window Size to &1080p [&1] Скинути розмір вікна до 1080p - + Reset Window Size to 1080p Скинути розмір вікна до 1080p - + &Multiplayer [&M] Багатоосібна гра - + &Tools [&T] Інструменти - + Am&iibo [&I] Amiibo - + Launch &Applet [&A] Запустити аплет - + &TAS [&T] TAS - + &Create Home Menu Shortcut [&C] Створити ярлик меню-домівки - + Install &Firmware [&F] Встановити прошивку - + &Help [&H] Допомога - + &Install Files to NAND... [&I] Встановити файли до NAND... - + L&oad File... [&O] Завантажити файл... - + Load &Folder... [&F] Завантажити теку... - + E&xit [&X] Вийти - - + + &Pause [&P] Призупинити - + &Stop [&S] Зупинити - + &Verify Installed Contents [&V] Перевірити встановлений вміст - + &About Eden [&A] Про Eden - + Single &Window Mode [&W] Одновіконний режим - + Con&figure... [&F] Налаштувати... - + Ctrl+, Ctrl+, - + Enable Overlay Display Applet Увімкнути аплет показу оверлея - + Show &Filter Bar [&F] Показати панель фільтрування - + Show &Status Bar [&S] Показати панель стану - + Show Status Bar Показати панель стану - + &Browse Public Game Lobby [&B] Переглянути публічні ігрові лобі - + &Create Room [&C] Створити кімнату - + &Leave Room [&L] Покинути кімнату - + &Direct Connect to Room [&D] Пряме з’єднання з кімнатою - + &Show Current Room [&S] Показати поточну кімнату - + F&ullscreen [&U] Повноекранний - + &Restart [&R] Перезапустити - + Load/Remove &Amiibo... [&A] Завантажити/вилучити amiibo... - + &Report Compatibility [&R] Повідомити про сумісність - + Open &Mods Page [&M] Відкрити сторінку модів - + Open &Quickstart Guide [&Q] Відкрити посібник користувача - + &FAQ [&F] ЧаПи - + &Capture Screenshot [&C] Зробити знімок екрана - + &Album [&A] Альбом - + &Set Nickname and Owner [&S] Указати псевдонім і власника - + &Delete Game Data [&D] Видалити дані гри - + &Restore Amiibo [&R] Відновити amiibo - + &Format Amiibo [&F] Форматувати amiibo - + &Mii Editor [&M] Редактор Mii - + &Configure TAS... [&C] Налаштувати TAS... - + Configure C&urrent Game... [&U] Налаштувати поточну гру... - - + + &Start [&S] Запустити - + &Reset [&S] Скинути - - + + R&ecord [&E] Запис - + Open &Controller Menu [&C] Відкрити меню контролерів - + Install Decryption &Keys [&K] Встановити ключі дешифрування - + &Home Menu [&H] Меню-домівка - + &Desktop [&D] Стільниця - + &Application Menu [&A] Меню застосунків - + &Root Data Folder [&R] Коренева тека даних - + &NAND Folder [&N] Тека NAND - + &SDMC Folder [&S] Тека SDMC - + &Mod Folder [&M] Тека модів - + &Log Folder [&L] Тека журналу - + From Folder З теки - + From ZIP Із ZIP - + &Eden Dependencies [&E] Залежності Eden - + &Data Manager [&D] Керування даними - + &Tree View [&T] Дерево вибору - + &Grid View [&G] Таблиця - + Game Icon Size Розмір значків ігор - - + + None Жодного - + Show Game &Name [&N] Показати назву гри - + Show &Performance Overlay [&P] Показати оверлей продуктивності - + + &Carousel View + + + + Small (32x32) Маленький (32х32) - + Standard (64x64) Стандартний (64х64) - + Large (128x128) Великий (128х128) - + Full Size (256x256) Повнорозмірний (256х256) - + Broken Vulkan Installation Detected Виявлено пошкоджене встановлення Vulkan - + Vulkan initialization failed during boot. Не вдалося ініціалізувати Vulkan під час запуску. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping Запущено гру - + Loading Web Applet... Завантаження вебаплета... - - + + Disable Web Applet Вимкнути вебаплет - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) Вимкнення вебапплета може призвести до несподіваної поведінки, і це слід робити лише для Super Mario 3D All-Stars. Ви впевнені, що хочете вимкнути вебапплет? (Його можна знову увімкнути в налаштуваннях зневадження.) - + The amount of shaders currently being built Кількість наразі створених шейдерів - + The current selected resolution scaling multiplier. Наразі вибраний множник масштабування роздільності. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. Поточна швидкість емуляції. Значення вище або нижче 100% вказують на те, що емуляція йде швидше або повільніше, ніж на Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Частота кадрів, яку наразі показує гра. Значення змінюватиметься залежно від гри та з кожною сценою. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Час, потрібний для емуляції 1 кадру Switch, не враховуючи обмеження частоти кадрів або вертикальну синхронізацію. Для повношвидкісної емуляції значення повинно бути не вище 16,67 мс. - + Unmute Увімкнути звук - + Mute Вимкнути звук - + Reset Volume Скинути гучність - + &Clear Recent Files [&C] Очистити нещодавні файли - + &Continue [&C] Продовжити - + Warning: Outdated Game Format Увага: Застарілий формат гри - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. Для цієї гри ви використовуєте формат теки з деконструйованим ROM, який є застарілим форматом, заміненим на інші, як-от NCA, NAX, XCI, або NSP. У тек із деконструйованими ROM немає значків, метаданих, а також вони не підтримують оновлення.<br>Для подробиць стосовно різноманітних форматів Switch, які підтримує Eden, ознайомтеся з нашим посібником користувача. Це повідомлення не буде показано знову. - - + + Error while loading ROM! Помилка під час завантаження ROM! - + The ROM format is not supported. Непідтримуваний формат ROM. - + An error occurred initializing the video core. Сталася помилка під час ініціалізації відеоядра. - + Eden has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-mirror.github.io/help/reference/log-files/'>How to Upload the Log File</a>. В Eden сталася помилка під час роботи відеоядра. Зазвичай це відбувається через застарілі драйвери ГП, зокрема інтегрованих. Для подробиць перегляньте журнал. Для додаткової інформації стосовно доступу до журналу перегляньте таку сторінку: <a href='https://yuzu-mirror.github.io/help/reference/log-files/'>Як відвантажити файл журналу</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. Помилка під час завантаження ROM! %1 - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. %1<br>Створіть новий дамп файлів або зверніться по допомогу в Discord/Stoat. - + An unknown error occurred. Please see the log for more details. Сталася невідома помилка. Ознайомтеся з журналом, щоб дізнатися подробиці. - + (64-bit) (64-бітовий) - + (32-bit) (32-бітовий) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Closing software... Закриття програмного засобу... - + Save Data Дані збережень - + Mod Data Дані модів - + Error Opening %1 Folder Помилка під час відкриття теки «%1» - - + + Folder does not exist! Теки не існує! - + Remove Installed Game Contents? Вилучити встановлений вміст гри? - + Remove Installed Game Update? Вилучити встановлені оновлення гри? - + Remove Installed Game DLC? Вилучити встановлені доповнення гри? - + Remove Entry Вилучити запис - + Delete OpenGL Transferable Shader Cache? Видалити переміщуваний кеш шейдерів OpenGL? - + Delete Vulkan Transferable Shader Cache? Видалити переміщуваний кеш шейдерів Vulkan? - + Delete All Transferable Shader Caches? Видалити весь переміщуваний кеш шейдерів? - + Remove Custom Game Configuration? Вилучити користувацькі налаштування гри? - + Remove Cache Storage? Вилучити сховище кешу? - + Remove File Вилучити файл - + Remove Play Time Data Вилучити дані награного часу - + Reset play time? Скинути награний час? - - + + RomFS Extraction Failed! Не вдалося видобути RomFS! - + There was an error copying the RomFS files or the user cancelled the operation. Під час копіювання файлів RomFS сталася помилка або користувач скасував операцію. - + Full Повний - + Skeleton Скелет - + Select RomFS Dump Mode Виберіть режим створення дампу RomFS - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. Виберіть, як ви хочете виконати дамп RomFS <br>Повний скопіює всі файли до нової теки, тоді як <br>скелет створить лише структуру тек. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root За адресою «%1» недостатньо вільного місця для видобування RomFS. Звільніть місце або виберіть іншу теку для створення дампу в «Емуляція» → «Налаштувати» → «Система» → «Файлова система» → «Коренева тека дампів». - + Extracting RomFS... Видобування RomFS... - - + + Cancel Скасувати - + RomFS Extraction Succeeded! RomFS видобуто успішно! - + The operation completed successfully. Операцію успішно виконано. - + Error Opening %1 Помилка під час відкриття «%1» - + Select Directory Вибрати теку - + Properties Властивості - + The game properties could not be loaded. Неможливо завантажити властивості гри. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Виконуваний файл Switch (%1);;Усі файли (*.*) - + Load File Завантажити файл - + Open Extracted ROM Directory Відкрити теку видобутого ROM - + Invalid Directory Selected Вибрано неправильну теку - + The directory you have selected does not contain a 'main' file. Вибрана тека не містить файлу «main». - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) Встановлюваний файл Switch (*.nca, *.nsp, *.xci);;Архів вмісту Nintendo (*.nca);;Пакет подання Nintendo (*.nsp);;Образ картриджа NX (*.xci) - + Install Files Встановити файли - + %n file(s) remaining Лишився 1 файлЛишилося %n файлиЛишилося %n файлівЛишилося %n файлів - + Installing file "%1"... Встановлення файлу «%1»... - - + + Install Results Результати встановлення - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. Щоб уникнути можливих конфліктів, ми не радимо користувачам встановлювати ігри в NAND. Користуйтеся цією функцією лише для встановлення оновлень і доповнень. - + %n file(s) were newly installed Щойно встановлено %n файл @@ -7670,7 +7710,7 @@ Please, only use this feature to install updates and DLC. - + %n file(s) were overwritten Перезаписано %n файл @@ -7680,7 +7720,7 @@ Please, only use this feature to install updates and DLC. - + %n file(s) failed to install Не вдалося встановити %n файл @@ -7690,361 +7730,314 @@ Please, only use this feature to install updates and DLC. - + System Application Системний застосунок - + System Archive Системний архів - + System Application Update Оновлення системного застосунку - + Firmware Package (Type A) Пакет прошивки (Тип А) - + Firmware Package (Type B) Пакет прошивки (Тип Б) - + Game Гра - + Game Update Оновлення гри - + Game DLC Доповнення гри - + Delta Title Проєкт «Дельта» - + Select NCA Install Type... Виберіть тип встановлення NCA... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) Виберіть тип проєкту, який ви хочете встановити для цього NCA: (У більшості випадків підходить стандартний вибір «Гра».) - + Failed to Install Не вдалося встановити - + The title type you selected for the NCA is invalid. Тип проєкту, який ви вибрали для NCA, неправильний. - + File not found Файл не виявлено - + File "%1" not found Файл «%1» не виявлено - + OK Гаразд - + Function Disabled Функцію вимкнену - + Compatibility list reporting is currently disabled. Check back later! Звітування для переліку сумісності наразі вимкнено. Зазирніть пізніше! - + Error opening URL Помилка під час відкриття URL - + Unable to open the URL "%1". Не вдалося відкрити URL: «%1». - + TAS Recording Записування TAS - + Overwrite file of player 1? Перезаписати файл гравця 1? - + Invalid config detected Виявлено неправильне налаштування - + Handheld controller can't be used on docked mode. Pro controller will be selected. Портативний контролер неможливо використовувати в режимі докстанції. Буде вибрано контролер Pro. - - + + Amiibo Amiibo - - + + The current amiibo has been removed Поточний amiibo вилучено - + Error Помилка - - + + The current game is not looking for amiibos Поточна гра не очікує amiibo - + Amiibo File (%1);; All Files (*.*) Файл amiibo (%1);; Усі файли (*.*) - + Load Amiibo Завантажити amiibo - + Error loading Amiibo data Помилка під час завантаження даних amiibo - + The selected file is not a valid amiibo Вибраний файл не є дійсним amiibo - + The selected file is already on use Вибраний файл уже використовується - + An unknown error occurred Сталася невідома помилка - - - Keys not installed - Ключі не встановлено - - - - - Install decryption keys and restart Eden before attempting to install firmware. - Встановіть ключі дешифрування та перезапустіть Eden, перш ніж спробувати встановити прошивку. - - - - Select Dumped Firmware Source Location - Виберіть розташування дампу прошивки - - - - Select Dumped Firmware ZIP - Виберіть ZIP із дампом прошивки - - - - Zipped Archives (*.zip) - Zip-архіви (*.zip) - - - - Firmware cleanup failed - Не вдалося очистити прошивку - - - - Failed to clean up extracted firmware cache. -Check write permissions in the system temp directory and try again. -OS reported error: %1 - Не вдалося очистити видобутий кеш прошивки. -Перевірте дозволи на запис у системної теки temp і спробуйте знову. -Помилка зі звіту від ОС: %1 - - - + No firmware available Немає доступних прошивок - + Firmware Corrupted Прошивка пошкоджена - + Unknown applet Невідомий аплет - + Applet doesn't map to a known value. Аплет не призначено до відомого значення. - + Record not found Запис не виявлено - + Applet not found. Please reinstall firmware. Аплет не виявлено. Перевстановіть прошивку. - + Capture Screenshot Зробити знімок екрана - + PNG Image (*.png) Зображення PNG (*.png) - + TAS state: Running %1/%2 Стан TAS: Працює %1/%2 - + TAS state: Recording %1 Стан TAS: Триває запис %1 - + TAS state: Idle %1/%2 Стан TAS: Бездіяльність %1/%2 - + TAS State: Invalid Стан TAS: Неправильний - + &Stop Running [&S] Зупинити - + Stop R&ecording [&E] Зупинити запис - + Building: %n shader(s) Компіляція: %n шейдерКомпіляція: %n шейдериКомпіляція: %n шейдерівКомпіляція: %n шейдерів - + Scale: %1x %1 is the resolution scaling factor Масштаб: %1x - + Speed: %1% / %2% Швидкість: %1% / %2% - + Speed: %1% Швидкість: %1% - + Game: %1 FPS Гра: %1 к/с - + Frame: %1 ms Кадр: %1 мс - - - FSR - FSR - - - + NO AA БЕЗ ЗГЛАДЖУВАННЯ - + VOLUME: MUTE ГУЧНІСТЬ: ВИМКНЕНО - + VOLUME: %1% Volume percentage (e.g. 50%) ГУЧНІСТЬ: %1% - + Derivation Components Missing Відсутні компоненти виведення - + Decryption keys are missing. Install them now? Відсутні ключі шифрування. Встановити їх зараз? - + Wayland Detected! Виявлено Wayland! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -8055,74 +8048,74 @@ Would you like to force it for future launches? Хочете примусово увімкнути його для наступних запусків? - + Use X11 Використовувати X11 - + Continue with Wayland Продовжити з Wayland - + Don't show again Не показувати знову - + Restart Required Потрібен перезапуск - + Restart Eden to apply the X11 backend. Перезапуск Eden для застосування бекенду X11. - + Slow Сповільнення - + Turbo Прискорення - + Unlocked Розблоковано - + Select RomFS Dump Target Виберіть розташування для створення дампу RomFS - + Please select which RomFS you would like to dump. Виберіть, який дамп RomFS ви хочете створити. - + Are you sure you want to close Eden? Ви впевнені, що хочете закрити Eden? - - - + + + Eden Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. Ви впевнені, що хочете зупинити емуляцію? Увесь незбережений поступ буде втрачено. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? @@ -8190,6 +8183,11 @@ Would you like to bypass this and exit anyway? ScaleForce ScaleForce + + + FSR + FSR + Area @@ -8201,52 +8199,62 @@ Would you like to bypass this and exit anyway? MMPX - + + SGSR + SGSR + + + + SGSR EdgeDir + SGSR EdgeDir + + + Docked У докстанції - + Handheld Портативний - + Fast Швидко - + Balanced Збалансовано - + Accurate Точно - + Vulkan Vulkan - + OpenGL GLSL OpenGL GLSL - + OpenGL SPIRV OpenGL SPIRV - + OpenGL GLASM OpenGL GLASM - + Null Нічого @@ -8289,7 +8297,7 @@ If you wish to clean up the files which were left in the old data location, you %1 - + Data was migrated successfully. Дані перенесено успішно. @@ -9147,47 +9155,47 @@ p, li { white-space: pre-wrap; } %1 грає в %2 - + Play Time: %1 Награний час: %1 - + Never Played Ще не зіграно - + Version: %1 Версія: %1 - + Version: 1.0.0 Версія: 1.0.0 - + Installed SD Titles Проєкти, встановлені до SD - + Installed NAND Titles Проєкти, встановлені до NAND - + System Titles Системні проєкти - + Add New Game Directory Додати нову теку з іграми - + Favorites Улюблені @@ -9308,47 +9316,47 @@ p, li { white-space: pre-wrap; } QtCommon::Content - + Game Requires Firmware Гра потребує прошивку - + The game you are trying to launch requires firmware to boot or to get past the opening menu. Please <a href='https://yuzu-mirror.github.io/help/quickstart'>dump and install firmware</a>, or press "OK" to launch anyways. Гра, яку ви намагаєтеся запустити, потребує прошивку, щоб запуститися або пройти меню запуску. <a href='https://yuzu-mirror.github.io/help/quickstart'>Створіть дамп і встановіть прошивку</a> або натисніть «Гаразд», щоб однаково запустити. - + Installing Firmware... Встановлення прошивки... - - - - - + + + + + Cancel Скасувати - + Firmware Install Failed Не вдалося встановити прошивку - + Firmware Install Succeeded Прошивку успішно встановлено - + Firmware integrity verification failed! Не вдалося перевірити цілісність прошивки! - - + + Verification failed for the following files: %1 @@ -9357,207 +9365,242 @@ p, li { white-space: pre-wrap; } %1 - - + + Verifying integrity... Перевірка цілісності... - - + + Integrity verification succeeded! Перевірка цілісності успішна! - - + + The operation completed successfully. Операцію успішно завершено. - - + + Integrity verification failed! Не вдалося перевірити цілісність! - + File contents may be corrupt or missing. Файли вмісту можуть бути пошкоджені або відсутні. - + Integrity verification couldn't be performed Неможливо виконати перевірку цілісності - + Firmware installation cancelled, firmware may be in a bad state or corrupted. File contents could not be checked for validity. Встановлення прошивки скасовано. Можливо, прошивка в поганому стані або пошкоджена. Неможливо перевірити на дійсність файли вмісту. - + Select Dumped Keys Location Виберіть розатшування дампу ключів - + Decryption Keys install succeeded Ключі дешифрування успішно встановлено - + Decryption Keys install failed Не вдалося встановити ключі дешифрування - + Orphaned Profiles Detected! Виявлено покинуті профілі! - + UNEXPECTED BAD THINGS MAY HAPPEN IF YOU DON'T READ THIS!<br>Eden has detected the following save directories with no attached profile:<br>%1<br><br>The following profiles are valid:<br>%2<br><br>Click "OK" to open your save folder and fix up your profiles.<br>Hint: copy the contents of the largest or last-modified folder elsewhere, delete all orphaned profiles, and move your copied contents to the good profile.<br><br>Still confused? See the <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/Orphaned.md'>help page</a>.<br> ЯКЩО ВИ ЦЕ НЕ ПРОЧИТАЄТЕ, МОЖУТЬ СТАТИСЯ НЕОЧІКУВАНІ ПОГАНІ РЕЧІ!<br>Eden виявив такі теки збережень без прикріпленого профілю:<br>%1<br><br>Є такі дійсні профілі:<br>%2<br><br>Натисніть «ОК», щоб відкрити теку збережень і полагодити свої профілі.<br>Порада: скопіюйте у будь-яке інше місце вміст найбільшої теки, у якій нещодавно були зміни, видаліть профілі, що лишилися та перемістіть скопійований вміст до провильного профілю.<br><br>Досі не розумієте, що робити? Перегляньте <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/Orphaned.md'>сторінку допомоги</a>.<br> - + Really clear data? Дійсно очистити дані? - + Important data may be lost! Може бути втрачено важливі дані! - + Are you REALLY sure? Ви ДІЙСНО впевнені? - + Once deleted, your data will NOT come back! Only do this if you're 100% sure you want to delete this data. Після видалення ваші дані буде НЕМОЖЛИВО повернути! Виконуйте цю дію, лише якщо ви на 100% упевнені, що хочете видалити ці дані. - + Clearing... Очищення... - + Select Export Location Виберіть розташування для експортування - + %1.zip %1.zip - - + + + Zipped Archives (*.zip) Zip-архіви (*.zip) - + Exporting data. This may take a while... Експортування даних. Це може тривати певний час... - + Exporting Експортування - + Exported Successfully Успішно експортовано - + Data was exported successfully. Дані успішно експортовано. - + Export Cancelled Експортування скасовано - + Export was cancelled by the user. Експортування скасовано користувачем. - + Export Failed Не вдалося експортувати - + Ensure you have write permissions on the targeted directory and try again. Запевніться, що у вас є дозволи на записування до вказаної теки й спробуйте знову. - + Select Import Location Виберіть розташування для імпортування - + Import Warning Попередження щодо імпортування - + All previous data in this directory will be deleted. Are you sure you wish to proceed? Усі попередні в цій теці будуть видалені. Ви впевнені, що хочете продовжити? - + Importing data. This may take a while... Імпортування даних. Це може тривати певний час... - + Importing Імпортування - + Imported Successfully Успішно імпортовано - + Data was imported successfully. Дані успішно імпортовано. - + Import Cancelled Імпортування скасовано - + Import was cancelled by the user. Імпортування скасовано користувачем. - + Import Failed Не вдалося імпортувати - + Ensure you have read permissions on the targeted directory and try again. Запевніться, що у вас є дозволи на читання зі вказаної теки й спробуйте знову. + + + Keys not installed + Ключі не встановлено + + + + Install decryption keys and restart Eden before attempting to install firmware. + Встановіть ключі дешифрування та перезапустіть Eden, перш ніж спробувати встановити прошивку. + + + + Select Dumped Firmware Source Location + Виберіть розташування дампу прошивки + + + + Select Dumped Firmware ZIP + Виберіть ZIP із дампом прошивки + + + + Firmware cleanup failed + Не вдалося очистити прошивку + + + + Failed to clean up extracted firmware cache. +Check write permissions in the system temp directory and try again. +OS reported error: %1 + Не вдалося очистити видобутий кеш прошивки. +Перевірте дозволи на запис у системної теки temp і спробуйте знову. +Помилка зі звіту від ОС: %1 + QtCommon::FS @@ -9806,72 +9849,72 @@ Would you like to manually select a portable folder to use? Неможливо видалити кеш метаданих. Можливо, він використовується або не існує. - + Create Shortcut Створити ярлик - + Do you want to launch the game in fullscreen? Ви хочете запустити гру в повноеранному режимі? - + Shortcut Created Ярлик створено - + Successfully created a shortcut to %1 Успішно створено ярлик для: %1 - + Shortcut may be Volatile! Ярлик може бути нестабільним! - + This will create a shortcut to the current AppImage. This may not work well if you update. Continue? Це створить ярлик для поточного AppImage. Можливо, він не буде належно працювати після оновлення. Продовжити? - + Failed to Create Shortcut Не вдалося створити ярлик - + Failed to create a shortcut to %1 Не вдалося створити ярлик для: %1 - + Create Icon Створити значок - + Cannot create icon file. Path "%1" does not exist and cannot be created. Неможливо створити файл значка. Шлях «%1» не існує або не може бути створений. - + No firmware available Немає доступних прошивок - + Please install firmware to use the home menu. Встановіть прошивку, щоб користуватися меню-домівкою. - + Home Menu Applet Аплет меню-домівки - + Home Menu is not available. Please reinstall firmware. Меню-домівка недоступна. Перевстановіть прошивку. @@ -9879,37 +9922,37 @@ Would you like to manually select a portable folder to use? QtCommon::Mod - + Mod Name Назва мода - + What should this mod be called? Як повинен називатися цей мод? - + RomFS RomFS - + ExeFS/Patch ExeFS/патч - + Cheat Чит - + Mod Type Тип мода - + Could not detect mod type automatically. Please manually specify the type of mod you downloaded. Most mods are RomFS mods, but patches (.pchtxt) are typically ExeFS mods. @@ -9918,18 +9961,18 @@ Most mods are RomFS mods, but patches (.pchtxt) are typically ExeFS mods. - - + + Mod Extract Failed Не вдалося видобути мод - + Failed to create temporary directory %1 Не вдалося створити тимчасову теку %1 - + Zip file %1 is empty Zip-файл %1 порожній @@ -10630,66 +10673,66 @@ By selecting "From Eden", previous save data stored in Ryujinx will be %1 доступно для завантаження. - + New Version Location Розташування нової версії - + All Files (*.*) Усі файли (*.*) - - - + + + Failed to save file Не вдалося зберегти файл - + Could not open file %1 for writing. Не вдалося відкрити файл «%1» для запису. - + Downloading... Завантаження... - + Cancel Скасувати - + Could not write to file %1. Не вдалося записати до файлу «%1». - + Could not commit to file %1. Не вдалося вкласти до файлу «%1». - + Failed to download file Не вдалося завантажити файл - + Could not download from %1%2 Error code: %3 Не вдалося завантажити з %1%2 Код помилки: %3 - + Download Complete Завантажено - + Successfully downloaded %1. Would you like to open it? %1 успішно завантажено. Хочете відкрити? diff --git a/dist/languages/vi.ts b/dist/languages/vi.ts index 5cbda3c24f..7b588ae229 100644 --- a/dist/languages/vi.ts +++ b/dist/languages/vi.ts @@ -704,7 +704,7 @@ Options lower than 1X can cause artifacts. - Determines how sharpened the image will look using FSR's dynamic contrast. + Determines how sharpened the image will look using FSR's or SGSR's dynamic contrast. @@ -755,35 +755,35 @@ Disabling it is only intended for debugging. - + Use asynchronous GPU emulation - + Uses an extra CPU thread for rendering. This option should always remain enabled. - + NVDEC emulation: Giả lập NVDEC: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. - + ASTC Decoding Method: - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -792,55 +792,66 @@ stuttering but may present artifacts. - + ASTC Recompression Method: - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. - + Frame Pacing Mode (Vulkan only) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. - + VRAM Usage Mode: - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. - + Skip CPU Inner Invalidation - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. - + + Anti-Flicker + + + + + Forces GPU fence callbacks to wait for submitted GPU work. +Use with Fast GPU mode, to avoid flicker with lower performance impact. + + + + VSync Mode: Chế độ Vsync: - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -848,1361 +859,1381 @@ Immediate (no synchronization) presents whatever is available and can exhibit te - + Sync Memory Operations - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. - + Enable asynchronous presentation (Vulkan only) Bật hiển thị bất đồng bộ (chỉ cho Vulkan) - + Slightly improves performance by moving presentation to a separate CPU thread. - + Force maximum clocks (Vulkan only) Buộc chạy ở xung nhịp tối đa (chỉ cho Vulkan) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. Chạy các công việc trong nền trong khi đang chờ lệnh đồ họa để giữ cho GPU không giảm xung nhịp. - + Anisotropic Filtering: Lọc bất đẳng hướng: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. - + GPU Mode: - + Controls the GPU emulation mode. Most games render fine with Fast or Balanced modes, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. - + DMA Accuracy: - + Controls the DMA precision accuracy. Safe precision fixes issues in some games but may degrade performance. - + Enable asynchronous shader compilation - + May reduce shader stutter. - + Fast GPU Time - + Overclocks the emulated GPU to increase dynamic resolution and render distance. Use 256 for maximal performance and 512 for maximal graphics fidelity. - + GPU Unswizzle - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. - + GPU Unswizzle Max Texture Size - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. - + GPU Unswizzle Stream Size - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. - + GPU Unswizzle Chunk Size - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. - + Use Vulkan pipeline cache Dùng bộ nhớ đệm pipeline Vulkan - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. - + Enable Compute Pipelines (Intel Vulkan Only) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. - + Enable Reactive Flushing Bật xả tương ứng - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. - + Sync to framerate of video playback Đồng bộ hóa với tốc độ khung hình khi phát video - + Run the game at normal speed during video playback, even when the framerate is unlocked. Chạy game với tốc độ bình thường trong quá trình phát video, ngay cả khi tốc độ khung hình được mở khóa. - + Barrier feedback loops Vòng lặp phản hồi rào cản - + Improves rendering of transparency effects in specific games. Cải thiện hiệu quả kết xuất của hiệu ứng trong suốt trong một số game. - + Enable buffer history - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. - + Fix bloom effects - + Removes bloom in Burnout. - + Enable Legacy Rescale Pass - + May fix rescale issues in some games by relying on behavior from the previous implementation. Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. - + Extended Dynamic State - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. - + Vertex Input Dynamic State - + Enables vertex input dynamic state feature for better quality and performance. - + Sample Shading - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. - + RNG Seed Hạt giống RNG - + Controls the seed of the random number generator. Mainly used for speedrunning. - + Device Name Tên thiết bị - + The name of the console. - + + Homebrew Args + + + + + Command-line arguments passed to homebrew at launch (e.g. -noglsl). + + + + Custom RTC Date: - + This option allows to change the clock of the console. Can be used to manipulate time in games. - + The number of seconds from the current unix time - + Language: - + This option can be overridden when region setting is auto-select - + Region: Vùng: - + The region of the console. - + Time Zone: Múi giờ: - + The time zone of the console. - + Sound Output Mode: Chế độ đầu ra âm thanh: - + Console Mode: - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. - + Prompt for user profile on boot - + Useful if multiple people use the same PC. - + Pause when not in focus - + Pauses emulation when focusing on other windows. - + Confirm before stopping emulation - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. - + Hide mouse on inactivity Ẩn con trỏ chuột khi không dùng - + Hides the mouse after 2.5s of inactivity. - + Disable controller applet Vô hiệu hoá applet tay cầm - + Forcibly disables the use of the controller applet in emulated programs. When a program attempts to open the controller applet, it is immediately closed. - + Check for updates - + Whether or not to check for updates upon startup. - + Enable Gamemode - + Force X11 as Graphics Backend - + Custom frontend - + Real applet - + Never - + On Load - + Always - + CPU CPU - + GPU - + CPU Asynchronous - + Uncompressed (Best quality) Không nén (Chất lượng tốt nhất) - + BC1 (Low quality) BC1 (Chất lượng thấp) - + BC3 (Medium quality) BC3 (Chất lượng trung bình) - - + + Auto Tự động - + 30 FPS - + 60 FPS - + 90 FPS - + 120 FPS - + Conservative - + Aggressive - + Vulkan Vulkan - + OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) - + Null Null - + Fast - + Balanced - - + + Accurate Chính xác - - + + Default Mặc định - + Unsafe (fast) - + Safe (stable) - + Unsafe Không an toàn - + Paranoid (disables most optimizations) Paranoid (vô hiệu hoá hầu hết sự tối ưu) - + Debugging - + Dynarmic - + NCE - + Borderless Windowed Cửa sổ không viền - + Exclusive Fullscreen Toàn màn hình - + No Video Output Không có đầu ra video - + CPU Video Decoding Giải mã video bằng CPU - + GPU Video Decoding (Default) Giải mã video bằng GPU (Mặc định) - + 0.25X (180p/270p) [EXPERIMENTAL] - + 0.5X (360p/540p) [EXPERIMENTAL] - + 0.75X (540p/810p) [EXPERIMENTAL] 0.75X (540p/810p) [THỬ NGHIỆM] - + 1X (720p/1080p) 1X (720p/1080p) - + 1.25X (900p/1350p) [EXPERIMENTAL] - + 1.5X (1080p/1620p) [EXPERIMENTAL] 1.5X (1080p/1620p) [THỬ NGHIỆM] - + 2X (1440p/2160p) 2X (1440p/2160p) - + 3X (2160p/3240p) 3X (2160p/3240p) - + 4X (2880p/4320p) 4X (2880p/4320p) - + 5X (3600p/5400p) 5X (3600p/5400p) - + 6X (4320p/6480p) 6X (4320p/6480p) - + 7X (5040p/7560p) 7X (5040p/7560p) - + 8X (5760p/8640p) 8X (5760p/8640p) - + Nearest Neighbor Nearest Neighbor - + Bilinear Bilinear - + Bicubic Bicubic - + Gaussian Gaussian - + Lanczos - + ScaleForce ScaleForce - + AMD FidelityFX Super Resolution - + Area - + MMPX - + Zero-Tangent - + B-Spline - + Mitchell - + Spline-1 - - + + Snapdragon Game Super Resolution + + + + + Snapdragon Game Super Resolution EdgeDir + + + + + None Không có - + FXAA FXAA - + SMAA SMAA - + Default (16:9) Mặc định (16:9) - + Force 4:3 Dùng 4:3 - + Force 21:9 Dùng 21:9 - + Force 16:10 Dùng 16:10 - + Stretch to Window Mở rộng đến cửa sổ - + Automatic Tự động - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x - + 32x - + 64x - + Japanese (日本語) Tiếng Nhật (日本語) - + American English Tiếng Anh Mỹ - + French (français) Tiếng Pháp (French) - + German (Deutsch) Tiếng Đức (Deutsch) - + Italian (italiano) Tiếng Ý (italiano) - + Spanish (español) Tiếng Tây Ban Nha (Español) - + Chinese Tiếng Trung - + Korean (한국어) Tiếng Hàn (한국어) - + Dutch (Nederlands) Tiếng Hà Lan (Nederlands) - + Portuguese (português) Tiếng Bồ Đào Nha (Portuguese) - + Russian (Русский) Tiếng Nga (Русский) - + Taiwanese Tiếng Đài Loan - + British English Tiếng Anh Anh - + Canadian French Tiếng Pháp Canada - + Latin American Spanish Tiếng Tây Ban Nha Mỹ Latinh - + Simplified Chinese Tiếng Trung giản thể - + Traditional Chinese (正體中文) Tiếng Trung phồn thể (正體中文) - + Brazilian Portuguese (português do Brasil) Tiếng Bồ Đào Nha Brasil (Português do Brasil) - + Polish (polska) - + Thai (แบบไทย) - - + + Japan Nhật Bản - + USA Hoa Kỳ - + Europe Châu Âu - + Australia Úc - + China Trung Quốc - + Korea Hàn Quốc - + Taiwan Đài Loan - + Auto (%1) Auto select time zone Tự động (%1) - + Default (%1) Default time zone Mặc định (%1) - + CET CET - + CST6CDT CST6CDT - + Cuba Cuba - + EET EET - + Egypt Ai Cập - + Eire Eire - + EST EST - + EST5EDT EST5EDT - + GB GB - + GB-Eire GB-Eire - + GMT GMT - + GMT+0 GMT+0 - + GMT-0 GMT-0 - + GMT0 GMT0 - + Greenwich Greenwich - + Hongkong Hồng Kông - + HST HST - + Iceland Iceland - + Iran Iran - + Israel Israel - + Jamaica Jamaica - + Kwajalein Kwajalein - + Libya Libya - + MET MET - + MST MST - + MST7MDT MST7MDT - + Navajo Navajo - + NZ NZ - + NZ-CHAT NZ-CHAT - + Poland Ba Lan - + Portugal Bồ Đào Nha - + PRC PRC - + PST8PDT PST8PDT - + ROC ROC - + ROK ROK - + Singapore Singapore - + Turkey Thổ Nhĩ Kỳ - + UCT UCT - + Universal Quốc tế - + UTC UTC - + W-SU W-SU - + WET WET - + Zulu Zulu - + Mono Mono - + Stereo Stereo - + Surround Surround - + 4GB DRAM (Default) - + 6GB DRAM (Unsafe) - + 8GB DRAM - + 10GB DRAM (Unsafe) - + 12GB DRAM (Unsafe) - + Docked Docked - + Handheld Handheld - - + + Off - + Boost (1700MHz) - + Fast (2000MHz) - + Always ask (Default) - + Only if game specifies not to stop - + Never ask - - + + Medium (256) - - + + High (512) - + Very Small (16 MB) - + Small (32 MB) - + Normal (128 MB) - + Large (256 MB) - + Very Large (512 MB) - + Very Low (4 MB) - + Low (8 MB) - + Normal (16 MB) - + Medium (32 MB) - + High (64 MB) - + Very Low (32) - + Low (64) - + Normal (128) - + Disabled - + ExtendedDynamicState 1 - + ExtendedDynamicState 2 - + ExtendedDynamicState 3 - + Tree View - + Grid View @@ -3257,33 +3288,33 @@ Would you like to delete the old save data? Màu nền: - + % - FSR sharpening percentage (e.g. 50%) - % + FSR/SGSR sharpening percentage (e.g. 50%) + - + Off Tắt - + VSync Off Tắt Vsync - + Recommended Đề xuất - + On Bật - + VSync On Bật Vsync @@ -4412,7 +4443,7 @@ To invert the axes, first move your joystick vertically, and then horizontally.< - + Configure Cấu hình @@ -4443,7 +4474,7 @@ To invert the axes, first move your joystick vertically, and then horizontally.< - + Test Thử nghiệm @@ -4458,77 +4489,77 @@ To invert the axes, first move your joystick vertically, and then horizontally.< Loại bỏ máy chủ - + %1:%2 %1:%2 - - - - - - + + + + + + Eden - + Port number has invalid characters Cổng có kí tự không hợp lệ - + Port has to be in range 0 and 65353 Cổng phải từ 0 đến 65353 - + IP address is not valid Địa chỉ IP không hợp lệ - + This UDP server already exists Máy chủ UDP này đã tồn tại - + Unable to add more than 8 servers Không thể thêm quá 8 máy chủ - + Testing Thử nghiệm - + Configuring Cấu hình - + Test Successful Thử nghiệm thành công - + Successfully received data from the server. Thành công nhận dữ liệu từ máy chủ. - + Test Failed Thử nghiệm thất bại - + Could not receive valid data from the server.<br>Please verify that the server is set up correctly and the address and port are correct. Không thể nhận được dữ liệu hợp lệ từ máy chủ.<br>Hãy chắc chắn máy chủ được thiết lập chính xác và địa chỉ lẫn cổng đều đúng. - + UDP Test or calibration configuration is in progress.<br>Please wait for them to finish. Cấu hình kiểm tra hoặc hiệu chuẩn UDP đang được tiến hành.<br>Vui lòng chờ cho đến khi nó hoàn thành. @@ -4713,57 +4744,57 @@ Các giá trị hiện tại lần lượt là %1% và %2%. Một số cài đặt chỉ khả dụng khi game không chạy. - + Add-Ons Add-Ons - + System Hệ thống - + CPU CPU - + Graphics Đồ hoạ - + Adv. Graphics Đồ hoạ nâng cao - + Ext. Graphics - + Audio Âm thanh - + Input Profiles Hồ sơ đầu vào - + Network - + Applets - + Properties Thuộc tính @@ -5774,7 +5805,7 @@ Kéo điểm để thay đổi vị trí, hoặc nhấp đúp chuột vào ô tr - + Calculating... @@ -5976,50 +6007,50 @@ Please go to Configure -> System -> Network and make a selection. GRenderWindow - - + + OpenGL not available! OpenGL không khả dụng! - + OpenGL shared contexts are not supported. Các ngữ cảnh OpenGL chung không được hỗ trợ. - + Eden has not been compiled with OpenGL support. - - - + + + Error while initializing OpenGL! Lỗi khi khởi tạo OpenGL! - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. GPU của bạn có thể không hỗ trợ OpenGL, hoặc bạn không có driver đồ hoạ mới nhất. - + Error while initializing OpenGL 4.6! Lỗi khi khởi tạo OpenGL 4.6! - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 GPU của bạn có thể không hỗ trợ OpenGL 4.6, hoặc bạn không có driver đồ hoạ mới nhất.<br><br>GL Renderer:<br>%1 - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 GPU của bạn có thể không hỗ trợ một hoặc nhiều tiện ích OpenGL cần thiết. Vui lòng đảm bảo bạn có driver đồ hoạ mới nhất.<br><br>GL Renderer:<br>%1<br><br>Tiện ích không hỗ trợ:<br>%2 - + This build doesn't have OpenGL support. @@ -6027,279 +6058,282 @@ Please go to Configure -> System -> Network and make a selection. GameList - + &Add New Game Directory - + Favorite Ưa thích - + Start Game Bắt đầu game - + Start Game without Custom Configuration Bắt đầu game mà không có cấu hình tuỳ chỉnh - + Open Save Data Location Mở vị trí dữ liệu save - + Open Mod Data Location Mở vị trí chứa dữ liệu mod - + Open Transferable Pipeline Cache Mở thư mục chứa bộ nhớ đệm pipeline - + Link to Ryujinx - + Remove Loại bỏ - + Remove Installed Update Loại bỏ bản cập nhật đã cài - + Remove All Installed DLC Loại bỏ tất cả DLC đã cài đặt - + Remove Custom Configuration Loại bỏ cấu hình tuỳ chỉnh - + Remove Cache Storage Loại bỏ bộ nhớ đệm - + Remove OpenGL Pipeline Cache Loại bỏ bộ nhớ đệm pipeline OpenGL - + Remove Vulkan Pipeline Cache Loại bỏ bộ nhớ đệm pipeline Vulkan - + Remove All Pipeline Caches Loại bỏ tất cả bộ nhớ đệm shader - + Remove All Installed Contents Loại bỏ tất cả nội dung đã cài đặt - + Manage Play Time - + Edit Play Time Data - + Remove Play Time Data - - + + Dump RomFS Trích xuất RomFS - + Dump RomFS to SDMC Trích xuất RomFS tới SDMC - + Verify Integrity Kiểm tra tính toàn vẹn - + Copy Title ID to Clipboard Sao chép ID title vào bộ nhớ tạm - + Navigate to GameDB entry Điều hướng đến mục GameDB - + Create Shortcut Tạo lối tắt - + Add to Desktop Thêm vào desktop - + Add to Applications Menu Thêm vào menu ứng dụng - + Configure Game - + Scan Subfolders Quét các thư mục con - + Remove Game Directory Loại bỏ thư mục game - + ▲ Move Up ▲ Di chuyển lên - + ▼ Move Down ▼ Di chuyển xuống - + Open Directory Location Mở vị trí thư mục - + Clear Xóa - - - Name - Tên - - - - Compatibility - Độ tương thích - - - - Add-ons - Add-ons - - - - File type - Loại tập tin - - - - Size - Kích thước - - - - Play time - - GameListItemCompat - + Ingame Trong game - + Game starts, but crashes or major glitches prevent it from being completed. Game khởi động, nhưng bị crash hoặc lỗi nghiêm trọng dẫn đến việc không thể hoàn thành nó. - + Perfect Hoàn hảo - + Game can be played without issues. Game có thể chơi mà không gặp vấn đề. - + Playable Có thể chơi - + Game functions with minor graphical or audio glitches and is playable from start to finish. Game hoạt động với lỗi hình ảnh hoặc âm thanh nhẹ và có thể chơi từ đầu tới cuối. - + Intro/Menu Phần mở đầu/Menu - + Game loads, but is unable to progress past the Start Screen. Game đã tải, nhưng không thể qua được màn hình bắt đầu. - + Won't Boot Không khởi động - + The game crashes when attempting to startup. Game crash khi đang khởi động. - + Not Tested Chưa ai thử - + The game has not yet been tested. Game này chưa được thử nghiệm. + + GameListModel + + + Name + + + + + Compatibility + + + + + Add-ons + + + + + File type + + + + + Size + + + + + Play time + + + GameListPlaceholder - + Double-click to add a new folder to the game list Nhấp đúp chuột để thêm một thư mục mới vào danh sách game @@ -6307,17 +6341,17 @@ Please go to Configure -> System -> Network and make a selection. GameListSearchField - + %1 of %n result(s) - + Filter: Lọc: - + Enter pattern to filter Nhập mẫu để lọc @@ -6812,1139 +6846,1099 @@ Debug Message: - + Game &Icon Size - + Reset Window Size to &720p Đặt lại kích thước cửa sổ về &720p - + Reset Window Size to 720p Đặt lại kích thước cửa sổ về 720p - + Reset Window Size to &900p Đặt lại kích thước cửa sổ về &900p - + Reset Window Size to 900p Đặt lại kích thước cửa sổ về 900p - + Reset Window Size to &1080p Đặt lại kích thước cửa sổ về &1080p - + Reset Window Size to 1080p Đặt lại kích thước cửa sổ về 1080p - + &Multiplayer &Nhiều người chơi - + &Tools &Công cụ - + Am&iibo - + Launch &Applet - + &TAS &TAS - + &Create Home Menu Shortcut - + Install &Firmware - + &Help &Trợ giúp - + &Install Files to NAND... &Cài đặt tập tin vào NAND... - + L&oad File... N&ạp tập tin... - + Load &Folder... Nạp &thư mục... - + E&xit T&hoát - - + + &Pause &Tạm dừng - + &Stop &Dừng - + &Verify Installed Contents - + &About Eden - + Single &Window Mode Chế độ &cửa sổ đơn - + Con&figure... Cấu &hình... - + Ctrl+, - + Enable Overlay Display Applet - + Show &Filter Bar Hiện thanh &lọc - + Show &Status Bar Hiện thanh &trạng thái - + Show Status Bar Hiện thanh trạng thái - + &Browse Public Game Lobby &Duyệt phòng game công khai - + &Create Room &Tạo phòng - + &Leave Room &Rời phòng - + &Direct Connect to Room &Kết nối trực tiếp tới phòng - + &Show Current Room &Hiện phòng hiện tại - + F&ullscreen T&oàn màn hình - + &Restart &Khởi động lại - + Load/Remove &Amiibo... Nạp/Loại bỏ &Amiibo... - + &Report Compatibility &Báo cáo độ tương thích - + Open &Mods Page Mở trang &mods - + Open &Quickstart Guide Mở &Hướng dẫn nhanh - + &FAQ &FAQ - + &Capture Screenshot &Chụp ảnh màn hình - + &Album - + &Set Nickname and Owner - + &Delete Game Data - + &Restore Amiibo - + &Format Amiibo - + &Mii Editor - + &Configure TAS... &Cấu hình TAS... - + Configure C&urrent Game... Cấu hình game h&iện tại... - - + + &Start &Bắt đầu - + &Reset &Đặt lại - - + + R&ecord G&hi lại - + Open &Controller Menu - + Install Decryption &Keys - + &Home Menu - + &Desktop - + &Application Menu - + &Root Data Folder - + &NAND Folder - + &SDMC Folder - + &Mod Folder - + &Log Folder - + From Folder - + From ZIP - + &Eden Dependencies - + &Data Manager - + &Tree View - + &Grid View - + Game Icon Size - - + + None - + Show Game &Name - + Show &Performance Overlay - + + &Carousel View + + + + Small (32x32) - + Standard (64x64) - + Large (128x128) - + Full Size (256x256) - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping - + Loading Web Applet... - - + + Disable Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. - + Unmute - + Mute - + Reset Volume - + &Clear Recent Files - + &Continue - + Warning: Outdated Game Format - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. - - + + Error while loading ROM! - + The ROM format is not supported. - + An error occurred initializing the video core. - + Eden has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-mirror.github.io/help/reference/log-files/'>How to Upload the Log File</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. - + (64-bit) - + (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit - + Closing software... - + Save Data - + Mod Data - + Error Opening %1 Folder - - + + Folder does not exist! - + Remove Installed Game Contents? - + Remove Installed Game Update? - + Remove Installed Game DLC? - + Remove Entry - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? - + Remove Cache Storage? - + Remove File - + Remove Play Time Data - + Reset play time? - - + + RomFS Extraction Failed! - + There was an error copying the RomFS files or the user cancelled the operation. - + Full - + Skeleton - + Select RomFS Dump Mode - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... - - + + Cancel - + RomFS Extraction Succeeded! - + The operation completed successfully. - + Error Opening %1 - + Select Directory - + Properties - + The game properties could not be loaded. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. - + Load File - + Open Extracted ROM Directory - + Invalid Directory Selected - + The directory you have selected does not contain a 'main' file. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files - + %n file(s) remaining - + Installing file "%1"... - - + + Install Results - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application - + System Archive - + System Application Update - + Firmware Package (Type A) - + Firmware Package (Type B) - + Game - + Game Update - + Game DLC - + Delta Title - + Select NCA Install Type... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) - + Failed to Install - + The title type you selected for the NCA is invalid. - + File not found - + File "%1" not found - + OK - + Function Disabled - + Compatibility list reporting is currently disabled. Check back later! - + Error opening URL - + Unable to open the URL "%1". - + TAS Recording - + Overwrite file of player 1? - + Invalid config detected - + Handheld controller can't be used on docked mode. Pro controller will be selected. - - + + Amiibo - - + + The current amiibo has been removed - + Error - - + + The current game is not looking for amiibos - + Amiibo File (%1);; All Files (*.*) - + Load Amiibo - + Error loading Amiibo data - + The selected file is not a valid amiibo - + The selected file is already on use - + An unknown error occurred - - - Keys not installed - - - - - - Install decryption keys and restart Eden before attempting to install firmware. - - - - - Select Dumped Firmware Source Location - - - - - Select Dumped Firmware ZIP - - - - - Zipped Archives (*.zip) - - - - - Firmware cleanup failed - - - - - Failed to clean up extracted firmware cache. -Check write permissions in the system temp directory and try again. -OS reported error: %1 - - - - + No firmware available - + Firmware Corrupted - + Unknown applet - + Applet doesn't map to a known value. - + Record not found - + Applet not found. Please reinstall firmware. - + Capture Screenshot - + PNG Image (*.png) - + TAS state: Running %1/%2 - + TAS state: Recording %1 - + TAS state: Idle %1/%2 - + TAS State: Invalid - + &Stop Running - + Stop R&ecording - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor - + Speed: %1% / %2% - + Speed: %1% - + Game: %1 FPS - + Frame: %1 ms - - - FSR - - - - + NO AA - + VOLUME: MUTE - + VOLUME: %1% Volume percentage (e.g. 50%) - + Derivation Components Missing - + Decryption keys are missing. Install them now? - + Wayland Detected! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -7952,74 +7946,74 @@ Would you like to force it for future launches? - + Use X11 - + Continue with Wayland - + Don't show again - + Restart Required - + Restart Eden to apply the X11 backend. - + Slow - + Turbo - + Unlocked - + Select RomFS Dump Target - + Please select which RomFS you would like to dump. - + Are you sure you want to close Eden? - - - + + + Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? @@ -8085,6 +8079,11 @@ Would you like to bypass this and exit anyway? ScaleForce + + + FSR + + Area @@ -8096,52 +8095,62 @@ Would you like to bypass this and exit anyway? - + + SGSR + + + + + SGSR EdgeDir + + + + Docked - + Handheld - + Fast - + Balanced - + Accurate - + Vulkan - + OpenGL GLSL - + OpenGL SPIRV - + OpenGL GLASM - + Null @@ -8174,7 +8183,7 @@ If you wish to clean up the files which were left in the old data location, you - + Data was migrated successfully. @@ -9029,47 +9038,47 @@ p, li { white-space: pre-wrap; } %1 đang chơi %2 - + Play Time: %1 - + Never Played - + Version: %1 - + Version: 1.0.0 - + Installed SD Titles Các title đã cài đặt trên thẻ SD - + Installed NAND Titles Các title đã cài đặt trên NAND - + System Titles Titles hệ thống - + Add New Game Directory Thêm thư mục game - + Favorites Ưa thích @@ -9190,253 +9199,286 @@ p, li { white-space: pre-wrap; } QtCommon::Content - + Game Requires Firmware - + The game you are trying to launch requires firmware to boot or to get past the opening menu. Please <a href='https://yuzu-mirror.github.io/help/quickstart'>dump and install firmware</a>, or press "OK" to launch anyways. - + Installing Firmware... - - - - - + + + + + Cancel - + Firmware Install Failed - + Firmware Install Succeeded - + Firmware integrity verification failed! - - + + Verification failed for the following files: %1 - - + + Verifying integrity... + + + + Integrity verification succeeded! + + - Integrity verification succeeded! - - - - - The operation completed successfully. - - + + Integrity verification failed! - + File contents may be corrupt or missing. - + Integrity verification couldn't be performed - + Firmware installation cancelled, firmware may be in a bad state or corrupted. File contents could not be checked for validity. - + Select Dumped Keys Location - + Decryption Keys install succeeded - + Decryption Keys install failed - + Orphaned Profiles Detected! - + UNEXPECTED BAD THINGS MAY HAPPEN IF YOU DON'T READ THIS!<br>Eden has detected the following save directories with no attached profile:<br>%1<br><br>The following profiles are valid:<br>%2<br><br>Click "OK" to open your save folder and fix up your profiles.<br>Hint: copy the contents of the largest or last-modified folder elsewhere, delete all orphaned profiles, and move your copied contents to the good profile.<br><br>Still confused? See the <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/Orphaned.md'>help page</a>.<br> - + Really clear data? - + Important data may be lost! - + Are you REALLY sure? - + Once deleted, your data will NOT come back! Only do this if you're 100% sure you want to delete this data. - + Clearing... - + Select Export Location - + %1.zip - - + + + Zipped Archives (*.zip) - + Exporting data. This may take a while... - + Exporting - + Exported Successfully - + Data was exported successfully. - + Export Cancelled - + Export was cancelled by the user. - + Export Failed - + Ensure you have write permissions on the targeted directory and try again. - + Select Import Location - + Import Warning - + All previous data in this directory will be deleted. Are you sure you wish to proceed? - + Importing data. This may take a while... - + Importing - + Imported Successfully - + Data was imported successfully. - + Import Cancelled - + Import was cancelled by the user. - + Import Failed - + Ensure you have read permissions on the targeted directory and try again. + + + Keys not installed + + + + + Install decryption keys and restart Eden before attempting to install firmware. + + + + + Select Dumped Firmware Source Location + + + + + Select Dumped Firmware ZIP + + + + + Firmware cleanup failed + + + + + Failed to clean up extracted firmware cache. +Check write permissions in the system temp directory and try again. +OS reported error: %1 + + QtCommon::FS @@ -9680,72 +9722,72 @@ Would you like to manually select a portable folder to use? - + Create Shortcut - + Do you want to launch the game in fullscreen? - + Shortcut Created - + Successfully created a shortcut to %1 - + Shortcut may be Volatile! - + This will create a shortcut to the current AppImage. This may not work well if you update. Continue? - + Failed to Create Shortcut - + Failed to create a shortcut to %1 - + Create Icon - + Cannot create icon file. Path "%1" does not exist and cannot be created. - + No firmware available - + Please install firmware to use the home menu. - + Home Menu Applet - + Home Menu is not available. Please reinstall firmware. @@ -9753,55 +9795,55 @@ Would you like to manually select a portable folder to use? QtCommon::Mod - + Mod Name - + What should this mod be called? - + RomFS - + ExeFS/Patch - + Cheat - + Mod Type - + Could not detect mod type automatically. Please manually specify the type of mod you downloaded. Most mods are RomFS mods, but patches (.pchtxt) are typically ExeFS mods. - - + + Mod Extract Failed - + Failed to create temporary directory %1 - + Zip file %1 is empty @@ -10495,65 +10537,65 @@ By selecting "From Eden", previous save data stored in Ryujinx will be - + New Version Location - + All Files (*.*) - - - + + + Failed to save file - + Could not open file %1 for writing. - + Downloading... - + Cancel - + Could not write to file %1. - + Could not commit to file %1. - + Failed to download file - + Could not download from %1%2 Error code: %3 - + Download Complete - + Successfully downloaded %1. Would you like to open it? diff --git a/dist/languages/vi_VN.ts b/dist/languages/vi_VN.ts index 8fa5494c73..c0284a979f 100644 --- a/dist/languages/vi_VN.ts +++ b/dist/languages/vi_VN.ts @@ -704,7 +704,7 @@ Options lower than 1X can cause artifacts. - Determines how sharpened the image will look using FSR's dynamic contrast. + Determines how sharpened the image will look using FSR's or SGSR's dynamic contrast. @@ -755,35 +755,35 @@ Disabling it is only intended for debugging. - + Use asynchronous GPU emulation - + Uses an extra CPU thread for rendering. This option should always remain enabled. - + NVDEC emulation: Giả lập NVDEC - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. - + ASTC Decoding Method: - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -792,55 +792,66 @@ stuttering but may present artifacts. - + ASTC Recompression Method: - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. - + Frame Pacing Mode (Vulkan only) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. - + VRAM Usage Mode: - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. - + Skip CPU Inner Invalidation - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. - + + Anti-Flicker + + + + + Forces GPU fence callbacks to wait for submitted GPU work. +Use with Fast GPU mode, to avoid flicker with lower performance impact. + + + + VSync Mode: Chế độ Vsync: - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -848,1361 +859,1381 @@ Immediate (no synchronization) presents whatever is available and can exhibit te - + Sync Memory Operations - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. - + Enable asynchronous presentation (Vulkan only) Bật hiển thị bất đồng bộ (chỉ dành cho Vulkan) - + Slightly improves performance by moving presentation to a separate CPU thread. - + Force maximum clocks (Vulkan only) Buộc chạy ở xung nhịp tối đa (chỉ Vulkan) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. Chạy các công việc trong nền trong khi đang chờ lệnh đồ họa để giữ cho GPU không giảm xung nhịp. - + Anisotropic Filtering: Bộ lọc góc nghiêng: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. - + GPU Mode: - + Controls the GPU emulation mode. Most games render fine with Fast or Balanced modes, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. - + DMA Accuracy: - + Controls the DMA precision accuracy. Safe precision fixes issues in some games but may degrade performance. - + Enable asynchronous shader compilation - + May reduce shader stutter. - + Fast GPU Time - + Overclocks the emulated GPU to increase dynamic resolution and render distance. Use 256 for maximal performance and 512 for maximal graphics fidelity. - + GPU Unswizzle - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. - + GPU Unswizzle Max Texture Size - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. - + GPU Unswizzle Stream Size - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. - + GPU Unswizzle Chunk Size - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. - + Use Vulkan pipeline cache Dùng Vulkan pipeline cache - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. - + Enable Compute Pipelines (Intel Vulkan Only) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. - + Enable Reactive Flushing Bật xả tương ứng - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. - + Sync to framerate of video playback Đồng bộ hóa với tốc độ khung hình khi phát video - + Run the game at normal speed during video playback, even when the framerate is unlocked. Chạy game với tốc độ bình thường trong quá trình phát video, ngay cả khi tốc độ khung hình được mở khóa. - + Barrier feedback loops Vòng lặp phản hồi rào cản - + Improves rendering of transparency effects in specific games. Cải thiện hiệu quả hiển thị của hiệu ứng trong suốt trong một số trò chơi. - + Enable buffer history - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. - + Fix bloom effects - + Removes bloom in Burnout. - + Enable Legacy Rescale Pass - + May fix rescale issues in some games by relying on behavior from the previous implementation. Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. - + Extended Dynamic State - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. - + Vertex Input Dynamic State - + Enables vertex input dynamic state feature for better quality and performance. - + Sample Shading - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. - + RNG Seed Hạt giống RNG - + Controls the seed of the random number generator. Mainly used for speedrunning. - + Device Name Tên thiết bị - + The name of the console. - + + Homebrew Args + + + + + Command-line arguments passed to homebrew at launch (e.g. -noglsl). + + + + Custom RTC Date: - + This option allows to change the clock of the console. Can be used to manipulate time in games. - + The number of seconds from the current unix time - + Language: - + This option can be overridden when region setting is auto-select - + Region: Vùng: - + The region of the console. - + Time Zone: Múi giờ: - + The time zone of the console. - + Sound Output Mode: Chế độ đầu ra âm thanh - + Console Mode: - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. - + Prompt for user profile on boot - + Useful if multiple people use the same PC. - + Pause when not in focus - + Pauses emulation when focusing on other windows. - + Confirm before stopping emulation - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. - + Hide mouse on inactivity Ẩn con trỏ chuột khi không dùng - + Hides the mouse after 2.5s of inactivity. - + Disable controller applet Vô hiệu hoá applet tay cầm - + Forcibly disables the use of the controller applet in emulated programs. When a program attempts to open the controller applet, it is immediately closed. - + Check for updates - + Whether or not to check for updates upon startup. - + Enable Gamemode - + Force X11 as Graphics Backend - + Custom frontend - + Real applet - + Never - + On Load - + Always - + CPU CPU - + GPU - + CPU Asynchronous - + Uncompressed (Best quality) Không nén (Chất lượng tốt nhất) - + BC1 (Low quality) BC1 (Chất lượng thấp) - + BC3 (Medium quality) BC3 (Chất lượng trung bình) - - + + Auto Tự động - + 30 FPS - + 60 FPS - + 90 FPS - + 120 FPS - + Conservative - + Aggressive - + Vulkan Vulkan - + OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) - + Null Null - + Fast - + Balanced - - + + Accurate Tuyệt đối - - + + Default Mặc định - + Unsafe (fast) - + Safe (stable) - + Unsafe Tương đối - + Paranoid (disables most optimizations) Paranoid (vô hiệu hoá hầu hết sự tối ưu) - + Debugging - + Dynarmic - + NCE - + Borderless Windowed Cửa sổ không viền - + Exclusive Fullscreen Toàn màn hình - + No Video Output Không Video Đầu Ra - + CPU Video Decoding Giải mã video bằng CPU - + GPU Video Decoding (Default) Giải mã video bằng GPU (Mặc định) - + 0.25X (180p/270p) [EXPERIMENTAL] - + 0.5X (360p/540p) [EXPERIMENTAL] - + 0.75X (540p/810p) [EXPERIMENTAL] 0.75X (540p/810p) [THỬ NGHIỆM] - + 1X (720p/1080p) 1X (720p/1080p) - + 1.25X (900p/1350p) [EXPERIMENTAL] - + 1.5X (1080p/1620p) [EXPERIMENTAL] 1.5X (1080p/1620p) [THỬ NGHIỆM] - + 2X (1440p/2160p) 2X (1440p/2160p) - + 3X (2160p/3240p) 3X (2160p/3240p) - + 4X (2880p/4320p) 4X (2880p/4320p) - + 5X (3600p/5400p) 5X (3600p/5400p) - + 6X (4320p/6480p) 6X (4320p/6480p) - + 7X (5040p/7560p) 7X (5040p/7560p) - + 8X (5760p/8640p) 8X (5760p/8640p) - + Nearest Neighbor Nearest Neighbor - + Bilinear Bilinear - + Bicubic Bicubic - + Gaussian ScaleForce - + Lanczos - + ScaleForce ScaleForce - + AMD FidelityFX Super Resolution - + Area - + MMPX - + Zero-Tangent - + B-Spline - + Mitchell - + Spline-1 - - + + Snapdragon Game Super Resolution + + + + + Snapdragon Game Super Resolution EdgeDir + + + + + None Trống - + FXAA FXAA - + SMAA SMAA - + Default (16:9) Mặc định (16:9) - + Force 4:3 Dùng 4:3 - + Force 21:9 Dùng 21:9 - + Force 16:10 Dung 16:10 - + Stretch to Window Kéo dãn đến cửa sổ phần mềm - + Automatic Tự động - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x - + 32x - + 64x - + Japanese (日本語) Tiếng Nhật (日本語) - + American English Tiếng Anh Mỹ - + French (français) Tiếng Pháp (French) - + German (Deutsch) Tiếng Đức (Deutsch) - + Italian (italiano) Tiếng Ý (italiano) - + Spanish (español) Tiếng Tây Ban Nha (Spanish) - + Chinese Tiếng Trung - + Korean (한국어) Tiếng Hàn (한국어) - + Dutch (Nederlands) Tiếng Hà Lan (Dutch) - + Portuguese (português) Tiếng Bồ Đào Nha (Portuguese) - + Russian (Русский) Tiếng Nga (Русский) - + Taiwanese Tiếng Đài Loan - + British English Tiếng Anh UK (British English) - + Canadian French Tiếng Pháp Canada - + Latin American Spanish Tiếng Mỹ La-tinh - + Simplified Chinese Tiếng Trung giản thể - + Traditional Chinese (正體中文) Tiếng Trung phồn thể (正體中文) - + Brazilian Portuguese (português do Brasil) Tiếng Bồ Đào Nha của người Brazil (Português do Brasil) - + Polish (polska) - + Thai (แบบไทย) - - + + Japan Nhật Bản - + USA Hoa Kỳ - + Europe Châu Âu - + Australia Châu Úc - + China Trung Quốc - + Korea Hàn Quốc - + Taiwan Đài Loan - + Auto (%1) Auto select time zone Tự động (%1) - + Default (%1) Default time zone Mặc định (%1) - + CET CET - + CST6CDT CST6CDT - + Cuba Cuba - + EET EET - + Egypt Ai Cập - + Eire Eire - + EST EST - + EST5EDT EST5EDT - + GB GB - + GB-Eire GB-Eire - + GMT GMT - + GMT+0 GMT+0 - + GMT-0 GMT-0 - + GMT0 GMT0 - + Greenwich Greenwich - + Hongkong Hồng Kông - + HST HST - + Iceland Iceland - + Iran Iran - + Israel Israel - + Jamaica Jamaica - + Kwajalein Kwajalein - + Libya Libya - + MET MET - + MST MST - + MST7MDT MST7MDT - + Navajo Navajo - + NZ NZ - + NZ-CHAT NZ-CHAT - + Poland Ba Lan - + Portugal Bồ Đào Nha - + PRC PRC - + PST8PDT PST8PDT - + ROC ROC - + ROK ROK - + Singapore Singapore - + Turkey Thổ Nhĩ Kỳ - + UCT UCT - + Universal Quốc tế - + UTC UTC - + W-SU W-SU - + WET WET - + Zulu Zulu - + Mono Mono - + Stereo Stereo - + Surround Surround - + 4GB DRAM (Default) - + 6GB DRAM (Unsafe) - + 8GB DRAM - + 10GB DRAM (Unsafe) - + 12GB DRAM (Unsafe) - + Docked Chế độ cắm TV - + Handheld Cầm tay - - + + Off - + Boost (1700MHz) - + Fast (2000MHz) - + Always ask (Default) - + Only if game specifies not to stop - + Never ask - - + + Medium (256) - - + + High (512) - + Very Small (16 MB) - + Small (32 MB) - + Normal (128 MB) - + Large (256 MB) - + Very Large (512 MB) - + Very Low (4 MB) - + Low (8 MB) - + Normal (16 MB) - + Medium (32 MB) - + High (64 MB) - + Very Low (32) - + Low (64) - + Normal (128) - + Disabled - + ExtendedDynamicState 1 - + ExtendedDynamicState 2 - + ExtendedDynamicState 3 - + Tree View - + Grid View @@ -3257,33 +3288,33 @@ Would you like to delete the old save data? Màu nền: - + % - FSR sharpening percentage (e.g. 50%) - % + FSR/SGSR sharpening percentage (e.g. 50%) + - + Off Tắt - + VSync Off Tắt Vsync - + Recommended Đề xuất - + On Bật - + VSync On Bật Vsync @@ -4412,7 +4443,7 @@ Nếu muốn đảo ngược hướng cần điều khiển, di chuyển cần s - + Configure Cài đặt @@ -4443,7 +4474,7 @@ Nếu muốn đảo ngược hướng cần điều khiển, di chuyển cần s - + Test Thử nghiệm @@ -4458,77 +4489,77 @@ Nếu muốn đảo ngược hướng cần điều khiển, di chuyển cần s Xóa Server - + %1:%2 %1:%2 - - - - - - + + + + + + Eden - + Port number has invalid characters Cổng có kí tự không hợp lệ - + Port has to be in range 0 and 65353 Cổng phải từ 0 đến 65353 - + IP address is not valid Địa chỉ IP không hợp lệ - + This UDP server already exists Server UDP đã tồn tại - + Unable to add more than 8 servers Không thể vượt quá 8 server - + Testing Thử nghiệm - + Configuring Cài đặt - + Test Successful Thử Nghiệm Thành Công - + Successfully received data from the server. Nhận được dữ liệu từ server! - + Test Failed Thử Nghiệm Thất Bại - + Could not receive valid data from the server.<br>Please verify that the server is set up correctly and the address and port are correct. Không thể nhận được dữ liệu hợp lệ từ server. <br>Hãy chắc chắn server được thiết lập chính xác, từ địa chỉ lẫn cổng phải được thiết lập đúng. - + UDP Test or calibration configuration is in progress.<br>Please wait for them to finish. Cấu hình kiểm tra hoặc hiệu chuẩn UDP đang được tiến hành.<br>Vui lòng chờ cho đến khi nó hoàn thành. @@ -4713,57 +4744,57 @@ Các giá trị hiện tại lần lượt là %1% và %2%. Một số cài đặt chỉ khả dụng khi game không chạy. - + Add-Ons Bổ Sung - + System Hệ Thống - + CPU CPU - + Graphics Đồ Họa - + Adv. Graphics Đồ Họa Nâng Cao - + Ext. Graphics - + Audio Âm Thanh - + Input Profiles Hồ sơ đầu vào - + Network - + Applets - + Properties Thuộc tính @@ -5774,7 +5805,7 @@ Kéo điểm để thay đổi vị trí, hoặc nhấp đúp chuột vào ô tr - + Calculating... @@ -5976,50 +6007,50 @@ Please go to Configure -> System -> Network and make a selection. GRenderWindow - - + + OpenGL not available! Không có sẵn OpenGL! - + OpenGL shared contexts are not supported. Các ngữ cảnh OpenGL chung không được hỗ trợ. - + Eden has not been compiled with OpenGL support. - - - + + + Error while initializing OpenGL! Đã xảy ra lỗi khi khởi tạo OpenGL! - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. GPU của bạn có thể không hỗ trợ OpenGL, hoặc bạn không có driver đồ hoạ mới nhất. - + Error while initializing OpenGL 4.6! Lỗi khi khởi tạo OpenGL 4.6! - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 GPU của bạn có thể không hỗ trợ OpenGL 4.6, hoặc bạn không có driver đồ hoạ mới nhất.<br><br>GL Renderer:<br>%1 - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 GPU của bạn có thể không hỗ trợ một hoặc nhiều tiện ích OpenGL cần thiết. Vui lòng đảm bảo bạn có driver đồ hoạ mới nhất.<br><br>GL Renderer:<br>%1<br><br>Tiện ích không hỗ trợ:<br>%2 - + This build doesn't have OpenGL support. @@ -6027,279 +6058,282 @@ Please go to Configure -> System -> Network and make a selection. GameList - + &Add New Game Directory - + Favorite Ưa thích - + Start Game Bắt đầu game - + Start Game without Custom Configuration Bắt đầu game mà không có cấu hình tuỳ chỉnh - + Open Save Data Location Mở vị trí lưu dữ liệu - + Open Mod Data Location Mở vị trí chỉnh sửa dữ liệu - + Open Transferable Pipeline Cache Mở thư mục chứa bộ nhớ cache pipeline - + Link to Ryujinx - + Remove Gỡ Bỏ - + Remove Installed Update Loại bỏ bản cập nhật đã cài - + Remove All Installed DLC Loại bỏ tất cả DLC đã cài đặt - + Remove Custom Configuration Loại bỏ cấu hình tuỳ chỉnh - + Remove Cache Storage Xoá bộ nhớ cache - + Remove OpenGL Pipeline Cache Loại bỏ OpenGL Pipeline Cache - + Remove Vulkan Pipeline Cache Loại bỏ bộ nhớ cache pipeline Vulkan - + Remove All Pipeline Caches Loại bỏ tất cả bộ nhớ cache shader - + Remove All Installed Contents Loại bỏ tất cả nội dung đã cài đặt - + Manage Play Time - + Edit Play Time Data - + Remove Play Time Data - - + + Dump RomFS Kết xuất RomFS - + Dump RomFS to SDMC Trích xuất RomFS tới SDMC - + Verify Integrity Kiểm tra tính toàn vẹn - + Copy Title ID to Clipboard Sao chép ID tiêu đề vào bộ nhớ tạm - + Navigate to GameDB entry Điều hướng đến mục cơ sở dữ liệu trò chơi - + Create Shortcut Tạo lối tắt - + Add to Desktop Thêm vào Desktop - + Add to Applications Menu Thêm vào menu ứng dụng - + Configure Game - + Scan Subfolders Quét các thư mục con - + Remove Game Directory Loại bỏ thư mục game - + ▲ Move Up ▲ Di chuyển lên - + ▼ Move Down ▼ Di chuyển xuống - + Open Directory Location Mở vị trí thư mục - + Clear Bỏ trống - - - Name - Tên - - - - Compatibility - Tương thích - - - - Add-ons - Tiện ích ngoài - - - - File type - Loại tệp tin - - - - Size - Kích cỡ - - - - Play time - - GameListItemCompat - + Ingame Trong game - + Game starts, but crashes or major glitches prevent it from being completed. Game khởi động, nhưng gặp vấn đề hoặc lỗi nghiêm trọng đến việc không thể hoàn thành trò chơi. - + Perfect Tốt nhất - + Game can be played without issues. Game có thể chơi mà không gặp vấn đề. - + Playable Có thể chơi - + Game functions with minor graphical or audio glitches and is playable from start to finish. Game hoạt động với lỗi hình ảnh hoặc âm thanh nhẹ và có thể chơi từ đầu tới cuối. - + Intro/Menu Phần mở đầu/Menu - + Game loads, but is unable to progress past the Start Screen. Trò chơi đã tải, nhưng không thể qua Màn hình Bắt đầu. - + Won't Boot Không hoạt động - + The game crashes when attempting to startup. Trò chơi sẽ thoát đột ngột khi khởi động. - + Not Tested Chưa ai thử - + The game has not yet been tested. Trò chơi này chưa có ai thử cả. + + GameListModel + + + Name + + + + + Compatibility + + + + + Add-ons + + + + + File type + + + + + Size + + + + + Play time + + + GameListPlaceholder - + Double-click to add a new folder to the game list Nháy đúp chuột để thêm một thư mục mới vào danh sách trò chơi game @@ -6307,17 +6341,17 @@ Please go to Configure -> System -> Network and make a selection. GameListSearchField - + %1 of %n result(s) - + Filter: Bộ lọc: - + Enter pattern to filter Nhập khuôn để lọc @@ -6812,1139 +6846,1099 @@ Debug Message: - + Game &Icon Size - + Reset Window Size to &720p Đặt lại kích thước cửa sổ về &720p - + Reset Window Size to 720p Đặt lại kích thước cửa sổ về 720p - + Reset Window Size to &900p Đặt lại kích thước cửa sổ về &900p - + Reset Window Size to 900p Đặt lại kích thước cửa sổ về 900p - + Reset Window Size to &1080p Đặt lại kích thước cửa sổ về &1080p - + Reset Window Size to 1080p Đặt lại kích thước cửa sổ về 1080p - + &Multiplayer &Nhiều người chơi - + &Tools &Công cụ - + Am&iibo - + Launch &Applet - + &TAS &TAS - + &Create Home Menu Shortcut - + Install &Firmware - + &Help &Trợ giúp - + &Install Files to NAND... &Cài đặt tập tin vào NAND... - + L&oad File... N&ạp tập tin... - + Load &Folder... Nạp &Thư mục - + E&xit Th&oát - - + + &Pause &Tạm dừng - + &Stop &Dừng - + &Verify Installed Contents - + &About Eden - + Single &Window Mode &Chế độ cửa sổ đơn - + Con&figure... Cấu& hình - + Ctrl+, - + Enable Overlay Display Applet - + Show &Filter Bar Hiện thanh &lọc - + Show &Status Bar Hiện thanh &trạng thái - + Show Status Bar Hiển thị thanh trạng thái - + &Browse Public Game Lobby &Duyệt phòng game công khai - + &Create Room &Tạo phòng - + &Leave Room &Rời phòng - + &Direct Connect to Room &Kết nối trực tiếp tới phòng - + &Show Current Room &Hiện phòng hiện tại - + F&ullscreen T&oàn màn hình - + &Restart &Khởi động lại - + Load/Remove &Amiibo... Tải/Loại bỏ &Amiibo - + &Report Compatibility &Báo cáo tương thích - + Open &Mods Page Mở trang &mods - + Open &Quickstart Guide Mở &Hướng dẫn nhanh - + &FAQ &FAQ - + &Capture Screenshot &Chụp ảnh màn hình - + &Album - + &Set Nickname and Owner - + &Delete Game Data - + &Restore Amiibo - + &Format Amiibo - + &Mii Editor - + &Configure TAS... &Cấu hình TAS... - + Configure C&urrent Game... Cấu hình game hiện tại... - - + + &Start &Bắt đầu - + &Reset &Đặt lại - - + + R&ecord G&hi - + Open &Controller Menu - + Install Decryption &Keys - + &Home Menu - + &Desktop - + &Application Menu - + &Root Data Folder - + &NAND Folder - + &SDMC Folder - + &Mod Folder - + &Log Folder - + From Folder - + From ZIP - + &Eden Dependencies - + &Data Manager - + &Tree View - + &Grid View - + Game Icon Size - - + + None - + Show Game &Name - + Show &Performance Overlay - + + &Carousel View + + + + Small (32x32) - + Standard (64x64) - + Large (128x128) - + Full Size (256x256) - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping - + Loading Web Applet... - - + + Disable Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. - + Unmute - + Mute - + Reset Volume - + &Clear Recent Files - + &Continue - + Warning: Outdated Game Format - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. - - + + Error while loading ROM! - + The ROM format is not supported. - + An error occurred initializing the video core. - + Eden has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-mirror.github.io/help/reference/log-files/'>How to Upload the Log File</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. - + (64-bit) - + (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit - + Closing software... - + Save Data - + Mod Data - + Error Opening %1 Folder - - + + Folder does not exist! - + Remove Installed Game Contents? - + Remove Installed Game Update? - + Remove Installed Game DLC? - + Remove Entry - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? - + Remove Cache Storage? - + Remove File - + Remove Play Time Data - + Reset play time? - - + + RomFS Extraction Failed! - + There was an error copying the RomFS files or the user cancelled the operation. - + Full - + Skeleton - + Select RomFS Dump Mode - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... - - + + Cancel - + RomFS Extraction Succeeded! - + The operation completed successfully. - + Error Opening %1 - + Select Directory - + Properties - + The game properties could not be loaded. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. - + Load File - + Open Extracted ROM Directory - + Invalid Directory Selected - + The directory you have selected does not contain a 'main' file. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files - + %n file(s) remaining - + Installing file "%1"... - - + + Install Results - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application - + System Archive - + System Application Update - + Firmware Package (Type A) - + Firmware Package (Type B) - + Game - + Game Update - + Game DLC - + Delta Title - + Select NCA Install Type... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) - + Failed to Install - + The title type you selected for the NCA is invalid. - + File not found - + File "%1" not found - + OK - + Function Disabled - + Compatibility list reporting is currently disabled. Check back later! - + Error opening URL - + Unable to open the URL "%1". - + TAS Recording - + Overwrite file of player 1? - + Invalid config detected - + Handheld controller can't be used on docked mode. Pro controller will be selected. - - + + Amiibo - - + + The current amiibo has been removed - + Error - - + + The current game is not looking for amiibos - + Amiibo File (%1);; All Files (*.*) - + Load Amiibo - + Error loading Amiibo data - + The selected file is not a valid amiibo - + The selected file is already on use - + An unknown error occurred - - - Keys not installed - - - - - - Install decryption keys and restart Eden before attempting to install firmware. - - - - - Select Dumped Firmware Source Location - - - - - Select Dumped Firmware ZIP - - - - - Zipped Archives (*.zip) - - - - - Firmware cleanup failed - - - - - Failed to clean up extracted firmware cache. -Check write permissions in the system temp directory and try again. -OS reported error: %1 - - - - + No firmware available - + Firmware Corrupted - + Unknown applet - + Applet doesn't map to a known value. - + Record not found - + Applet not found. Please reinstall firmware. - + Capture Screenshot - + PNG Image (*.png) - + TAS state: Running %1/%2 - + TAS state: Recording %1 - + TAS state: Idle %1/%2 - + TAS State: Invalid - + &Stop Running - + Stop R&ecording - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor - + Speed: %1% / %2% - + Speed: %1% - + Game: %1 FPS - + Frame: %1 ms - - - FSR - - - - + NO AA - + VOLUME: MUTE - + VOLUME: %1% Volume percentage (e.g. 50%) - + Derivation Components Missing - + Decryption keys are missing. Install them now? - + Wayland Detected! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -7952,74 +7946,74 @@ Would you like to force it for future launches? - + Use X11 - + Continue with Wayland - + Don't show again - + Restart Required - + Restart Eden to apply the X11 backend. - + Slow - + Turbo - + Unlocked - + Select RomFS Dump Target - + Please select which RomFS you would like to dump. - + Are you sure you want to close Eden? - - - + + + Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? @@ -8085,6 +8079,11 @@ Would you like to bypass this and exit anyway? ScaleForce + + + FSR + + Area @@ -8096,52 +8095,62 @@ Would you like to bypass this and exit anyway? - + + SGSR + + + + + SGSR EdgeDir + + + + Docked - + Handheld - + Fast - + Balanced - + Accurate - + Vulkan - + OpenGL GLSL - + OpenGL SPIRV - + OpenGL GLASM - + Null @@ -8174,7 +8183,7 @@ If you wish to clean up the files which were left in the old data location, you - + Data was migrated successfully. @@ -9029,47 +9038,47 @@ p, li { white-space: pre-wrap; } %1 đang chơi %2 - + Play Time: %1 - + Never Played - + Version: %1 - + Version: 1.0.0 - + Installed SD Titles Các title đã cài đặt trên thẻ SD - + Installed NAND Titles Các title đã cài đặt trên NAND - + System Titles Titles hệ thống - + Add New Game Directory Thêm thư mục game - + Favorites Ưa thích @@ -9190,253 +9199,286 @@ p, li { white-space: pre-wrap; } QtCommon::Content - + Game Requires Firmware - + The game you are trying to launch requires firmware to boot or to get past the opening menu. Please <a href='https://yuzu-mirror.github.io/help/quickstart'>dump and install firmware</a>, or press "OK" to launch anyways. - + Installing Firmware... - - - - - + + + + + Cancel - + Firmware Install Failed - + Firmware Install Succeeded - + Firmware integrity verification failed! - - + + Verification failed for the following files: %1 - - + + Verifying integrity... + + + + Integrity verification succeeded! + + - Integrity verification succeeded! - - - - - The operation completed successfully. - - + + Integrity verification failed! - + File contents may be corrupt or missing. - + Integrity verification couldn't be performed - + Firmware installation cancelled, firmware may be in a bad state or corrupted. File contents could not be checked for validity. - + Select Dumped Keys Location - + Decryption Keys install succeeded - + Decryption Keys install failed - + Orphaned Profiles Detected! - + UNEXPECTED BAD THINGS MAY HAPPEN IF YOU DON'T READ THIS!<br>Eden has detected the following save directories with no attached profile:<br>%1<br><br>The following profiles are valid:<br>%2<br><br>Click "OK" to open your save folder and fix up your profiles.<br>Hint: copy the contents of the largest or last-modified folder elsewhere, delete all orphaned profiles, and move your copied contents to the good profile.<br><br>Still confused? See the <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/Orphaned.md'>help page</a>.<br> - + Really clear data? - + Important data may be lost! - + Are you REALLY sure? - + Once deleted, your data will NOT come back! Only do this if you're 100% sure you want to delete this data. - + Clearing... - + Select Export Location - + %1.zip - - + + + Zipped Archives (*.zip) - + Exporting data. This may take a while... - + Exporting - + Exported Successfully - + Data was exported successfully. - + Export Cancelled - + Export was cancelled by the user. - + Export Failed - + Ensure you have write permissions on the targeted directory and try again. - + Select Import Location - + Import Warning - + All previous data in this directory will be deleted. Are you sure you wish to proceed? - + Importing data. This may take a while... - + Importing - + Imported Successfully - + Data was imported successfully. - + Import Cancelled - + Import was cancelled by the user. - + Import Failed - + Ensure you have read permissions on the targeted directory and try again. + + + Keys not installed + + + + + Install decryption keys and restart Eden before attempting to install firmware. + + + + + Select Dumped Firmware Source Location + + + + + Select Dumped Firmware ZIP + + + + + Firmware cleanup failed + + + + + Failed to clean up extracted firmware cache. +Check write permissions in the system temp directory and try again. +OS reported error: %1 + + QtCommon::FS @@ -9680,72 +9722,72 @@ Would you like to manually select a portable folder to use? - + Create Shortcut Tạo lối tắt - + Do you want to launch the game in fullscreen? Bạn có muốn khởi chạy trò chơi ở chế độ toàn màn hình không? - + Shortcut Created Lối tắt đã được tạo - + Successfully created a shortcut to %1 Đã tạo thành công lối tắt tới %1 - + Shortcut may be Volatile! Lối tắt có thể không ổn định! - + This will create a shortcut to the current AppImage. This may not work well if you update. Continue? Thao tác này sẽ tạo một lối tắt đến AppImage hiện tại. Việc này có thể không hoạt động tốt nếu bạn cập nhật. Bạn có muốn tiếp tục không? - + Failed to Create Shortcut Không thể tạo lối tắt - + Failed to create a shortcut to %1 Không thể tạo lối tắt tới %1 - + Create Icon Tạo icon - + Cannot create icon file. Path "%1" does not exist and cannot be created. Không thể tạo icon. Đường dẫn "%1" không tồn tại và không thể tạo được. - + No firmware available Không có firmware khả dụng - + Please install firmware to use the home menu. - + Home Menu Applet - + Home Menu is not available. Please reinstall firmware. @@ -9753,55 +9795,55 @@ Would you like to manually select a portable folder to use? QtCommon::Mod - + Mod Name - + What should this mod be called? - + RomFS - + ExeFS/Patch - + Cheat - + Mod Type - + Could not detect mod type automatically. Please manually specify the type of mod you downloaded. Most mods are RomFS mods, but patches (.pchtxt) are typically ExeFS mods. - - + + Mod Extract Failed - + Failed to create temporary directory %1 - + Zip file %1 is empty @@ -10495,65 +10537,65 @@ By selecting "From Eden", previous save data stored in Ryujinx will be - + New Version Location - + All Files (*.*) - - - + + + Failed to save file - + Could not open file %1 for writing. - + Downloading... - + Cancel - + Could not write to file %1. - + Could not commit to file %1. - + Failed to download file - + Could not download from %1%2 Error code: %3 - + Download Complete - + Successfully downloaded %1. Would you like to open it? diff --git a/dist/languages/zh_CN.ts b/dist/languages/zh_CN.ts index aa5cda0fcc..c47b0c0e95 100644 --- a/dist/languages/zh_CN.ts +++ b/dist/languages/zh_CN.ts @@ -728,8 +728,8 @@ Options lower than 1X can cause artifacts. - Determines how sharpened the image will look using FSR's dynamic contrast. - 确定使用 FSR 的动态对比度后的图像锐化度。 + Determines how sharpened the image will look using FSR's or SGSR's dynamic contrast. + 确定使用 FSR 或 SGSR 的动态对比度时图像看起来有多锐利。 @@ -786,24 +786,24 @@ Disabling it is only intended for debugging. 请仅在调试时禁用此项。 - + Use asynchronous GPU emulation 使用异步 GPU 模拟 - + Uses an extra CPU thread for rendering. This option should always remain enabled. 使用额外的 CPU 线程进行渲染。 此选项应始终保持启用状态。 - + NVDEC emulation: NVDEC 模拟方式: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. @@ -812,12 +812,12 @@ In most cases, GPU decoding provides the best performance. 大多数情况下,使用 GPU 解码将提供最好的性能。 - + ASTC Decoding Method: ASTC 纹理解码方式: - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -830,12 +830,12 @@ CPU 异步模拟:使用 CPU 在 ASTC 纹理到达时对其进行解码。 消除 ASTC 解码带来的卡顿,但在解码时可能出现渲染问题。 - + ASTC Recompression Method: ASTC 纹理重压缩方式: - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. @@ -844,44 +844,56 @@ BC1/BC3: 中间格式将被重新压缩为 BC1 或 BC3 格式,从而节省显存 但会降低图像质量。 - + Frame Pacing Mode (Vulkan only) 帧同步模式 (仅限 Vulkan) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. 控制模拟器如何管理帧同步,以减少卡顿,使帧率表现更加平稳顺滑。 - + VRAM Usage Mode: VRAM 使用模式: - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. 选择模拟器是应优先节省内存还是最大限度地使用可用视频内存以提高性能。 激进模式可能会影响诸如录屏软件等其他应用程序的性能,。 - + Skip CPU Inner Invalidation 跳过 CPU 内部失效处理 - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. 在内存更新期间跳过某些缓存失效,从而降低 CPU 使用率并改善延迟。这可能导致软件崩溃。 - + + Anti-Flicker + 防闪烁 + + + + Forces GPU fence callbacks to wait for submitted GPU work. +Use with Fast GPU mode, to avoid flicker with lower performance impact. + 强制 GPU fence 回调等待已提交的 GPU 工作。 +与快速 GPU 模式一起使用,以避免较低性能影响下的闪烁。 + + + VSync Mode: 垂直同步模式: - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -892,12 +904,12 @@ Mailbox 的延迟可能比 FIFO 低且不会导致撕裂,但可能会丢帧。 Immediate (不同步) 会呈现全部可用内容,并可能出现撕裂。 - + Sync Memory Operations 同步内存操作 - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. @@ -906,98 +918,98 @@ Unreal Engine 4 games often see the most significant changes thereof. 虚幻 4 引擎的游戏通常会看到最显著的变化。 - + Enable asynchronous presentation (Vulkan only) 启用异步帧提交 (仅限 Vulkan) - + Slightly improves performance by moving presentation to a separate CPU thread. 将帧提交移动到单独的 CPU 线程,略微提高性能。 - + Force maximum clocks (Vulkan only) 强制最大时钟 (仅限 Vulkan) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. 在后台运行的同时等待图形命令,以防止 GPU 降低时钟速度。 - + Anisotropic Filtering: 各向异性过滤: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. 控制在斜角下纹理渲染的质量。 大多数 GPU 上设置为 16 倍是安全的。 - + GPU Mode: GPU 模式: - + Controls the GPU emulation mode. Most games render fine with Fast or Balanced modes, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. 控制 GPU 模拟的精确度。大部分游戏在性能或平衡模式下可以正常渲染,但部分游戏需要设置为精确。粒子效果通常只有在精确模式下才能正确显示。 - + DMA Accuracy: DMA 精度: - + Controls the DMA precision accuracy. Safe precision fixes issues in some games but may degrade performance. 控制 DMA 精度。安全精度可修复某些游戏中的问题,但可能会降低性能。 - + Enable asynchronous shader compilation 开启异步着色器编译 - + May reduce shader stutter. 可能减少着色器卡顿。 - + Fast GPU Time GPU 超频频率 - + Overclocks the emulated GPU to increase dynamic resolution and render distance. Use 256 for maximal performance and 512 for maximal graphics fidelity. 将模拟的 GPU 超频,以提高动态分辨率和渲染距离。使用 256 可获得最大性能,使用 512 可获得最高的图形保真度。 - + GPU Unswizzle GPU 还原 - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. 利用 GPU 计算来加速 BCn 格式 3D 纹理的解码。如果遇到崩溃或画面花屏,请禁用此项。 - + GPU Unswizzle Max Texture Size GPU 还原最大纹理尺寸 - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. @@ -1006,48 +1018,48 @@ Adjust this to find the balance between GPU acceleration and CPU overhead. - + GPU Unswizzle Stream Size GPU 还原流大小 - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. 设置每帧处理的纹理数据最大量(单位:MiB)。 较高的数值可以减少纹理加载时的卡顿,但可能会影响帧率的稳定性(即造成帧时间波动)。 - + GPU Unswizzle Chunk Size GPU 还原块大小 - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. 确定在单次调度(Dispatch)中处理的深度切片(Depth Slices)数量。 增加此数值可以提高高端 GPU 的吞吐量(处理效率),但在性能较弱的硬件上可能会引发 TDR(驱动程序重置)或驱动超时。 - + Use Vulkan pipeline cache 启用 Vulkan 管线缓存 - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. 启用 GPU 供应商专用的管线缓存。 在 Vulkan 驱动程序内部不存储管线缓存的情况下,此选项可显著提高着色器加载速度。 - + Enable Compute Pipelines (Intel Vulkan Only) 启用计算管线 (仅限 Intel 显卡 Vulkan 模式) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. @@ -1056,183 +1068,193 @@ Compute pipelines are always enabled on all other drivers. 在所有其他驱动程序上始终启用计算管线。 - + Enable Reactive Flushing 启用反应性刷新 - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. 使用反应性刷新取代预测性刷新,从而更精确地同步内存。 - + Sync to framerate of video playback 播放视频时帧率同步 - + Run the game at normal speed during video playback, even when the framerate is unlocked. 在视频播放期间以正常速度运行游戏,即使帧率未锁定。 - + Barrier feedback loops 屏障反馈环路 - + Improves rendering of transparency effects in specific games. 改进某些游戏中透明效果的渲染。 - + Enable buffer history 启用缓冲区历史 - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. 允许访问之前的缓冲状态。 这个选项可能会提升某些游戏的渲染质量和性能一致性。 - + Fix bloom effects 修复泛光效果 - + Removes bloom in Burnout. 去除《火爆狂飙》中的泛光特效。 - + Enable Legacy Rescale Pass 启用旧版缩放 - + May fix rescale issues in some games by relying on behavior from the previous implementation. Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. 通过依赖之前实现的行为,可能会修复部分游戏的缩放重叠问题。 修复 AMD 和 Intel 显卡上的线条伪影,以及《路易斯洋楼3》中 Nvidia 显卡的灰色纹理闪烁的遗留行为变通方法。 - + Extended Dynamic State 扩展动态状态 - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. 控制在扩展动态状态中可使用的函数数量。更高的数值允许启用更多功能,并可能提升性能,但同时也可能导致额外的图形问题。 - + Vertex Input Dynamic State 顶点输入动态状态 - + Enables vertex input dynamic state feature for better quality and performance. 开启顶点输入动态状态功能来获得更好的质量和性能。 - + Sample Shading 采样着色 - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. 允许片段着色器在多重采样的片段中每个样本执行一次而不是每个片段执行一次。可以提高图形质量,但会降低性能。 更高的值可以提高质量但会降低性能。 - + RNG Seed 随机数生成器种子 - + Controls the seed of the random number generator. Mainly used for speedrunning. 控制随机数生成器的种子。 主要用于竞速游戏。 - + Device Name 设备名称 - + The name of the console. 主机的数量。 - + + Homebrew Args + + + + + Command-line arguments passed to homebrew at launch (e.g. -noglsl). + + + + Custom RTC Date: 自定义系统时间: - + This option allows to change the clock of the console. Can be used to manipulate time in games. 此选项允许更改控制台的时钟。 可用于操纵游戏中的时间。 - + The number of seconds from the current unix time 来自当前 unix 时间的秒数。 - + Language: 语言: - + This option can be overridden when region setting is auto-select 当区域设置为自动选择时可以使用此选项替代。 - + Region: 地区: - + The region of the console. 主机的区域。 - + Time Zone: 时区: - + The time zone of the console. 主机的时区。 - + Sound Output Mode: 声音输出模式: - + Console Mode: 控制台模式: - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. @@ -1241,1031 +1263,1041 @@ Setting to Handheld can help improve performance for low end systems. 将设置为掌机模式可以帮助低端系统提高性能。 - + Prompt for user profile on boot 启动时提示选择用户账户 - + Useful if multiple people use the same PC. 在多人使用相同的 PC 时有效。 - + Pause when not in focus 在丢失焦点时暂停 - + Pauses emulation when focusing on other windows. 当焦点位于其它窗口时暂停模拟器。 - + Confirm before stopping emulation 停止模拟时需要确认 - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. 替代提示以确认停止模拟。 启用它将绕过此类提示并直接退出模拟。 - + Hide mouse on inactivity 自动隐藏鼠标光标 - + Hides the mouse after 2.5s of inactivity. 在 2.5 秒无活动后隐藏鼠标。 - + Disable controller applet 禁用控制器小程序 - + Forcibly disables the use of the controller applet in emulated programs. When a program attempts to open the controller applet, it is immediately closed. 强制禁止在模拟程序中使用控制器小程序。 当程序尝试打开控制器小程序时,它会立即被关闭。 - + Check for updates 检查更新 - + Whether or not to check for updates upon startup. 在启动时是否检查更新。 - + Enable Gamemode 启用游戏模式 - + Force X11 as Graphics Backend 强制使用 X11 作为图形后端 - + Custom frontend 自定义前端 - + Real applet 真实的小程序 - + Never 永不 - + On Load 加载时 - + Always 总是 - + CPU CPU - + GPU GPU - + CPU Asynchronous CPU 异步模拟 - + Uncompressed (Best quality) 不压缩 (最高质量) - + BC1 (Low quality) BC1 (低质量) - + BC3 (Medium quality) BC3 (中等质量) - - + + Auto 自动 - + 30 FPS 30 FPS - + 60 FPS 60 FPS - + 90 FPS 90 FPS - + 120 FPS 120 FPS - + Conservative 保守模式 - + Aggressive 激进模式 - + Vulkan Vulkan - + OpenGL GLSL OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) OpenGL GLASM (汇编着色器,仅限 NVIDIA 显卡) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) OpenGL SPIR-V (实验性,仅限 AMD/Mesa) - + Null - + Fast 高速 - + Balanced 平衡 - - + + Accurate 高精度 - - + + Default 系统默认 - + Unsafe (fast) 不安全(快速) - + Safe (stable) 安全(稳定) - + Unsafe 低精度 - + Paranoid (disables most optimizations) 偏执模式 (禁用绝大多数优化项) - + Debugging 调试 - + Dynarmic 动态编译 - + NCE 本机代码执行 - + Borderless Windowed 无边框窗口 - + Exclusive Fullscreen 独占全屏 - + No Video Output 无视频输出 - + CPU Video Decoding CPU 视频解码 - + GPU Video Decoding (Default) GPU 视频解码 (默认) - + 0.25X (180p/270p) [EXPERIMENTAL] 0.25X (180p/270p) [实验性] - + 0.5X (360p/540p) [EXPERIMENTAL] 0.5X (360p/540p) [实验性] - + 0.75X (540p/810p) [EXPERIMENTAL] 0.75X (540p/810p) [实验性] - + 1X (720p/1080p) 1X (720p/1080p) - + 1.25X (900p/1350p) [EXPERIMENTAL] 1.25X (900p/1350p) [实验性] - + 1.5X (1080p/1620p) [EXPERIMENTAL] 1.5X (1080p/1620p) [实验性] - + 2X (1440p/2160p) 2X (1440p/2160p) - + 3X (2160p/3240p) 3X (2160p/3240p) - + 4X (2880p/4320p) 4X (2880p/4320p) - + 5X (3600p/5400p) 5X (3600p/5400p) - + 6X (4320p/6480p) 6X (4320p/6480p) - + 7X (5040p/7560p) 7X (5040p/7560p) - + 8X (5760p/8640p) 8X (5760p/8640p) - + Nearest Neighbor 近邻取样 - + Bilinear 双线性过滤 - + Bicubic 双三线过滤 - + Gaussian 高斯模糊 - + Lanczos Lanczos - + ScaleForce 强制缩放 - + AMD FidelityFX Super Resolution AMD FidelityFX 超级分辨率 - + Area 区域 - + MMPX MMPX - + Zero-Tangent 零切线 - + B-Spline B-Spline - + Mitchell Mitchell - + Spline-1 Spline-1 - - + + Snapdragon Game Super Resolution + 骁龙游戏超级分辨率 + + + + Snapdragon Game Super Resolution EdgeDir + 骁龙游戏超级分辨率 EdgeDir + + + + None - + FXAA 快速近似抗锯齿 - + SMAA 子像素形态学抗锯齿 - + Default (16:9) 默认 (16:9) - + Force 4:3 强制 4:3 - + Force 21:9 强制 21:9 - + Force 16:10 强制 16:10 - + Stretch to Window 拉伸窗口 - + Automatic 自动 - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x - + 32x 32x - + 64x 64x - + Japanese (日本語) 日语 (日本語) - + American English 美式英语 - + French (français) 法语 (français) - + German (Deutsch) 德语 (Deutsch) - + Italian (italiano) 意大利语 (italiano) - + Spanish (español) 西班牙语 (español) - + Chinese 中文 - + Korean (한국어) 韩语 (한국어) - + Dutch (Nederlands) 荷兰语 (Nederlands) - + Portuguese (português) 葡萄牙语 (português) - + Russian (Русский) 俄语 (Русский) - + Taiwanese 台湾中文 - + British English 英式英语 - + Canadian French 加拿大法语 - + Latin American Spanish 拉美西班牙语 - + Simplified Chinese 简体中文 - + Traditional Chinese (正體中文) 繁体中文 (正體中文) - + Brazilian Portuguese (português do Brasil) 巴西-葡萄牙语 (português do Brasil) - + Polish (polska) 波兰语(波兰语) - + Thai (แบบไทย) 泰语 - - + + Japan 日本 - + USA 美国 - + Europe 欧洲 - + Australia 澳大利亚 - + China 中国 - + Korea 韩国 - + Taiwan 台湾地区 - + Auto (%1) Auto select time zone 自动 (%1) - + Default (%1) Default time zone 默认 (%1) - + CET 欧洲中部时间 - + CST6CDT 古巴标准时间&古巴夏令时 - + Cuba 古巴 - + EET 东欧时间 - + Egypt 埃及 - + Eire 爱尔兰 - + EST 东部标准时间 - + EST5EDT 东部标准时间&东部夏令时 - + GB 英国 - + GB-Eire 英国-爱尔兰时间 - + GMT GMT - + GMT+0 GMT+0 - + GMT-0 GMT-0 - + GMT0 GMT0 - + Greenwich 格林威治 - + Hongkong 香港 - + HST 美国夏威夷时间 - + Iceland 冰岛 - + Iran 伊朗 - + Israel 以色列 - + Jamaica 牙买加 - + Kwajalein 夸贾林环礁 - + Libya 利比亚 - + MET 中欧时间 - + MST 山区标准时间 (北美) - + MST7MDT 山区标准时间&山区夏令时 (北美) - + Navajo 纳瓦霍 - + NZ 新西兰时间 - + NZ-CHAT NZ-CHAT - + Poland 波兰 - + Portugal 葡萄牙 - + PRC 中国标准时间 - + PST8PDT 太平洋标准时间&太平洋夏令时 - + ROC 台湾时间 - + ROK 韩国时间 - + Singapore 新加坡 - + Turkey 土耳其 - + UCT UCT - + Universal 世界时间 - + UTC 协调世界时 - + W-SU 欧洲-莫斯科时间 - + WET 西欧时间 - + Zulu 祖鲁 - + Mono 单声道 - + Stereo 立体声 - + Surround 环绕声 - + 4GB DRAM (Default) 4GB DRAM (默认) - + 6GB DRAM (Unsafe) 6GB DRAM (不安全) - + 8GB DRAM 8GB DRAM - + 10GB DRAM (Unsafe) 10GB DRAM (不安全) - + 12GB DRAM (Unsafe) 12GB DRAM (不安全) - + Docked 主机模式 - + Handheld 掌机模式 - - + + Off 关闭 - + Boost (1700MHz) 加速 (1700MHz) - + Fast (2000MHz) 快速 (2000MHz) - + Always ask (Default) 总是询问 (默认) - + Only if game specifies not to stop 仅当游戏不希望停止时 - + Never ask 从不询问 - - + + Medium (256) 中(256) - - + + High (512) 高(512) - + Very Small (16 MB) 很小 (16 MB) - + Small (32 MB) 较小 (32 MB) - + Normal (128 MB) 正常 (128 MB) - + Large (256 MB) 较大 (256 MB) - + Very Large (512 MB) 很大 (512 MB) - + Very Low (4 MB) 很低 (4 MB) - + Low (8 MB) 低 (8 MB) - + Normal (16 MB) 正常 (16 MB) - + Medium (32 MB) 中 (32 MB) - + High (64 MB) 高 (64 MB) - + Very Low (32) 很低 (32) - + Low (64) 低 (64) - + Normal (128) 正常 (128) - + Disabled 禁用 - + ExtendedDynamicState 1 扩展动态状态 1 - + ExtendedDynamicState 2 扩展动态状态 2 - + ExtendedDynamicState 3 扩展动态状态 3 - + Tree View 树景视图 - + Grid View 网格视图 @@ -3320,33 +3352,33 @@ Would you like to delete the old save data? 背景颜色: - + % - FSR sharpening percentage (e.g. 50%) + FSR/SGSR sharpening percentage (e.g. 50%) % - + Off 关闭 - + VSync Off 垂直同步关 - + Recommended 推荐 - + On 开启 - + VSync On 垂直同步开 @@ -4475,7 +4507,7 @@ To invert the axes, first move your joystick vertically, and then horizontally.< - + Configure 设置 @@ -4506,7 +4538,7 @@ To invert the axes, first move your joystick vertically, and then horizontally.< - + Test 测试 @@ -4521,77 +4553,77 @@ To invert the axes, first move your joystick vertically, and then horizontally.< 移除服务器 - + %1:%2 %1:%2 - - - - - - + + + + + + Eden Eden - + Port number has invalid characters 端口号中包含无效字符 - + Port has to be in range 0 and 65353 端口必须为 0 到 65353 之间 - + IP address is not valid 无效的 IP 地址 - + This UDP server already exists 此 UDP 服务器已存在 - + Unable to add more than 8 servers 最多只能添加 8 个服务器 - + Testing 测试中 - + Configuring 配置中 - + Test Successful 测试成功 - + Successfully received data from the server. 已成功地从服务器获取数据。 - + Test Failed 测试失败 - + Could not receive valid data from the server.<br>Please verify that the server is set up correctly and the address and port are correct. 无法从服务器获取数据。<br>请验证服务器是否正在运行,以及地址和端口是否配置正确。 - + UDP Test or calibration configuration is in progress.<br>Please wait for them to finish. UDP 测试或触摸校准正在进行中。<br>请耐心等待。 @@ -4776,57 +4808,57 @@ Current values are %1% and %2% respectively. 只有当游戏不在运行时,某些设置项才可用。 - + Add-Ons 附加项 - + System 系统 - + CPU CPU - + Graphics 图形 - + Adv. Graphics 高级图形 - + Ext. Graphics 扩展图形 - + Audio 声音 - + Input Profiles 输入配置文件 - + Network 网络 - + Applets 小程序 - + Properties 属性 @@ -5841,7 +5873,7 @@ Drag points to change position, or double-click table cells to edit values.导入此目录的数据。这可能需要一些时间,并且会删除所有现有数据! - + Calculating... 正在计算... @@ -6044,330 +6076,333 @@ Please go to Configure -> System -> Network and make a selection. GRenderWindow - - + + OpenGL not available! OpenGL 模式不可用! - + OpenGL shared contexts are not supported. 不支持 OpenGL 共享上下文。 - + Eden has not been compiled with OpenGL support. Eden 尚未编译为支持 OpenGL。 - - - + + + Error while initializing OpenGL! 初始化 OpenGL 时出错! - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. 您的 GPU 可能不支持 OpenGL ,或者您没有安装最新的显卡驱动。 - + Error while initializing OpenGL 4.6! 初始化 OpenGL 4.6 时出错! - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 您的 GPU 可能不支持 OpenGL 4.6 ,或者您没有安装最新的显卡驱动。<br><br>GL 渲染器:<br>%1 - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 您的 GPU 可能不支持某些必需的 OpenGL 扩展。请确保您已经安装最新的显卡驱动。<br><br>GL 渲染器:<br>%1<br><br>不支持的扩展:<br>%2 - + This build doesn't have OpenGL support. - + 此版本不支持 OpenGL。 GameList - + &Add New Game Directory 添加新游戏目录 (&A) - + Favorite 收藏 - + Start Game 开始游戏 - + Start Game without Custom Configuration 使用公共设置项进行游戏 - + Open Save Data Location 打开存档位置 - + Open Mod Data Location 打开 MOD 数据位置 - + Open Transferable Pipeline Cache 打开可转移着色器缓存 - + Link to Ryujinx 链接到 Ryujinx - + Remove 删除 - + Remove Installed Update 删除已安装的游戏更新 - + Remove All Installed DLC 删除所有已安装 DLC - + Remove Custom Configuration 删除自定义设置 - + Remove Cache Storage 移除缓存 - + Remove OpenGL Pipeline Cache 删除 OpenGL 着色器缓存 - + Remove Vulkan Pipeline Cache 删除 Vulkan 着色器缓存 - + Remove All Pipeline Caches 删除所有着色器缓存 - + Remove All Installed Contents 删除所有安装的项目 - + Manage Play Time 管理游戏时间 - + Edit Play Time Data 编辑游戏时间数据 - + Remove Play Time Data 清除游玩时间 - - + + Dump RomFS 转储 RomFS - + Dump RomFS to SDMC 转储 RomFS 到 SDMC - + Verify Integrity 完整性验证 - + Copy Title ID to Clipboard 复制游戏 ID 到剪贴板 - + Navigate to GameDB entry 查看兼容性报告 - + Create Shortcut 创建快捷方式 - + Add to Desktop 添加到桌面 - + Add to Applications Menu 添加到应用程序菜单 - + Configure Game 配置游戏 - + Scan Subfolders 扫描子文件夹 - + Remove Game Directory 移除游戏目录 - + ▲ Move Up ▲ 向上移动 - + ▼ Move Down ▼ 向下移动 - + Open Directory Location 打开目录位置 - + Clear 清除 - - - Name - 名称 - - - - Compatibility - 兼容性 - - - - Add-ons - 附加项 - - - - File type - 文件类型 - - - - Size - 大小 - - - - Play time - 游玩时间 - GameListItemCompat - + Ingame 可进游戏 - + Game starts, but crashes or major glitches prevent it from being completed. 游戏可以开始,但会出现崩溃或严重故障导致游戏无法继续。 - + Perfect 完美 - + Game can be played without issues. 游戏可以毫无问题地运行。 - + Playable 可运行 - + Game functions with minor graphical or audio glitches and is playable from start to finish. 游戏可以从头到尾完整地运行,但可能出现轻微的图形或音频故障。 - + Intro/Menu 开场/菜单 - + Game loads, but is unable to progress past the Start Screen. 游戏可以加载,但无法通过标题页面。 - + Won't Boot 无法启动 - + The game crashes when attempting to startup. 在启动游戏时直接崩溃。 - + Not Tested 未测试 - + The game has not yet been tested. 游戏尚未经过测试。 + + GameListModel + + + Name + 名称 + + + + Compatibility + 兼容性 + + + + Add-ons + 附加内容 + + + + File type + 文件类型 + + + + Size + 大小 + + + + Play time + 游戏时间 + + GameListPlaceholder - + Double-click to add a new folder to the game list 双击添加新的游戏文件夹 @@ -6375,17 +6410,17 @@ Please go to Configure -> System -> Network and make a selection. GameListSearchField - + %1 of %n result(s) %n个结果中的第%1个 - + Filter: 搜索: - + Enter pattern to filter 搜索游戏 @@ -6880,1146 +6915,1104 @@ Debug Message: 游戏列表模式 (&G) - + Game &Icon Size 游戏图标大小 (&I) - + Reset Window Size to &720p 重置窗口大小为720p (&7) - + Reset Window Size to 720p 重置窗口大小为720p - + Reset Window Size to &900p 重置窗口大小为900p (&9) - + Reset Window Size to 900p 重置窗口大小为900p - + Reset Window Size to &1080p 重置窗口大小为1080p (&1) - + Reset Window Size to 1080p 重置窗口大小为1080p - + &Multiplayer 多人游戏 (&M) - + &Tools 工具 (&T) - + Am&iibo Am&iibo - + Launch &Applet 启动小程序 (&A) - + &TAS TAS (&T) - + &Create Home Menu Shortcut 创建主页菜单快捷方式(&C) - + Install &Firmware 安装固件(&F) - + &Help 帮助 (&H) - + &Install Files to NAND... 安装文件到 NAND... (&I) - + L&oad File... 加载文件... (&O) - + Load &Folder... 加载文件夹... (&F) - + E&xit 退出 (&X) - - + + &Pause 暂停 (&P) - + &Stop 停止 (&S) - + &Verify Installed Contents 验证已安装内容的完整性 (&V) - + &About Eden 关于 Eden(&A) - + Single &Window Mode 单窗口模式 (&W) - + Con&figure... 设置... (&F) - + Ctrl+, Ctrl+, - + Enable Overlay Display Applet 开启覆盖层显示小程序 - + Show &Filter Bar 显示搜索栏 (&F) - + Show &Status Bar 显示状态栏 (&S) - + Show Status Bar 显示状态栏 - + &Browse Public Game Lobby 浏览公共游戏大厅 (&B) - + &Create Room 创建房间 (&C) - + &Leave Room 离开房间 (&L) - + &Direct Connect to Room 直接连接到房间 (&D) - + &Show Current Room 显示当前房间 (&S) - + F&ullscreen 全屏 (&U) - + &Restart 重新启动 (&R) - + Load/Remove &Amiibo... 加载/移除 Amiibo... (&A) - + &Report Compatibility 报告兼容性 (&R) - + Open &Mods Page 打开 Mod 页面 (&M) - + Open &Quickstart Guide 查看快速导航 (&Q) - + &FAQ FAQ (&F) - + &Capture Screenshot 捕获截图 (&C) - + &Album 相册 (&A) - + &Set Nickname and Owner 设置昵称及所有者 (&S) - + &Delete Game Data 删除游戏数据 (&D) - + &Restore Amiibo 重置 Amiibo (&R) - + &Format Amiibo 格式化 Amiibo (&F) - + &Mii Editor Mii 编辑器 (&M) - + &Configure TAS... 配置 TAS... (&C) - + Configure C&urrent Game... 配置当前游戏... (&U) - - + + &Start 开始 (&S) - + &Reset 重置 (&R) - - + + R&ecord 录制 (&E) - + Open &Controller Menu 打开控制器菜单 (&C) - + Install Decryption &Keys 安装解密密钥(&K) - + &Home Menu 主页 (&H) - + &Desktop 桌面(&D) - + &Application Menu 应用程序菜单(&A) - + &Root Data Folder 根数据文件夹(&R) - + &NAND Folder &NAND 文件夹 - + &SDMC Folder &SDMC 文件夹 - + &Mod Folder &Mod 文件夹 - + &Log Folder &Log 文件夹 - + From Folder 从文件夹 - + From ZIP 从 ZIP - + &Eden Dependencies &Eden 依赖项 - + &Data Manager 数据管理器(&D) - + &Tree View 树景视图 (&T) - + &Grid View 网格视图 (&G) - + Game Icon Size 游戏图标大小 - - + + None - + Show Game &Name 显示游戏名称 (&N) - + Show &Performance Overlay 显示性能覆盖层 - + + &Carousel View + + + + Small (32x32) 小 (32x32) - + Standard (64x64) 标准 (64x64) - + Large (128x128) 大 (128x128) - + Full Size (256x256) 最大 (256x256) - + Broken Vulkan Installation Detected 检测到损坏的 Vulkan 安装 - + Vulkan initialization failed during boot. 在启动时初始化 Vulkan 失败。 - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping 运行游戏 - + Loading Web Applet... 正在加载 Web 小程序... - - + + Disable Web Applet 禁用 Web 小程序 - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) 禁用网页小程序可能会导致未定义的行为并且应仅在 超级马里奥 3D 全明星中使用。您确定要禁用网页小程序吗? (这可以在调试设置中重新启用。) - + The amount of shaders currently being built 当前正在构建的着色器数量 - + The current selected resolution scaling multiplier. 当前选择的分辨率缩放倍数。 - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. 当前模拟速度。高于或低于 100% 的数值表示模拟运行比 Switch 快或慢。 - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. 游戏当前显示的每秒帧数。这个数值会因游戏和场景的不同而有所变化。 - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. 模拟 Switch 一帧所需的时间,不包括帧限制或垂直同步。为了全速模拟这个时间最多应为 16.67 毫秒。 - + Unmute 取消静音 - + Mute 静音 - + Reset Volume 重置音量 - + &Clear Recent Files 清除最近的文件(&C) - + &Continue 继续(&C) - + Warning: Outdated Game Format 警告: 游戏格式过时 - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. 您正在为此游戏使用解包 ROM 目录格式,这是一种已过时的格式,它已被 NCA、NAX、XCI 或 NSP 等其他格式取代。解包 ROM 目录缺少图标、元数据和更新支持。<br>有关 Eden 支持的各种 Switch 格式的说明请查阅我们的用户手册。此消息将不再显示。 - - + + Error while loading ROM! 加载 ROM 时出错! - + The ROM format is not supported. 不支持该 ROM 格式。 - + An error occurred initializing the video core. 初始化视频核心时发生错误。 - + Eden has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-mirror.github.io/help/reference/log-files/'>How to Upload the Log File</a>. Eden 在运行视频核心时遇到了错误。通常这是由于 GPU 驱动程序过时引起的,包括集成显卡驱动程序。有关详细信息,请查看日志。有关如何访问日志的更多信息,请参阅以下页面:<a href="https://yuzu-mirror.github.io/help/reference/log-files/">如何上传日志文件</a>。 - + Error while loading ROM! %1 %1 signifies a numeric error code. 加载 ROM 时出错! %1 - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. %1<br>请重新导出您的文件,或在 Discord/Stoat 上寻求帮助。 - + An unknown error occurred. Please see the log for more details. 发生未知错误。请查看日志以获取更多详情。 - + (64-bit) (64 位) - + (32-bit) (32 位) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Closing software... 正在关闭软件... - + Save Data 存档数据 - + Mod Data Mod 数据 - + Error Opening %1 Folder 打开 %1 文件夹出错 - - + + Folder does not exist! 文件夹不存在! - + Remove Installed Game Contents? 是否移除已安装的游戏内容? - + Remove Installed Game Update? 是否移除已安装的游戏更新? - + Remove Installed Game DLC? 是否移除已安装的游戏 DLC? - + Remove Entry 删除条目 - + Delete OpenGL Transferable Shader Cache? 要删除 OpenGL 可传输着色器缓存吗? - + Delete Vulkan Transferable Shader Cache? 要删除 Vulkan 可传输着色器缓存吗? - + Delete All Transferable Shader Caches? 删除所有可传输的着色器缓存? - + Remove Custom Game Configuration? 是否移除自定义游戏配置? - + Remove Cache Storage? 要清除缓存存储吗? - + Remove File 删除文件 - + Remove Play Time Data 删除游戏时间数据 - + Reset play time? 要重置播放时间吗? - - + + RomFS Extraction Failed! RomFS 提取失败! - + There was an error copying the RomFS files or the user cancelled the operation. 复制 RomFS 文件时出错或用户取消了操作。 - + Full 完整 - + Skeleton 结构 - + Select RomFS Dump Mode 选择 RomFS 转储模式 - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. 请选择您希望如何导出 RomFS。<br>&quot;完整&quot; 将把所有文件复制到新的目录中,而<br>&quot;结构&quot; 仅会创建目录结构。</br></br> - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root %1 的可用空间不足,无法提取 RomFS。请释放空间或在模拟 > 配置 > 系统 > 文件系统 > 转储根目录,中选择其它目录 - + Extracting RomFS... 正在提取 RomFS... - - + + Cancel 取消 - + RomFS Extraction Succeeded! RomFS 提取成功! - + The operation completed successfully. 操作已成功完成。 - + Error Opening %1 打开 %1 时出错 - + Select Directory 选择目录 - + Properties 属性 - + The game properties could not be loaded. 无法加载游戏属性。 - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Switch 可执行文件 (%1);;所有文件 (*.*) - + Load File 加载文件 - + Open Extracted ROM Directory 打开已提取的 ROM 目录 - + Invalid Directory Selected 选择的目录无效 - + The directory you have selected does not contain a 'main' file. 您选择的目录不包含 'main' 文件。 - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) 可安装的 Switch 文件 (*.nca *.nsp *.xci);;任天堂内容档案 (*.nca);;任天堂提交包 (*.nsp);;NX 卡带镜像 (*.xci) - + Install Files 安装文件 - + %n file(s) remaining 剩余 %n 个文件 - + Installing file "%1"... 正在安装文件 "%1"... - - + + Install Results 安装结果 - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. 为了避免可能的冲突,我们不鼓励用户将基础游戏安装到 NAND。 请仅使用此功能来安装更新和 DLC。 - + %n file(s) were newly installed 已新安装 %n 个文件 - + %n file(s) were overwritten 已覆盖了 %n 个文件 - + %n file(s) failed to install %n 个文件安装失败 - + System Application 系统应用 - + System Archive 系统档案 - + System Application Update 系统应用更新 - + Firmware Package (Type A) 固件包 (类型 A) - + Firmware Package (Type B) 固件包 (类型 B) - + Game 游戏 - + Game Update 游戏更新 - + Game DLC 游戏可下载内容 - + Delta Title Delta 标题 - + Select NCA Install Type... 选择 NCA 安装类型... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) 请选择您希望将此 NCA 安装为的标题类型: (在大多数情况下,默认的 '游戏' 就可以。) - + Failed to Install 安装失败 - + The title type you selected for the NCA is invalid. 您为 NCA 选择的标题类型无效。 - + File not found 找不到文件 - + File "%1" not found 未找到文件 "%1" - + OK 确定 - + Function Disabled 功能已被关闭 - + Compatibility list reporting is currently disabled. Check back later! 兼容性列表报告目前已被禁用。请稍后再查看! - + Error opening URL 打开网址出错 - + Unable to open the URL "%1". 无法打开 URL "%1"。 - + TAS Recording TAS 录像 - + Overwrite file of player 1? 要覆盖玩家 1 的文件吗? - + Invalid config detected 检测到无效配置 - + Handheld controller can't be used on docked mode. Pro controller will be selected. 手柄在主机模式下无法使用。将选择 Pro 手柄。 - - + + Amiibo Amiibo - - + + The current amiibo has been removed 当前的 amiibo 已被移除 - + Error 错误 - - + + The current game is not looking for amiibos 当前游戏不支持寻找 amiibo - + Amiibo File (%1);; All Files (*.*) Amiibo 文件 (%1);; 所有文件 (*.*) - + Load Amiibo 读取 Amiibo - + Error loading Amiibo data 加载 Amiibo 数据出错 - + The selected file is not a valid amiibo 所选文件不是有效的 amiibo - + The selected file is already on use 所选文件正在使用中 - + An unknown error occurred 发生未知错误 - - - Keys not installed - 未安装密钥 - - - - - Install decryption keys and restart Eden before attempting to install firmware. - 在尝试安装固件之前请先安装解密密钥并重启 Eden。 - - - - Select Dumped Firmware Source Location - 选择已转储固件源位置 - - - - Select Dumped Firmware ZIP - 选择已转储的固件 ZIP - - - - Zipped Archives (*.zip) - 压缩文件 (*.zip) - - - - Firmware cleanup failed - 固件清理失败 - - - - Failed to clean up extracted firmware cache. -Check write permissions in the system temp directory and try again. -OS reported error: %1 - 清理提取的固件缓存失败。 -检查系统临时目录的写入权限然后重试。 -操作系统报告错误: %1 - - - + No firmware available 没有可用的固件 - + Firmware Corrupted 固件已损坏 - + Unknown applet 未知小程序 - + Applet doesn't map to a known value. 无法识别该小程序对应的值。 - + Record not found 找不到记录程序 - + Applet not found. Please reinstall firmware. 找不到小程序。请重新安装固件。 - + Capture Screenshot 截取屏幕截图 - + PNG Image (*.png) PNG 图像 (*.png) - + TAS state: Running %1/%2 TAS 状态: 正在运行 %1/%2 - + TAS state: Recording %1 TAS 状态: 正在录制 %1 - + TAS state: Idle %1/%2 TAS 状态: 空闲 %1/%2 - + TAS State: Invalid TAS 状态: 无效 - + &Stop Running 停止运行(&S) - + Stop R&ecording 停止录制(&A) - + Building: %n shader(s) 正在编译:%n 个着色器 - + Scale: %1x %1 is the resolution scaling factor 缩放: %1x - + Speed: %1% / %2% 速度: %1% / %2% - + Speed: %1% 速度: %1% - + Game: %1 FPS 游戏: %1 FPS - + Frame: %1 ms 帧: %1 ms - - - FSR - FSR - - - + NO AA 无 AA - + VOLUME: MUTE 音量: 静音 - + VOLUME: %1% Volume percentage (e.g. 50%) 音量: %1% - + Derivation Components Missing 缺少派生组件 - + Decryption keys are missing. Install them now? 缺少解密密钥。现在安装吗? - + Wayland Detected! 检测到 Wayland! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -8030,74 +8023,74 @@ Would you like to force it for future launches? 您想要在未来的启动中强制执行吗? - + Use X11 使用 X11 - + Continue with Wayland 继续使用 Wayland - + Don't show again 不再显示 - + Restart Required 需要重新启动 - + Restart Eden to apply the X11 backend. 重新启动 Eden 以应用 X11 后端。 - + Slow 慢速 - + Turbo 加速 - + Unlocked 解锁 - + Select RomFS Dump Target 选择 RomFS 转储目标 - + Please select which RomFS you would like to dump. 请选择您想要转储的 RomFS。 - + Are you sure you want to close Eden? 您确实要关闭 Eden 吗? - - - + + + Eden Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. 您确定要停止模拟吗?任何未保存的进度将会丢失。 - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? @@ -8165,6 +8158,11 @@ Would you like to bypass this and exit anyway? ScaleForce ScaleForce + + + FSR + FSR + Area @@ -8176,52 +8174,62 @@ Would you like to bypass this and exit anyway? MMPX - + + SGSR + SGSR + + + + SGSR EdgeDir + SGSR EdgeDir + + + Docked 主机模式 - + Handheld 掌机模式 - + Fast 加速 - + Balanced 平衡 - + Accurate 精确 - + Vulkan Vulkan - + OpenGL GLSL OpenGL GLSL - + OpenGL SPIRV OpenGL SPIRV - + OpenGL GLASM OpenGL GLASM - + Null @@ -8264,7 +8272,7 @@ If you wish to clean up the files which were left in the old data location, you %1 - + Data was migrated successfully. 数据已成功迁移。 @@ -9122,47 +9130,47 @@ p, li { white-space: pre-wrap; } %1 正在玩 %2 - + Play Time: %1 游玩时间:%1 - + Never Played 未曾游玩 - + Version: %1 版本:%1 - + Version: 1.0.0 版本:1.0.0 - + Installed SD Titles SD 卡中安装的项目 - + Installed NAND Titles NAND 中安装的项目 - + System Titles 系统项目 - + Add New Game Directory 添加游戏目录 - + Favorites 收藏 @@ -9283,47 +9291,47 @@ p, li { white-space: pre-wrap; } QtCommon::Content - + Game Requires Firmware 游戏需要固件 - + The game you are trying to launch requires firmware to boot or to get past the opening menu. Please <a href='https://yuzu-mirror.github.io/help/quickstart'>dump and install firmware</a>, or press "OK" to launch anyways. 您正尝试启动的游戏需要固件才能启动或通过启动画面打开菜单。请 <a href='https://yuzu-mirror.github.io/help/quickstart'>转储并安装固件</a>, 或点击 "确定" 继续启动。 - + Installing Firmware... 正在安装固件…… - - - - - + + + + + Cancel 取消 - + Firmware Install Failed 安装固件失败 - + Firmware Install Succeeded 安装固件成功 - + Firmware integrity verification failed! 固件完整性验证失败! - - + + Verification failed for the following files: %1 @@ -9332,207 +9340,242 @@ p, li { white-space: pre-wrap; } %1 - - + + Verifying integrity... 正在验证完整性... - - + + Integrity verification succeeded! 完整性验证成功! - - + + The operation completed successfully. 操作成功完成。 - - + + Integrity verification failed! 完整性验证失败! - + File contents may be corrupt or missing. 文件内容可能缺失或已损坏。 - + Integrity verification couldn't be performed 无法执行完整性验证 - + Firmware installation cancelled, firmware may be in a bad state or corrupted. File contents could not be checked for validity. 固件安装失败。固件可能处于异常状态或已损坏,无法验证文件内容的有效性。 - + Select Dumped Keys Location 选择导出的密钥文件位置 - + Decryption Keys install succeeded 密钥文件安装成功 - + Decryption Keys install failed 密钥文件安装失败 - + Orphaned Profiles Detected! 检测到孤立的配置文件! - + UNEXPECTED BAD THINGS MAY HAPPEN IF YOU DON'T READ THIS!<br>Eden has detected the following save directories with no attached profile:<br>%1<br><br>The following profiles are valid:<br>%2<br><br>Click "OK" to open your save folder and fix up your profiles.<br>Hint: copy the contents of the largest or last-modified folder elsewhere, delete all orphaned profiles, and move your copied contents to the good profile.<br><br>Still confused? See the <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/Orphaned.md'>help page</a>.<br> 如果您不阅读此内容,可能会发生意想不到的糟糕情况!<br>Eden 检测到以下存档目录没有附加的配置文件:<br>%1<br><br>下列配置是有效的:<br>%2<br><br>点击“确定”以打开您的存档文件夹并修复配置文件。<br>提示: 将最大或最近修改的文件夹内容复制到其他地方,删除所有孤立的配置文件,然后将复制的内容移到正确的配置文件中。<br><br>还是感到疑惑? 请查看 <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/Orphaned.md'>帮助页</a>。<br> - + Really clear data? 确实要清除数据吗? - + Important data may be lost! 可能会丢失重要的数据! - + Are you REALLY sure? 您真的确定吗? - + Once deleted, your data will NOT come back! Only do this if you're 100% sure you want to delete this data. 在删除后,您将无法找回数据! 仅在您 100% 确认要删除此数据时才这样做。 - + Clearing... 正在清除... - + Select Export Location 选择导出位置 - + %1.zip %1.zip - - + + + Zipped Archives (*.zip) 压缩档案 (*.zip) - + Exporting data. This may take a while... 正在导出数据。这可能需要一些时间... - + Exporting 正在导出 - + Exported Successfully 导出成功 - + Data was exported successfully. 数据已成功导出。 - + Export Cancelled 导出已被取消 - + Export was cancelled by the user. 导出已被用户取消。 - + Export Failed 导出失败 - + Ensure you have write permissions on the targeted directory and try again. 请确认您是否具有目标目录的写入权限然后再次尝试。 - + Select Import Location 选择导入位置 - + Import Warning 导入警告 - + All previous data in this directory will be deleted. Are you sure you wish to proceed? 此目录中的所有先前数据将被删除。您确定要继续吗? - + Importing data. This may take a while... 正在导入数据。这需要一些时间... - + Importing 正在导入 - + Imported Successfully 导入成功 - + Data was imported successfully. 数据已导入成功。 - + Import Cancelled 导入已被取消 - + Import was cancelled by the user. 导入已被用户取消。 - + Import Failed 导入失败 - + Ensure you have read permissions on the targeted directory and try again. 请确认是否您具有目标目录的读取权限然后再次尝试。 + + + Keys not installed + 未安装密钥 + + + + Install decryption keys and restart Eden before attempting to install firmware. + 在尝试安装固件之前先安装解密密钥并重启 Eden。 + + + + Select Dumped Firmware Source Location + 选择已转储的固件源位置 + + + + Select Dumped Firmware ZIP + 选择已转储的固件 ZIP + + + + Firmware cleanup failed + 清理固件失败 + + + + Failed to clean up extracted firmware cache. +Check write permissions in the system temp directory and try again. +OS reported error: %1 + 清理提取的固件缓存失败。 +请检查系统临时目录的写入权限然后重试。 +OS 报告的错误: %1 + QtCommon::FS @@ -9779,72 +9822,72 @@ Would you like to manually select a portable folder to use? 缓存数据删除失败。它可能不存在或正在被使用。 - + Create Shortcut 创建快捷方式 - + Do you want to launch the game in fullscreen? 您想以全屏模式启动游戏吗? - + Shortcut Created 已创建快捷方式 - + Successfully created a shortcut to %1 %1 的快捷方式创建成功 - + Shortcut may be Volatile! 快捷方式可能不稳定! - + This will create a shortcut to the current AppImage. This may not work well if you update. Continue? 这回创建到当前 AppImage 的快捷方式。它可能会在您更新后失效。要继续吗? - + Failed to Create Shortcut 创建快捷方式失败 - + Failed to create a shortcut to %1 %1 的快捷方式创建失败 - + Create Icon 创建图标 - + Cannot create icon file. Path "%1" does not exist and cannot be created. 无法创建图标文件。路径“ %1 ”不存在且无法被创建。 - + No firmware available 无可用固件 - + Please install firmware to use the home menu. 请先安装固件才能使用主页菜单。 - + Home Menu Applet 主页菜单小程序 - + Home Menu is not available. Please reinstall firmware. 主页菜单不可用。请重新安装固件。 @@ -9852,37 +9895,37 @@ Would you like to manually select a portable folder to use? QtCommon::Mod - + Mod Name 模组名称 - + What should this mod be called? 这个模组应该叫什么名字? - + RomFS RomFS - + ExeFS/Patch ExeFS/Patch - + Cheat 作弊 - + Mod Type 模组类型 - + Could not detect mod type automatically. Please manually specify the type of mod you downloaded. Most mods are RomFS mods, but patches (.pchtxt) are typically ExeFS mods. @@ -9891,18 +9934,18 @@ Most mods are RomFS mods, but patches (.pchtxt) are typically ExeFS mods. - - + + Mod Extract Failed 模组提取失败 - + Failed to create temporary directory %1 未能创建临时 %1 目录 - + Zip file %1 is empty 压缩文件 %1 是空的。 @@ -10604,66 +10647,66 @@ By selecting "From Eden", previous save data stored in Ryujinx will be %1 可用于下载。 - + New Version Location 新版本位置 - + All Files (*.*) 所有文件 (*.*) - - - + + + Failed to save file 保存文件失败 - + Could not open file %1 for writing. 无法打开要写入的 %1 文件。 - + Downloading... 正在下载... - + Cancel 取消 - + Could not write to file %1. 无法写入到文件 %1。 - + Could not commit to file %1. 无法提交到文件 %1。 - + Failed to download file 下载文件失败 - + Could not download from %1%2 Error code: %3 无法从 %1%2 下载 错误代码: %3 - + Download Complete 下载完成 - + Successfully downloaded %1. Would you like to open it? 已成功下载 %1。您要打开它吗? diff --git a/dist/languages/zh_TW.ts b/dist/languages/zh_TW.ts index b09469591b..6c88bcb756 100644 --- a/dist/languages/zh_TW.ts +++ b/dist/languages/zh_TW.ts @@ -724,8 +724,8 @@ Options lower than 1X can cause artifacts. - Determines how sharpened the image will look using FSR's dynamic contrast. - 調整使用FSR的動態對比時影像的銳利度 + Determines how sharpened the image will look using FSR's or SGSR's dynamic contrast. + @@ -780,23 +780,23 @@ Disabling it is only intended for debugging. 建議僅在偵錯時才停用此選項 - + Use asynchronous GPU emulation - + Uses an extra CPU thread for rendering. This option should always remain enabled. - + NVDEC emulation: NVDEC 模擬: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. @@ -805,12 +805,12 @@ In most cases, GPU decoding provides the best performance. GPU解碼在大多數情況下提供最好的性能 - + ASTC Decoding Method: ASTC解碼方式: - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -819,55 +819,66 @@ stuttering but may present artifacts. - + ASTC Recompression Method: ASTC重新壓縮方式: - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. - + Frame Pacing Mode (Vulkan only) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. - + VRAM Usage Mode: VRAM 使用模式: - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. - + Skip CPU Inner Invalidation 跳過CPU內部失效處理 - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. - + + Anti-Flicker + + + + + Forces GPU fence callbacks to wait for submitted GPU work. +Use with Fast GPU mode, to avoid flicker with lower performance impact. + + + + VSync Mode: 垂直同步: - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -875,1362 +886,1382 @@ Immediate (no synchronization) presents whatever is available and can exhibit te - + Sync Memory Operations 同步記憶體操作 - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. - + Enable asynchronous presentation (Vulkan only) 啟用非同步顯示(僅限Vulkan) - + Slightly improves performance by moving presentation to a separate CPU thread. 透過將畫面顯示移至獨立的CPU執行緒來略微提升性能。 - + Force maximum clocks (Vulkan only) 強制使用最大時脈(僅限Vulkan) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. 在等待圖形命令時於背景執行任務以防GPU降低時脈 - + Anisotropic Filtering: 各向異性過濾: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. - + GPU Mode: - + Controls the GPU emulation mode. Most games render fine with Fast or Balanced modes, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. - + DMA Accuracy: - + Controls the DMA precision accuracy. Safe precision fixes issues in some games but may degrade performance. - + Enable asynchronous shader compilation - + May reduce shader stutter. - + Fast GPU Time - + Overclocks the emulated GPU to increase dynamic resolution and render distance. Use 256 for maximal performance and 512 for maximal graphics fidelity. - + GPU Unswizzle - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. - + GPU Unswizzle Max Texture Size - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. - + GPU Unswizzle Stream Size - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. - + GPU Unswizzle Chunk Size - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. - + Use Vulkan pipeline cache 启用 Vulkan 管线缓存 - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. 启用 GPU 供应商专用的管线缓存。 在 Vulkan 驱动程序内部不存储管线缓存的情况下,此选项可显著提高着色器加载速度。 - + Enable Compute Pipelines (Intel Vulkan Only) 启用计算管线 (仅限 Intel 显卡 Vulkan 模式) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. - + Enable Reactive Flushing 启用反应性刷新 - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. 使用反应性刷新取代预测性刷新,从而更精确地同步内存。 - + Sync to framerate of video playback 播放视频时帧率同步 - + Run the game at normal speed during video playback, even when the framerate is unlocked. 在视频播放期间以正常速度运行游戏,即使帧率未锁定。 - + Barrier feedback loops 屏障反馈循环 - + Improves rendering of transparency effects in specific games. 改进某些游戏中透明效果的渲染。 - + Enable buffer history - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. - + Fix bloom effects - + Removes bloom in Burnout. - + Enable Legacy Rescale Pass - + May fix rescale issues in some games by relying on behavior from the previous implementation. Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. - + Extended Dynamic State - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. - + Vertex Input Dynamic State - + Enables vertex input dynamic state feature for better quality and performance. - + Sample Shading - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. - + RNG Seed 隨機種子 - + Controls the seed of the random number generator. Mainly used for speedrunning. - + Device Name 裝置名稱 - + The name of the console. - + + Homebrew Args + + + + + Command-line arguments passed to homebrew at launch (e.g. -noglsl). + + + + Custom RTC Date: 自定义系统时间: - + This option allows to change the clock of the console. Can be used to manipulate time in games. - + The number of seconds from the current unix time - + Language: 语言: - + This option can be overridden when region setting is auto-select - + Region: 區域: - + The region of the console. - + Time Zone: 時區: - + The time zone of the console. - + Sound Output Mode: 音訊輸出模式: - + Console Mode: 控制台模式: - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. - + Prompt for user profile on boot - + Useful if multiple people use the same PC. - + Pause when not in focus - + Pauses emulation when focusing on other windows. - + Confirm before stopping emulation 停止模拟时需要确认 - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. - + Hide mouse on inactivity 滑鼠閒置時自動隱藏 - + Hides the mouse after 2.5s of inactivity. - + Disable controller applet 禁用控制器程序 - + Forcibly disables the use of the controller applet in emulated programs. When a program attempts to open the controller applet, it is immediately closed. - + Check for updates - + Whether or not to check for updates upon startup. - + Enable Gamemode 启用游戏模式 - + Force X11 as Graphics Backend - + Custom frontend 自定义前端 - + Real applet 真实的小程序 - + Never - + On Load - + Always - + CPU CPU - + GPU GPU - + CPU Asynchronous CPU 异步模拟 - + Uncompressed (Best quality) 不壓縮 (最高品質) - + BC1 (Low quality) BC1 (低品質) - + BC3 (Medium quality) BC3 (中品質) - - + + Auto 自動 - + 30 FPS - + 60 FPS - + 90 FPS - + 120 FPS - + Conservative 保守模式(节省 VRAM) - + Aggressive 激进模式 - + Vulkan Vulkan - + OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) - + Null - + Fast - + Balanced - - + + Accurate 高精度 - - + + Default 預設 - + Unsafe (fast) - + Safe (stable) - + Unsafe 低精度 - + Paranoid (disables most optimizations) 偏执模式 (禁用绝大多数优化项) - + Debugging - + Dynarmic Dynarmic - + NCE NCE - + Borderless Windowed 無邊框視窗 - + Exclusive Fullscreen 全螢幕獨占 - + No Video Output 無視訊輸出 - + CPU Video Decoding CPU 視訊解碼 - + GPU Video Decoding (Default) GPU 視訊解碼(預設) - + 0.25X (180p/270p) [EXPERIMENTAL] - + 0.5X (360p/540p) [EXPERIMENTAL] 0.5X (360p/540p) [实验性] - + 0.75X (540p/810p) [EXPERIMENTAL] 0.75X (540p/810p) [實驗性] - + 1X (720p/1080p) 1X (720p/1080p) - + 1.25X (900p/1350p) [EXPERIMENTAL] - + 1.5X (1080p/1620p) [EXPERIMENTAL] 1.5X (1080p/1620p) [實驗性] - + 2X (1440p/2160p) 2X (1440p/2160p) - + 3X (2160p/3240p) 3X (2160p/3240p) - + 4X (2880p/4320p) 4X (2880p/4320p) - + 5X (3600p/5400p) 5X (3600p/5400p) - + 6X (4320p/6480p) 6X (4320p/6480p) - + 7X (5040p/7560p) 7X (5040p/7560p) - + 8X (5760p/8640p) 8X (5760p/8640p) - + Nearest Neighbor 最近鄰 - + Bilinear 雙線性 - + Bicubic 雙立方 - + Gaussian 高斯 - + Lanczos - + ScaleForce 強制縮放 - + AMD FidelityFX Super Resolution - + Area - + MMPX - + Zero-Tangent - + B-Spline - + Mitchell - + Spline-1 - - + + Snapdragon Game Super Resolution + + + + + Snapdragon Game Super Resolution EdgeDir + + + + + None - + FXAA FXAA - + SMAA SMAA - + Default (16:9) 預設 (16:9) - + Force 4:3 強制 4:3 - + Force 21:9 強制 21:9 - + Force 16:10 強制 16:10 - + Stretch to Window 延伸視窗 - + Automatic 自動 - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x - + 32x - + 64x - + Japanese (日本語) 日文 (日本語) - + American English 美式英语 - + French (français) 法文 (français) - + German (Deutsch) 德文 (Deutsch) - + Italian (italiano) 義大利文 (italiano) - + Spanish (español) 西班牙文 (español) - + Chinese 中文 - + Korean (한국어) 韓文 (한국어) - + Dutch (Nederlands) 荷蘭文 (Nederlands) - + Portuguese (português) 葡萄牙文 (português) - + Russian (Русский) 俄文 (Русский) - + Taiwanese 台灣中文 - + British English 英式英文 - + Canadian French 加拿大法文 - + Latin American Spanish 拉丁美洲西班牙文 - + Simplified Chinese 簡體中文 - + Traditional Chinese (正體中文) 正體中文 (正體中文) - + Brazilian Portuguese (português do Brasil) 巴西-葡萄牙語 (português do Brasil) - + Polish (polska) - + Thai (แบบไทย) - - + + Japan 日本 - + USA 美國 - + Europe 歐洲 - + Australia 澳洲 - + China 中國 - + Korea 南韓 - + Taiwan 台灣 - + Auto (%1) Auto select time zone 自動 (%1) - + Default (%1) Default time zone 預設 (%1) - + CET 中歐 - + CST6CDT CST6CDT - + Cuba 古巴 - + EET EET - + Egypt 埃及 - + Eire 愛爾蘭 - + EST 北美東部 - + EST5EDT EST5EDT - + GB GB - + GB-Eire 英國-愛爾蘭 - + GMT GMT - + GMT+0 GMT+0 - + GMT-0 GMT-0 - + GMT0 GMT0 - + Greenwich 格林威治 - + Hongkong 香港 - + HST 夏威夷 - + Iceland 冰島 - + Iran 伊朗 - + Israel 以色列 - + Jamaica 牙買加 - + Kwajalein 瓜加林環礁 - + Libya 利比亞 - + MET 中歐 - + MST 北美山區 - + MST7MDT MST7MDT - + Navajo 納瓦霍 - + NZ 紐西蘭 - + NZ-CHAT 紐西蘭-查塔姆群島 - + Poland 波蘭 - + Portugal 葡萄牙 - + PRC 中國 - + PST8PDT 太平洋 - + ROC 臺灣 - + ROK 韓國 - + Singapore 新加坡 - + Turkey 土耳其 - + UCT UCT - + Universal 世界 - + UTC UTC - + W-SU 莫斯科 - + WET 西歐 - + Zulu 協調世界時 - + Mono 單聲道 - + Stereo 立體聲 - + Surround 環繞音效 - + 4GB DRAM (Default) 4GB DRAM (默认) - + 6GB DRAM (Unsafe) 6GB DRAM (不安全) - + 8GB DRAM - + 10GB DRAM (Unsafe) - + 12GB DRAM (Unsafe) - + Docked TV - + Handheld 掌機模式 - - + + Off - + Boost (1700MHz) - + Fast (2000MHz) - + Always ask (Default) 总是询问 (默认) - + Only if game specifies not to stop 仅当游戏不希望停止时 - + Never ask 从不询问 - - + + Medium (256) - - + + High (512) - + Very Small (16 MB) - + Small (32 MB) - + Normal (128 MB) - + Large (256 MB) - + Very Large (512 MB) - + Very Low (4 MB) - + Low (8 MB) - + Normal (16 MB) - + Medium (32 MB) - + High (64 MB) - + Very Low (32) - + Low (64) - + Normal (128) - + Disabled - + ExtendedDynamicState 1 - + ExtendedDynamicState 2 - + ExtendedDynamicState 3 - + Tree View - + Grid View @@ -3284,33 +3315,33 @@ Would you like to delete the old save data? 背景顏色: - + % - FSR sharpening percentage (e.g. 50%) - % + FSR/SGSR sharpening percentage (e.g. 50%) + - + Off 關閉 - + VSync Off 垂直同步關 - + Recommended 推薦 - + On 開啟 - + VSync On 垂直同步開 @@ -4439,7 +4470,7 @@ To invert the axes, first move your joystick vertically, and then horizontally.< - + Configure 設定 @@ -4470,7 +4501,7 @@ To invert the axes, first move your joystick vertically, and then horizontally.< - + Test 測試 @@ -4485,77 +4516,77 @@ To invert the axes, first move your joystick vertically, and then horizontally.< 移除伺服器 - + %1:%2 %1:%2 - - - - - - + + + + + + Eden - + Port number has invalid characters 連線埠中包含無效字元 - + Port has to be in range 0 and 65353 連線埠必須為 0 到 65353 之間 - + IP address is not valid 無效的 IP 位址 - + This UDP server already exists 此 UDP 伺服器已存在 - + Unable to add more than 8 servers 最多只能新增 8 個伺服器 - + Testing 測試中 - + Configuring 設定中 - + Test Successful 測試成功 - + Successfully received data from the server. 已成功從伺服器取得資料 - + Test Failed 測試失敗 - + Could not receive valid data from the server.<br>Please verify that the server is set up correctly and the address and port are correct. 無法從伺服器取得有效的資料。<br>請檢查伺服器是否正確設定以及位址和連接埠是否正確。 - + UDP Test or calibration configuration is in progress.<br>Please wait for them to finish. UDP 測試或觸控校正進行中。<br>請耐心等候。 @@ -4740,57 +4771,57 @@ Current values are %1% and %2% respectively. 某些設定僅在遊戲未執行時才能修改 - + Add-Ons 延伸模組 - + System 系統 - + CPU CPU - + Graphics 圖形 - + Adv. Graphics 進階圖形 - + Ext. Graphics - + Audio 音訊 - + Input Profiles 輸入設定檔 - + Network - + Applets - + Properties 屬性 @@ -5801,7 +5832,7 @@ Drag points to change position, or double-click table cells to edit values. - + Calculating... @@ -6003,50 +6034,50 @@ Please go to Configure -> System -> Network and make a selection. GRenderWindow - - + + OpenGL not available! 無法使用 OpenGL 模式! - + OpenGL shared contexts are not supported. 不支援 OpenGL 共用的上下文。 - + Eden has not been compiled with OpenGL support. - - - + + + Error while initializing OpenGL! 初始化 OpenGL 時發生錯誤! - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. 您的 GPU 可能不支援 OpenGL,或是未安裝最新的圖形驅動程式 - + Error while initializing OpenGL 4.6! 初始化 OpenGL 4.6 時發生錯誤! - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 您的 GPU 可能不支援 OpenGL 4.6,或是未安裝最新的圖形驅動程式<br><br>GL 渲染器:<br>%1 - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 您的 GPU 可能不支援某些必需的 OpenGL 功能。請確保您已安裝最新的圖形驅動程式。<br><br>GL 渲染器:<br>%1<br><br>不支援的功能:<br>%2 - + This build doesn't have OpenGL support. @@ -6054,279 +6085,282 @@ Please go to Configure -> System -> Network and make a selection. GameList - + &Add New Game Directory - + Favorite 我的最愛 - + Start Game 開始遊戲 - + Start Game without Custom Configuration 開始遊戲(不使用額外設定) - + Open Save Data Location 開啟存檔位置 - + Open Mod Data Location 開啟模組位置 - + Open Transferable Pipeline Cache 開啟通用著色器管線快取位置 - + Link to Ryujinx - + Remove 移除 - + Remove Installed Update 移除已安裝的遊戲更新 - + Remove All Installed DLC 移除所有安裝的DLC - + Remove Custom Configuration 移除額外設定 - + Remove Cache Storage 移除快取儲存空間 - + Remove OpenGL Pipeline Cache 刪除 OpenGL 著色器管線快取 - + Remove Vulkan Pipeline Cache 刪除 Vulkan 著色器管線快取 - + Remove All Pipeline Caches 刪除所有著色器管線快取 - + Remove All Installed Contents 移除所有安裝項目 - + Manage Play Time - + Edit Play Time Data - + Remove Play Time Data 清除遊玩時間 - - + + Dump RomFS 傾印 RomFS - + Dump RomFS to SDMC 傾印 RomFS 到 SDMC - + Verify Integrity 完整性驗證 - + Copy Title ID to Clipboard 複製遊戲 ID 到剪貼簿 - + Navigate to GameDB entry 檢視遊戲相容性報告 - + Create Shortcut 建立捷徑 - + Add to Desktop 新增至桌面 - + Add to Applications Menu 新增至應用程式選單 - + Configure Game - + Scan Subfolders 包含子資料夾 - + Remove Game Directory 移除遊戲資料夾 - + ▲ Move Up ▲ 向上移動 - + ▼ Move Down ▼ 向下移動 - + Open Directory Location 開啟資料夾位置 - + Clear 清除 - - - Name - 名稱 - - - - Compatibility - 相容性 - - - - Add-ons - 延伸模組 - - - - File type - 檔案格式 - - - - Size - 大小 - - - - Play time - 遊玩時間 - GameListItemCompat - + Ingame 遊戲內 - + Game starts, but crashes or major glitches prevent it from being completed. 遊戲可以執行,但可能會出現當機或故障導致遊戲無法正常運作。 - + Perfect 完美 - + Game can be played without issues. 遊戲可以毫無問題的遊玩。 - + Playable 可遊玩 - + Game functions with minor graphical or audio glitches and is playable from start to finish. 遊戲自始至終可以正常遊玩,但可能會有一些輕微的圖形或音訊故障。 - + Intro/Menu 開始畫面/選單 - + Game loads, but is unable to progress past the Start Screen. 遊戲可以載入,但無法通過開始畫面。 - + Won't Boot 無法啟動 - + The game crashes when attempting to startup. 啟動遊戲時異常關閉 - + Not Tested 未測試 - + The game has not yet been tested. 此遊戲尚未經過測試 + + GameListModel + + + Name + + + + + Compatibility + + + + + Add-ons + + + + + File type + + + + + Size + + + + + Play time + + + GameListPlaceholder - + Double-click to add a new folder to the game list 連點兩下以新增資料夾至遊戲清單 @@ -6334,17 +6368,17 @@ Please go to Configure -> System -> Network and make a selection. GameListSearchField - + %1 of %n result(s) - + Filter: 搜尋: - + Enter pattern to filter 輸入文字以搜尋 @@ -6838,1139 +6872,1099 @@ Debug Message: - + Game &Icon Size - + Reset Window Size to &720p 重設視窗大小為 &720p - + Reset Window Size to 720p 重設視窗大小為 720p - + Reset Window Size to &900p 重設視窗大小為 &900p - + Reset Window Size to 900p 重設視窗大小為 900p - + Reset Window Size to &1080p 重設視窗大小為 &1080p - + Reset Window Size to 1080p 重設視窗大小為 1080p - + &Multiplayer 多人遊戲 (&M) - + &Tools 工具 (&T) - + Am&iibo - + Launch &Applet - + &TAS TAS (&T) - + &Create Home Menu Shortcut - + Install &Firmware - + &Help 說明 (&H) - + &Install Files to NAND... &安裝檔案至內部儲存空間 - + L&oad File... 開啟檔案(&O)... - + Load &Folder... 開啟資料夾(&F)... - + E&xit 結束(&X) - - + + &Pause 暫停(&P) - + &Stop 停止(&S) - + &Verify Installed Contents 驗證已安裝內容的完整性 (&V) - + &About Eden - + Single &Window Mode 單一視窗模式(&W) - + Con&figure... 設定 (&F) - + Ctrl+, - + Enable Overlay Display Applet - + Show &Filter Bar 顯示搜尋列(&F) - + Show &Status Bar 顯示狀態列(&S) - + Show Status Bar 顯示狀態列 - + &Browse Public Game Lobby 瀏覽公用遊戲大廳 (&B) - + &Create Room 建立房間 (&C) - + &Leave Room 離開房間 (&L) - + &Direct Connect to Room 直接連線到房間 (&D) - + &Show Current Room 顯示目前的房間 (&S) - + F&ullscreen 全螢幕(&U) - + &Restart 重新啟動(&R) - + Load/Remove &Amiibo... 載入/移除 Amiibo... (&A) - + &Report Compatibility 回報相容性(&R) - + Open &Mods Page 模組資訊 (&M) - + Open &Quickstart Guide 快速入門 (&Q) - + &FAQ 常見問題 (&F) - + &Capture Screenshot 截圖 (&C) - + &Album - + &Set Nickname and Owner 登錄持有者和暱稱 (&S) - + &Delete Game Data 清除遊戲資料 (&D) - + &Restore Amiibo 復原資料 (&R) - + &Format Amiibo 初始化 Amiibo (&F) - + &Mii Editor - + &Configure TAS... 設定 &TAS… - + Configure C&urrent Game... 目前遊戲設定...(&U) - - + + &Start 開始(&S) - + &Reset 重設 (&R) - - + + R&ecord 錄製 (&E) - + Open &Controller Menu 打开控制器菜单 (&C) - + Install Decryption &Keys - + &Home Menu - + &Desktop - + &Application Menu - + &Root Data Folder - + &NAND Folder - + &SDMC Folder - + &Mod Folder - + &Log Folder - + From Folder - + From ZIP - + &Eden Dependencies - + &Data Manager - + &Tree View - + &Grid View - + Game Icon Size - - + + None - + Show Game &Name - + Show &Performance Overlay - + + &Carousel View + + + + Small (32x32) - + Standard (64x64) - + Large (128x128) - + Full Size (256x256) - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping - + Loading Web Applet... - - + + Disable Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. - + Unmute - + Mute - + Reset Volume - + &Clear Recent Files - + &Continue - + Warning: Outdated Game Format - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. - - + + Error while loading ROM! - + The ROM format is not supported. - + An error occurred initializing the video core. - + Eden has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-mirror.github.io/help/reference/log-files/'>How to Upload the Log File</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. - + (64-bit) - + (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit - + Closing software... - + Save Data - + Mod Data - + Error Opening %1 Folder - - + + Folder does not exist! - + Remove Installed Game Contents? - + Remove Installed Game Update? - + Remove Installed Game DLC? - + Remove Entry - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? - + Remove Cache Storage? - + Remove File - + Remove Play Time Data - + Reset play time? - - + + RomFS Extraction Failed! - + There was an error copying the RomFS files or the user cancelled the operation. - + Full - + Skeleton - + Select RomFS Dump Mode - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... - - + + Cancel - + RomFS Extraction Succeeded! - + The operation completed successfully. - + Error Opening %1 - + Select Directory - + Properties - + The game properties could not be loaded. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. - + Load File - + Open Extracted ROM Directory - + Invalid Directory Selected - + The directory you have selected does not contain a 'main' file. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files - + %n file(s) remaining - + Installing file "%1"... - - + + Install Results - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application - + System Archive - + System Application Update - + Firmware Package (Type A) - + Firmware Package (Type B) - + Game - + Game Update - + Game DLC - + Delta Title - + Select NCA Install Type... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) - + Failed to Install - + The title type you selected for the NCA is invalid. - + File not found - + File "%1" not found - + OK - + Function Disabled - + Compatibility list reporting is currently disabled. Check back later! - + Error opening URL - + Unable to open the URL "%1". - + TAS Recording - + Overwrite file of player 1? - + Invalid config detected - + Handheld controller can't be used on docked mode. Pro controller will be selected. - - + + Amiibo - - + + The current amiibo has been removed - + Error - - + + The current game is not looking for amiibos - + Amiibo File (%1);; All Files (*.*) - + Load Amiibo - + Error loading Amiibo data - + The selected file is not a valid amiibo - + The selected file is already on use - + An unknown error occurred - - - Keys not installed - - - - - - Install decryption keys and restart Eden before attempting to install firmware. - - - - - Select Dumped Firmware Source Location - - - - - Select Dumped Firmware ZIP - - - - - Zipped Archives (*.zip) - - - - - Firmware cleanup failed - - - - - Failed to clean up extracted firmware cache. -Check write permissions in the system temp directory and try again. -OS reported error: %1 - - - - + No firmware available - + Firmware Corrupted - + Unknown applet - + Applet doesn't map to a known value. - + Record not found - + Applet not found. Please reinstall firmware. - + Capture Screenshot - + PNG Image (*.png) - + TAS state: Running %1/%2 - + TAS state: Recording %1 - + TAS state: Idle %1/%2 - + TAS State: Invalid - + &Stop Running - + Stop R&ecording - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor - + Speed: %1% / %2% - + Speed: %1% - + Game: %1 FPS - + Frame: %1 ms - - - FSR - - - - + NO AA - + VOLUME: MUTE - + VOLUME: %1% Volume percentage (e.g. 50%) - + Derivation Components Missing - + Decryption keys are missing. Install them now? - + Wayland Detected! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -7978,74 +7972,74 @@ Would you like to force it for future launches? - + Use X11 - + Continue with Wayland - + Don't show again - + Restart Required - + Restart Eden to apply the X11 backend. - + Slow - + Turbo - + Unlocked - + Select RomFS Dump Target - + Please select which RomFS you would like to dump. - + Are you sure you want to close Eden? - - - + + + Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? @@ -8111,6 +8105,11 @@ Would you like to bypass this and exit anyway? ScaleForce + + + FSR + + Area @@ -8122,52 +8121,62 @@ Would you like to bypass this and exit anyway? - + + SGSR + + + + + SGSR EdgeDir + + + + Docked - + Handheld - + Fast - + Balanced - + Accurate - + Vulkan - + OpenGL GLSL - + OpenGL SPIRV - + OpenGL GLASM - + Null @@ -8200,7 +8209,7 @@ If you wish to clean up the files which were left in the old data location, you - + Data was migrated successfully. @@ -9055,47 +9064,47 @@ p, li { white-space: pre-wrap; } %1 正在玩 %2 - + Play Time: %1 - + Never Played - + Version: %1 - + Version: 1.0.0 - + Installed SD Titles 安裝在 SD 卡中的遊戲 - + Installed NAND Titles 安裝在內部儲存空間中的遊戲 - + System Titles 系統項目 - + Add New Game Directory 加入遊戲資料夾 - + Favorites 我的最愛 @@ -9216,253 +9225,286 @@ p, li { white-space: pre-wrap; } QtCommon::Content - + Game Requires Firmware - + The game you are trying to launch requires firmware to boot or to get past the opening menu. Please <a href='https://yuzu-mirror.github.io/help/quickstart'>dump and install firmware</a>, or press "OK" to launch anyways. - + Installing Firmware... - - - - - + + + + + Cancel - + Firmware Install Failed - + Firmware Install Succeeded - + Firmware integrity verification failed! - - + + Verification failed for the following files: %1 - - + + Verifying integrity... + + + + Integrity verification succeeded! + + - Integrity verification succeeded! - - - - - The operation completed successfully. - - + + Integrity verification failed! - + File contents may be corrupt or missing. - + Integrity verification couldn't be performed - + Firmware installation cancelled, firmware may be in a bad state or corrupted. File contents could not be checked for validity. - + Select Dumped Keys Location - + Decryption Keys install succeeded - + Decryption Keys install failed - + Orphaned Profiles Detected! - + UNEXPECTED BAD THINGS MAY HAPPEN IF YOU DON'T READ THIS!<br>Eden has detected the following save directories with no attached profile:<br>%1<br><br>The following profiles are valid:<br>%2<br><br>Click "OK" to open your save folder and fix up your profiles.<br>Hint: copy the contents of the largest or last-modified folder elsewhere, delete all orphaned profiles, and move your copied contents to the good profile.<br><br>Still confused? See the <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/Orphaned.md'>help page</a>.<br> - + Really clear data? - + Important data may be lost! - + Are you REALLY sure? - + Once deleted, your data will NOT come back! Only do this if you're 100% sure you want to delete this data. - + Clearing... - + Select Export Location - + %1.zip - - + + + Zipped Archives (*.zip) - + Exporting data. This may take a while... - + Exporting - + Exported Successfully - + Data was exported successfully. - + Export Cancelled - + Export was cancelled by the user. - + Export Failed - + Ensure you have write permissions on the targeted directory and try again. - + Select Import Location - + Import Warning - + All previous data in this directory will be deleted. Are you sure you wish to proceed? - + Importing data. This may take a while... - + Importing - + Imported Successfully - + Data was imported successfully. - + Import Cancelled - + Import was cancelled by the user. - + Import Failed - + Ensure you have read permissions on the targeted directory and try again. + + + Keys not installed + + + + + Install decryption keys and restart Eden before attempting to install firmware. + + + + + Select Dumped Firmware Source Location + + + + + Select Dumped Firmware ZIP + + + + + Firmware cleanup failed + + + + + Failed to clean up extracted firmware cache. +Check write permissions in the system temp directory and try again. +OS reported error: %1 + + QtCommon::FS @@ -9706,72 +9748,72 @@ Would you like to manually select a portable folder to use? - + Create Shortcut - + Do you want to launch the game in fullscreen? - + Shortcut Created - + Successfully created a shortcut to %1 - + Shortcut may be Volatile! - + This will create a shortcut to the current AppImage. This may not work well if you update. Continue? - + Failed to Create Shortcut - + Failed to create a shortcut to %1 - + Create Icon - + Cannot create icon file. Path "%1" does not exist and cannot be created. - + No firmware available - + Please install firmware to use the home menu. - + Home Menu Applet - + Home Menu is not available. Please reinstall firmware. @@ -9779,55 +9821,55 @@ Would you like to manually select a portable folder to use? QtCommon::Mod - + Mod Name - + What should this mod be called? - + RomFS - + ExeFS/Patch - + Cheat - + Mod Type - + Could not detect mod type automatically. Please manually specify the type of mod you downloaded. Most mods are RomFS mods, but patches (.pchtxt) are typically ExeFS mods. - - + + Mod Extract Failed - + Failed to create temporary directory %1 - + Zip file %1 is empty @@ -10521,65 +10563,65 @@ By selecting "From Eden", previous save data stored in Ryujinx will be - + New Version Location - + All Files (*.*) - - - + + + Failed to save file - + Could not open file %1 for writing. - + Downloading... - + Cancel - + Could not write to file %1. - + Could not commit to file %1. - + Failed to download file - + Could not download from %1%2 Error code: %3 - + Download Complete - + Successfully downloaded %1. Would you like to open it? diff --git a/dist/qt_themes/default/icons/256x256/eden.png b/dist/qt_themes/default/icons/256x256/eden.png index d0b02580df..3c4bd566a1 100644 Binary files a/dist/qt_themes/default/icons/256x256/eden.png and b/dist/qt_themes/default/icons/256x256/eden.png differ diff --git a/docs/Build.md b/docs/Build.md index 394fe4871d..ec99b20558 100644 --- a/docs/Build.md +++ b/docs/Build.md @@ -136,6 +136,16 @@ cmake -S . -B build -G "" -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COM +#### Option D: Visual Studio with clang-cl + +
+1. Install `"x64 Native Tools Command Prompt"` for VS from the installer and also install `cmake-gui`. +2. Open `"x64 Native Tools Command Prompt"` and type `cmake-gui`. +3. Click configure choose ninja generator > specify native compilers. +4. Put `"C:/Program Files/Microsoft Visual Studio/18/Community/VC/Tools/Llvm/x64/bin/clang-cl.exe"` as your C/C++ compiler path. +5. Open `Visual studio > Open project` or Solution > Change to search for the CMake project file (`CMakeList.txt`) file on the cloned directory, and then build. +
+ ## Troubleshooting If your initial configure failed: diff --git a/docs/CPMUtil.md b/docs/CPMUtil.md new file mode 100644 index 0000000000..52aa4aa722 --- /dev/null +++ b/docs/CPMUtil.md @@ -0,0 +1,320 @@ +# CPMUtil + +CPMUtil is a wrapper around CPM that aims to reduce boilerplate and add useful utility functions to make dependency management a piece of cake. + +- [CPMUtil](#cpmutil) + - [Global Options](#global-options) + - [About](#about) + - [Common Properties](#common-properties) + - [Standard Packages](#standard-packages) + - [Versioning](#versioning) + - [Patches](#patches) + - [Pre-built CI Packages](#pre-built-ci-packages) + - [Usage](#usage) + - [Addendum: Cache Storage](#addendum-cache-storage) + - [Addendum: Making Patches](#addendum-making-patches) + - [Addendum: Package Identification Lists](#addendum-package-identification-lists) + - [Addendum: Notes for Packagers](#addendum-notes-for-packagers) + - [Network Sandbox](#network-sandbox) + - [Unsandboxed](#unsandboxed) + - [Addendum: Dependent Packages](#addendum-dependent-packages) + - [Example: Vulkan](#example-vulkan) + - [Addendum: Module Path Packages](#addendum-module-path-packages) + - [Example: OpenSSL](#example-openssl) + - [Addendum: Adding Qt](#addendum-adding-qt) + +## Global Options + +- `CPMUTIL_FORCE_SYSTEM` (default `OFF`): Require all CPM dependencies to use system packages. NOT RECOMMENDED! + - You may optionally override this (section) +- `CPMUTIL_FORCE_BUNDLED` (default `ON` on MSVC and Android, `OFF` elsewhere): Require all CPM dependencies to use bundled packages. +- `CPMUTIL_PATCH_DIR` (default `${PROJECT_SOURCE_DIR}/.patch`): Path to patches used in packages. Stored as `/json-package-name/0001-patch-name.patch`, etc. +- `CPM_SOURCE_CACHE` (default `${PROJECT_SOURCE_DIR}/.cache/cpm`): Where downloaded dependencies get stored. + +## About + +CPMUtil works by defining dependencies in a JSON file, `cpmfile.json`, and calling `AddJsonPackage`. These dependencies generally must define, at minimum: + +- The repository and Git host +- A release artifact, commit, or tag archive to download +- A SHA512 sum for the downloaded artifact + +And may optionally define other properties like: + +- The minimum version for system packages +- The package name used for system packages (this defaults to the json key if undefined) +- In-tree source patches +- Options passed to CMake +- Options passed to find_package + +For instance: + +```json +"fmt": { + "repo": "fmtlib/fmt", + "tag": "12.1.0", + "hash": "f0da82c545b01692e9fd30fdfb613dbb8dd9716983dcd0ff19ac2a8d36f74beb5540ef38072fdecc1e34191b3682a8542ecbf3a61ef287dbba0a2679d4e023f2", + "min_version": "8", + "options": [ + "FMT_TEST ON" + ], + "patches": [ + "0001-disable-reference-copy.patch" + ] +} +``` + +Calling `AddJsonPackage(fmt)`: + +- Searches for a system package named `fmt` of version 8 or higher +- If found, uses the system package and caches it for future use +- If not found: + - Downloads fmt 12.1.0 from the GitHub Archive into `.cache/cpm/fmt/12.1.0` + - Verifies the hash + - Applies the `0001-disable-reference-copy.patch` patch to the source tree + - Sets `FMT_TEST` to `ON` + - Adds the downloaded directory to CMake + - Now, future `find_package(fmt)` calls will use the downloaded package + +There are two types of packages CPMUtil can define: standard and prebuilt CI packages. Some properties are common to both types, however. + +## Common Properties + +These JSON properties are used by standard and CI packages alike. + +- `package`: The package name used by `find_package` to check for the existence of a system package. + - If unset, defaults to the JSON key +- `repo`: The Git repository the package is stored in, if applicable. +- `version`: The version of the package to download. This is required. +- `min_version`: The minimum required version of the package, if a system package is desired. +- `git_host`: The Git host the package is stored in, if applicable. Defaults to `github.com`. + +## Standard Packages + +Normal packages, like the prior `fmt` example, *must* also define: + +- `hash`: The SHA512 hash of the downloaded artifact. CPMUtil generally computes this for you. +- A valid version/URL identifier: + - `url`: Download from a raw URL. + - `sha`: A short or fully-qualified Git commit sha. CPMUtil recommends using 10-character wide shas. + - `tag`: A Git tag. See [Versioning](#versioning) for its relation to `version`. + - `artifact`: A GitHub/Forgejo/Gitea release artifact (requires `tag`). See [Versioning](#versioning) for its relation to `tag` and `version`. + +The following are optional to define: + +- `source_subdir`: A subdirectory containing the `CMakeLists.txt` to configure a project. Useful for projects like `zstd`. +- `bundled`: Force the usage of a bundled package. Useful for packages where the system package is broken or nonexistent; e.g. including external fragment shaders. +- `find_args`: Additional arguments passed to `find_package`, e.g. `MODULE` +- `patches`: Array of in-tree patches to apply to the downloaded source code. See [#Patches](TODO). +- `options`: Array of CMake options to apply before configuring the package, e.g. `"FMT_TEST ON"`. + +### Versioning + +When using tags or artifacts, it may be cumbersome to repeat the version multiple times; especially if it's constantly changing. For this purpose, `tag` and `artifact` both support basic version text replacement. + +`tag` can use `%VERSION%` to have its version replaced with the `version` defined for the package, e.g. for OpenSSL; when downloading, `tag` will evaluate to `openssl-3.6.2`: + +```json +"openssl": { + "repo": "openssl/openssl", + "version": "3.6.2", + "tag": "openssl-%VERSION%" +} +``` + +`artifact` also supports `%VERSION%` replacement, and can also use `%TAG%` to be replaced by the computed tag. Take this Boost definition: + +```json +"boost": { + "repo": "boostorg/boost", + "tag": "boost-%VERSION%", + "version": "1.90.0" +} +``` + +Boost's artifact for this version is stored in `boost-1.90.0-cmake.tar.xz`. Notice that the computed tag,`boost-1.90.0`, is in the name of the artifact! Thus, `artifact` can be either: + +- `boost-%VERSION%-cmake.tar.xz` +- Or, even simpler: `%TAG%-cmake.tar.xz` + +Future updates need only change the `version` identifier, and the artifact and tag will automatically be updated! + +### Patches + +CPMUtil is able to apply in-place source tree patches to downloaded packages. These are defined in JSON as an array of names, preferably using `git-format-patch`'s scheme of `<4 digit number>-patch-name.patch`. These are stored in `/` (remember that `CPMUTIL_PATCH_DIR` defaults to `$ROOT/.patch`); e.g. `boost` patches would be in `.patch/boost`. Let's say we've made three patches and want to add them; in the Boost JSON definition, we would add: + +```json +"patches": [ + "0001-fix-clang-cl-compilation.patch", + "0002-fix-msvc-arm64-compilation.patch", + "0003-fix-bsd-linking.patch" +] +``` + +Then, when Boost is downloaded, it will apply these patches in order to the source tree. + +To learn how to make patches, see [Addendum: Making Patches](#addendum-making-patches). + +## Pre-built CI Packages + +The definition and usage of CI packages is subject to change in the very near future. + +CI packages are, in essence, prebuilt binary distributions for libraries. They exist for a few reasons: + +- Creating static libraries for system packages that normally lack them, e.g. Qt/SDL +- Reducing duplicated compilation effort on rarely-changing externals, e.g. SDL +- Creating debloated prebuilt packages specifically for your project to reduce binary size, e.g. FFmpeg + +CPMUtil is specifically designed to work with a small subset of prebuilt CI packages; namely, those that follow the format of the [crueter-ci spec](https://github.com/crueter-ci/spec/blob/master/README.md). + +To use them, you must add `ci: true` to your package definition. Alongside the common properties, CI packages define the following: + +- `name`: The artifacts' name prefix (required), e.g. `openssl` +- `extension`: The artifacts' extension (default `tar.zst`) +- `disabled_platforms`: CPMUtil-supported platforms that are not built into this repository. + - This is subject to change. + +**Note that `package` is subject to removal here.** + +Example: + +```json +"sdl2": { + "repo": "crueter-ci/SDL2", + "package": "SDL2", + "min_version": "2.26.4", + "ci": true, + "version": "2.32.10-a65111bd2d", + "artifact": "SDL2" +}, +``` + +## Usage + +Once you've defined your package in `cpmfile.json`, simply call `AddJsonPackage()` and go from there! Specific instructions differ between individual packages, so you're on your own from here. + +If you're only concerned with basic usage, you can stop reading. For more advanced use cases and package management, read these addenda. + +## Addendum: Cache Storage + +CPMUtil stores downloaded packages within `.cache/cpm` by default (see `CPM_SOURCE_CACHE`). Subdirectories stored within are lowercase representations of the `find_package` name for the package; for instance, a `vulkan-headers` definition with `package: "VulkanHeaders"` would be stored in `.cache/cpm/vulkanheaders`. + +Within these subdirectories, additional directories are created for each individual version: + +- A four-character shorthand of `sha`, if defined +- If `sha` is not defined, the fully qualified `version` is used + +CI packages use `--` unconditionally. + +## Addendum: Making Patches + +CPMUtil has a dedicated command for making patches. You're recommended to have Git and a command line editor installed, but CPMUtil is able to work without either. To do so, follow these steps, noting your package's JSON key: + +- Clean-fetch your package: `tools/cpmutil.sh package reset ` +- Make any necessary modifications to the package source. + - You can access the package source directory via `tools/cpmutil.sh package dir `. +- Create the patch: `tools/cpmutil.sh package patch ` + - Follow the on-screen prompts. If you have Git installed, an editor will be opened so you can type your commit message. If not, just type a one-line description. + +And you're done! CPMUtil will automatically create and name the patch, and add it to the list of patches in the JSON definition. + +## Addendum: Package Identification Lists + +CPMUtil will create three lists of dependencies where `AddPackage` or similar was used. Each is in order of addition. + +- `CPM_PACKAGE_NAMES`: The names of packages included by CPMUtil +- `CPM_PACKAGE_URLS`: The URLs to project/repo pages of packages +- `CPM_PACKAGE_SHAS`: Short version identifiers for each package + - If the package was included as a system package, `(system)` is appended thereafter + - Packages whose versions can't be deduced will be left as `unknown`. + +For an example of how this might be implemented in an application, see Eden's implementation: + +- [`dep_hashes.h.in`](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/src/dep_hashes.h.in) +- [`GenerateDepHashes.cmake`](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CMakeModules/GenerateDepHashes.cmake) +- [`deps_dialog.cpp`](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/src/yuzu/deps_dialog.cpp) + +## Addendum: Notes for Packagers + +If you are packaging a project that uses CPMUtil, read this! + +### Network Sandbox + +For sandboxed environments (e.g. Gentoo, nixOS) you must install all dependencies to the system beforehand and set `-DCPMUTIL_FORCE_SYSTEM=ON`. If a dependency is missing, get creating! + +Alternatively, if CPMUtil pulls in a package that has no suitable way to install or use a system version, download it separately and pass `-DPackageName_DIR=/path/to/downloaded/dir` (e.g. shaders) + +### Unsandboxed + +For others (AUR, MPR, etc). CPMUtil will handle everything for you, including if some of the project's dependencies are missing from your distribution's repositories. See [`eden-git`](https://aur.archlinux.org/cgit/aur.git/tree/PKGBUILD?h=eden-git) for an example. + +## Addendum: Dependent Packages + +Consider the following scenario: the Vulkan Headers and Vulkan Utility libraries are both pulled in by my project. In order for both to compile cleanly, their versions *must* match. However, a user may have the Vulkan Headers installed, but *not* the Vulkan Utility Libraries! This can cause a version mismatch where the Utility Libraries expect a much newer version of the Vulkan Headers than the user has installed. + +To solve this, CPMUtil has an `AddDependentPackages` command. This takes a list of JSON package keys that *must* either ALL be installed to the system, or ALL be bundled. + +### Example: Vulkan + +Using the prior Vulkan example: + +```json +"vulkan-headers": { + "repo": "KhronosGroup/Vulkan-Headers", + "package": "VulkanHeaders", + "min_version": "1.4.317", + "version": "1.4.342", + "tag": "v%VERSION%" +}, +"vulkan-utility-libraries": { + "repo": "KhronosGroup/Vulkan-Utility-Libraries", + "package": "VulkanUtilityLibraries", + "version": "1.4.342", + "tag": "v%VERSION%" +} +``` + +In CMake: + +```cmake +AddDependentPackages(vulkan-headers vulkan-utility-libraries) +``` + +Possible scenarios: + +- The user has both Vulkan Headers and Vulkan Utility Libraries installed to the system, and both are new enough. + - Configuration proceeds without issue. +- The user has neither installed, or has a too-old version of Vulkan Headers installed + - Configuration proceeds without issue. +- The user has a valid Vulkan Headers installed, but not Vulkan Utility Libraries. + - CPMUtil instructs the user to either force bundled Vulkan Headers, or install Vulkan Utility Libraries. +- The user has both installed, but Vulkan Headers are too old. + - CPMUtil instructs the user to install a valid version of Vulkan Headers, or force bundled Vulkan Utility Libraries. + +## Addendum: Module Path Packages + +Sometimes, a prebuilt CI package may be packed in such a way that it's meant to be used in the context of a system install (e.g. pkgconfig or CMakeConfig files). In this case, CPMUtil normally will be unable to configure the downloaded subdirectory. To solve this, you can use `AddJsonPackage`'s `MODULE_PATH` mode, which adds the downloaded source directory to the `CMAKE_MODULE_PATH`. + +### Example: OpenSSL + +Say an OpenSSL CI package is packed to contain its CMake config files rather than a root `CMakeLists.txt`; in this case, you would call: + +```cmake +AddJsonPackage(NAME openssl MODULE_PATH) +``` + +The `NAME` argument is also required, as the parsing is different from the standard single-argument function signature. + +From here, calling `find_package(OpenSSL)` will use the bundled OpenSSL. + +## Addendum: Adding Qt + +If you'd like to use customized Qt builds, CPMUtil provides a convenience function that allows you to add Qt builds. This usage and setup is subject to change. + +See [crueter-ci/Qt](https://github.com/crueter-ci/Qt) for an example of how one might build customized Qt. To add a Qt build to your project, use `AddQt( )`, e.g.: + +```cmake +AddQt(QDash-CI/Qt 6.11.1) +``` + +Then, call `find_package(Qt6 ...)` and it will pull Qt from your downloaded source. diff --git a/docs/CPMUtil/AddCIPackage.md b/docs/CPMUtil/AddCIPackage.md deleted file mode 100644 index bc7c1ccfad..0000000000 --- a/docs/CPMUtil/AddCIPackage.md +++ /dev/null @@ -1,20 +0,0 @@ -# AddPackage - -- `VERSION` (required): The version to get (the tag will be `v${VERSION}`) -- `NAME` (required): Name used within the artifacts -- `REPO` (required): CI repository, e.g. `crueter-ci/OpenSSL` -- `PACKAGE` (required): `find_package` package name -- `EXTENSION`: Artifact extension (default `tar.zst`) -- `MIN_VERSION`: Minimum version for `find_package`. Only used if platform does not support this package as a bundled artifact -- `DISABLED_PLATFORMS`: List of platforms that lack artifacts for this package. Options: - - `windows-amd64` - - `windows-arm64` - - `mingw-amd64` - - `mingw-arm64` - - `android-x86_64` - - `android-aarch64` - - `solaris-amd64` - - `freebsd-amd64` - - `linux-amd64` - - `linux-aarch64` - - `macos-universal` diff --git a/docs/CPMUtil/AddDependentPackage.md b/docs/CPMUtil/AddDependentPackage.md deleted file mode 100644 index bb6651e4b8..0000000000 --- a/docs/CPMUtil/AddDependentPackage.md +++ /dev/null @@ -1,41 +0,0 @@ -# AddDependentPackage - -Use `AddDependentPackage` when you have multiple packages that are required to all be from the system, OR bundled. This is useful in cases where e.g. versions must absolutely match. - -## Versioning - -Versioning must be handled by the package itself. - -## Examples - -### Vulkan - -`cpmfile.json` - -```json -{ - "vulkan-headers": { - "repo": "KhronosGroup/Vulkan-Headers", - "package": "VulkanHeaders", - "version": "1.4.317", - "hash": "26e0ad8fa34ab65a91ca62ddc54cc4410d209a94f64f2817dcdb8061dc621539a4262eab6387e9b9aa421db3dbf2cf8e2a4b041b696d0d03746bae1f25191272", - "git_version": "1.4.342", - "tag": "v%VERSION%" - }, - "vulkan-utility-libraries": { - "repo": "KhronosGroup/Vulkan-Utility-Libraries", - "package": "VulkanUtilityLibraries", - "hash": "8147370f964fd82c315d6bb89adeda30186098427bf3efaa641d36282d42a263f31e96e4586bfd7ae0410ff015379c19aa4512ba160630444d3d8553afd1ec14", - "git_version": "1.4.342", - "tag": "v%VERSION%" - } -} -``` - -`CMakeLists.txt`: - -```cmake -AddDependentPackages(vulkan-headers vulkan-utility-libraries) -``` - -If Vulkan Headers are installed, but NOT Vulkan Utility Libraries, then CPMUtil will throw an error. diff --git a/docs/CPMUtil/AddJsonPackage.md b/docs/CPMUtil/AddJsonPackage.md deleted file mode 100644 index 464cd1731b..0000000000 --- a/docs/CPMUtil/AddJsonPackage.md +++ /dev/null @@ -1,104 +0,0 @@ -# AddJsonPackage - -In each directory that utilizes `CPMUtil`, there must be a `cpmfile.json` that defines dependencies in a similar manner to the individual calls. - -The cpmfile is an object of objects, with each sub-object being named according to the package's identifier, e.g. `openssl`, which can then be fetched with `AddJsonPackage()`. Options are designed to map closely to the argument names, and are always strings unless otherwise specified. - -- [Options](#options) -- [Examples](#examples) - - -## Options - -- `package` -> `NAME` (`PACKAGE` for CI), defaults to the object key -- `repo` -> `REPO` -- `version` -> `VERSION` -- `ci` (bool) - -If `ci` is `false`: - -- `hash` -> `HASH` -- `hash_suffix` -> `HASH_SUFFIX` -- `sha` -> `SHA` -- `key` -> `KEY` -- `tag` -> `TAG` - - If the tag contains `%VERSION%`, that part will be replaced by the `git_version`, OR `version` if `git_version` is not specified -- `url` -> `URL` -- `artifact` -> `ARTIFACT` - - If the artifact contains `%VERSION%`, that part will be replaced by the `git_version`, OR `version` if `git_version` is not specified - - If the artifact contains `%TAG%`, that part will be replaced by the `tag` (with its replacement already done) -- `git_version` -> `GIT_VERSION` -- `git_host` -> `GIT_HOST` -- `source_subdir` -> `SOURCE_SUBDIR` -- `bundled` -> `BUNDLED_PACKAGE` -- `find_args` -> `FIND_PACKAGE_ARGUMENTS` -- `download_only` -> `DOWNLOAD_ONLY` -- `patches` -> `PATCHES` (array) -- `options` -> `OPTIONS` (array) -- `skip_updates`: Tells `check-updates.sh` to not check for new updates on this package. - -Other arguments aren't currently supported. If you wish to add them, see the `AddJsonPackage` function in `CMakeModules/CPMUtil.cmake`. - -If `ci` is `true`: - -- `name` -> `NAME`, defaults to the object key -- `extension` -> `EXTENSION`, defaults to `tar.zst` -- `min_version` -> `MIN_VERSION` -- `extension` -> `EXTENSION` -- `disabled_platforms` -> `DISABLED_PLATFORMS` (array) - -## Examples - -In order: OpenSSL CI, Boost (tag + artifact), Opus (options + find_args), discord-rpc (sha + options + patches). - -```json -{ - "openssl": { - "ci": true, - "package": "OpenSSL", - "name": "openssl", - "repo": "crueter-ci/OpenSSL", - "version": "3.6.0", - "min_version": "1.1.1", - "disabled_platforms": [ - "macos-universal" - ] - }, - "boost": { - "package": "Boost", - "repo": "boostorg/boost", - "tag": "boost-%VERSION%", - "artifact": "%TAG%-cmake.7z", - "hash": "e5b049e5b61964480ca816395f63f95621e66cb9bcf616a8b10e441e0e69f129e22443acb11e77bc1e8170f8e4171b9b7719891efc43699782bfcd4b3a365f01", - "git_version": "1.88.0", - "version": "1.57" - }, - "opus": { - "package": "Opus", - "repo": "xiph/opus", - "sha": "5ded705cf4", - "hash": "0dc89e58ddda1f3bc6a7037963994770c5806c10e66f5cc55c59286fc76d0544fe4eca7626772b888fd719f434bc8a92f792bdb350c807968b2ac14cfc04b203", - "version": "1.3", - "find_args": "MODULE", - "options": [ - "OPUS_BUILD_TESTING OFF", - "OPUS_BUILD_PROGRAMS OFF", - "OPUS_INSTALL_PKG_CONFIG_MODULE OFF", - "OPUS_INSTALL_CMAKE_CONFIG_MODULE OFF" - ] - }, - "discord-rpc": { - "repo": "discord/discord-rpc", - "sha": "963aa9f3e5", - "hash": "386e1344e9a666d730f2d335ee3aef1fd05b1039febefd51aa751b705009cc764411397f3ca08dffd46205c72f75b235c870c737b2091a4ed0c3b061f5919bde", - "options": [ - "BUILD_EXAMPLES OFF" - ], - "patches": [ - "0001-cmake-version.patch", - "0002-no-clang-format.patch", - "0003-fix-cpp17.patch" - ] - } -} -``` diff --git a/docs/CPMUtil/AddPackage.md b/docs/CPMUtil/AddPackage.md deleted file mode 100644 index 6e9ae1b775..0000000000 --- a/docs/CPMUtil/AddPackage.md +++ /dev/null @@ -1,118 +0,0 @@ -# `AddPackage` - - -- [Identification/Fetching](#identificationfetching) -- [Hashing](#hashing) -- [Other Options](#other-options) -- [Extra Variables](#extra-variables) -- [System/Bundled Packages](#systembundled-packages) -- [Identification](#identification) - - -## Identification/Fetching - -- `NAME` (required): The package name (must be the same as the `find_package` name if applicable) -- `VERSION`: The minimum version of this package that can be used on the system -- `GIT_VERSION`: The "version" found within git -- `URL`: The URL to fetch. -- `REPO`: The repo to use (`owner/repo`). -- `GIT_HOST`: The Git host to use - - Defaults to `github.com`. Do not include the protocol, as HTTPS is enforced. -- `TAG`: The tag to fetch, if applicable. -- `ARTIFACT`: The name of the artifact, if applicable. -- `SHA`: Commit sha to fetch, if applicable. -- `BRANCH`: Branch to fetch, if applicable. - -The following configurations are supported, in descending order of precedence: - -- `URL`: Bare URL download, useful for custom artifacts - - If this is set, `GIT_URL` or `REPO` should be set to allow the dependency viewer to link to the project's Git repository. - - If this is NOT set, `REPO` must be defined. -- `REPO + TAG + ARTIFACT`: GitHub release artifact - - The final download URL will be `https://github.com/${REPO}/releases/download/${TAG}/${ARTIFACT}` - - Useful for prebuilt libraries and prefetched archives -- `REPO + TAG`: GitHub tag archive - - The final download URL will be `https://github.com/${REPO}/archive/refs/tags/${TAG}.tar.gz` - - Useful for pinning to a specific tag, better for build identification -- `REPO + SHA`: GitHub commit archive - - The final download URL will be `https://github.com/${REPO}/archive/${SHA}.zip` - - Useful for pinning to a specific commit -- `REPO + BRANCH`: GitHub branch archive - - The final download URL will be `https://github.com/${REPO}/archive/refs/heads/${BRANCH}.zip` - - Generally not recommended unless the branch is frozen -- `REPO`: GitHub master archive - - The final download URL will be `https://github.com/${REPO}/archive/refs/heads/master.zip` - - Generally not recommended unless the project is dead - -## Hashing - -Hashing is used for verifying downloads. It's highly recommended to use these. - -- `HASH_ALGO` (default `SHA512`): Hash algorithm to use - -Hashing strategies, descending order of precedence: - -- `HASH`: Bare hash verification, useful for static downloads e.g. commit archives -- `HASH_SUFFIX`: Download the hash as `${DOWNLOAD_URL}.${HASH_SUFFIX}` - - The downloaded hash *must* match the hash algorithm and contain nothing but the hash; no filenames or extra content. -- `HASH_URL`: Download the hash from a separate URL - -## Other Options - -- `KEY`: Custom cache key to use (stored as `.cache/cpm/${packagename_lower}/${key}`) - - Default is based on, in descending order of precedence: - - First 4 characters of the sha - - `GIT_VERSION` - - Tag - - `VERSION` - - Otherwise, CPM defaults will be used. This is not recommended as it doesn't produce reproducible caches -- `DOWNLOAD_ONLY`: Whether or not to configure the downloaded package via CMake - - Useful to turn `OFF` if the project doesn't use CMake -- `SOURCE_SUBDIR`: Subdirectory of the project containing a CMakeLists.txt file -- `FIND_PACKAGE_ARGUMENTS`: Arguments to pass to the `find_package` call -- `BUNDLED_PACKAGE`: Set to `ON` to default to the bundled package -- `FORCE_BUNDLED_PACKAGE`: Set to `ON` to force the usage of the bundled package, regardless of CPMUTIL_FORCE_SYSTEM or `_FORCE_SYSTEM` -- `OPTIONS`: Options to pass to the configuration of the package -- `PATCHES`: Patches to apply to the package, stored in `.patch/${packagename_lower}/0001-patch-name.patch` and so on -- Other arguments can be passed to CPM as well - -## Extra Variables - -For each added package, users may additionally force usage of the system/bundled package. - -- `${package}_DIR`: Path to a separately-downloaded copy of the package. Note that versioning is not checked! -- `${package}_FORCE_SYSTEM`: Require the package to be installed on the system -- `${package}_FORCE_BUNDLED`: Force the package to be fetched and use the bundled version - -## System/Bundled Packages - -Descending order of precedence: - -- If `${package}_FORCE_SYSTEM` is true, requires the package to be on the system -- If `${package}_FORCE_BUNDLED` is true, forcefully uses the bundled package -- If `CPMUTIL_FORCE_SYSTEM` is true, requires the package to be on the system -- If `CPMUTIL_FORCE_BUNDLED` is true, forcefully uses the bundled package -- If the `BUNDLED_PACKAGE` argument is true, forcefully uses the bundled package -- Otherwise, CPM will search for the package first, and if not found, will use the bundled package - -## Identification - -All dependencies must be identifiable in some way for usage in the dependency viewer. Lists are provided in descending order of precedence. - -URLs: - -- `GIT_URL` -- `REPO` as a Git repository - - You may optionally specify `GIT_HOST` to use a custom host, e.g. `GIT_HOST git.crueter.xyz`. Note that the git host MUST be GitHub-like in its artifact/archive downloads, e.g. Forgejo - - If `GIT_HOST` is unspecified, defaults to `github.com` -- `URL` - -Versions (bundled): - -- `SHA` -- `GIT_VERSION` -- `VERSION` -- `TAG` -- "unknown" - -If the package is a system package, AddPackage will attempt to determine the package version and append `(system)` to the identifier. Otherwise, it will be marked as `unknown (system)` diff --git a/docs/CPMUtil/AddQt.md b/docs/CPMUtil/AddQt.md deleted file mode 100644 index e9595b8004..0000000000 --- a/docs/CPMUtil/AddQt.md +++ /dev/null @@ -1,28 +0,0 @@ -# AddQt - -Simply call `AddQt()` before any Qt `find_package` calls and everything will be set up for you. On Linux, the bundled Qt library is built as a shared library, and provided you have OpenSSL and X11, everything should just work. - -On Windows, MinGW, and MacOS, Qt is bundled as a static library. No further action is needed, as the provided libraries automatically integrate the Windows/Cocoa plugins, alongside the corresponding Multimedia and Network plugins. - -## Modules - -The following modules are bundled into these Qt builds: - -- Base (Gui, Core, Widgets, Network) -- Multimedia -- Declarative (Quick, QML) -- Linux: Wayland client - -Each platform has the corresponding QPA built in and set as the default as well. This means you don't need to add `Q_IMPORT_PLUGIN`! - -## Example - -See an example in the [`tests/qt`](https://git.crueter.xyz/CMake/CPMUtil/src/branch/master/tests/qt/CMakeLists.txt) directory. - -## Versions - -The following versions have available builds: - -- 6.9.3 - -See [`crueter-ci/Qt`](https://github.com/crueter-ci/Qt) for an updated list at any time. diff --git a/docs/CPMUtil/README.md b/docs/CPMUtil/README.md deleted file mode 100644 index dbc5f0922a..0000000000 --- a/docs/CPMUtil/README.md +++ /dev/null @@ -1,70 +0,0 @@ -# CPMUtil - -CPMUtil is a wrapper around CPM that aims to reduce boilerplate and add useful utility functions to make dependency management a piece of cake. - -Global Options: - -- `CPMUTIL_FORCE_SYSTEM` (default `OFF`): Require all CPM dependencies to use system packages. NOT RECOMMENDED! - - You may optionally override this (section) -- `CPMUTIL_FORCE_BUNDLED` (default `ON` on MSVC and Android, `OFF` elsewhere): Require all CPM dependencies to use bundled packages. - -You are highly encouraged to read AddPackage first, even if you plan to only interact with CPMUtil via `AddJsonPackage`. - -- [AddPackage](#addpackage) -- [AddCIPackage](#addcipackage) -- [AddJsonPackage](#addjsonpackage) -- [AddQt](#addqt) -- [Lists](#lists) -- [For Packagers](#for-packagers) - - [Network Sandbox](#network-sandbox) - - [Unsandboxed](#unsandboxed) - -## AddPackage - -The core of CPMUtil is the [`AddPackage`](./AddPackage.md) function. [`AddPackage`](./AddPackage.md) itself is fully CMake-based, and largely serves as an interface between CPM and the rest of CPMUtil. - -## AddCIPackage - -[`AddCIPackage`](./AddCIPackage.md) adds a package that follows [crueter's CI repository spec](https://github.com/crueter-ci). - -## AddJsonPackage - -[`AddJsonPackage`](./AddJsonPackage.md) is the recommended method of usage for CPMUtil. - -## AddDependentPackage - -[`AddDependentPackage`](./AddDependentPackage.md) allows you to add multiple packages such that all of them must be from the system OR bundled. - -## AddQt - -[`AddQt`](./AddQt.md) adds a specific version of Qt to your project. - -## Lists - -CPMUtil will create three lists of dependencies where `AddPackage` or similar was used. Each is in order of addition. - -- `CPM_PACKAGE_NAMES`: The names of packages included by CPMUtil -- `CPM_PACKAGE_URLS`: The URLs to project/repo pages of packages -- `CPM_PACKAGE_SHAS`: Short version identifiers for each package - - If the package was included as a system package, `(system)` is appended thereafter - - Packages whose versions can't be deduced will be left as `unknown`. - -For an example of how this might be implemented in an application, see Eden's implementation: - -- [`dep_hashes.h.in`](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/src/dep_hashes.h.in) -- [`GenerateDepHashes.cmake`](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CMakeModules/GenerateDepHashes.cmake) -- [`deps_dialog.cpp`](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/src/yuzu/deps_dialog.cpp) - -## For Packagers - -If you are packaging a project that uses CPMUtil, read this! - -### Network Sandbox - -For sandboxed environments (e.g. Gentoo, nixOS) you must install all dependencies to the system beforehand and set `-DCPMUTIL_FORCE_SYSTEM=ON`. If a dependency is missing, get creating! - -Alternatively, if CPMUtil pulls in a package that has no suitable way to install or use a system version, download it separately and pass `-DPackageName_DIR=/path/to/downloaded/dir` (e.g. shaders) - -### Unsandboxed - -For others (AUR, MPR, etc). CPMUtil will handle everything for you, including if some of the project's dependencies are missing from your distribution's repositories. That is pretty much half the reason I created this behemoth, after all. diff --git a/docs/Caveats.md b/docs/Caveats.md index 8c752cf79d..c5b925f4cd 100644 --- a/docs/Caveats.md +++ b/docs/Caveats.md @@ -46,7 +46,7 @@ Qt Widgets appears to be broken. For now, add `-DENABLE_QT=OFF` to your configur This is needed for some dependencies that call cc directly (tz): ```sh -echo '#!/bin/sh' >cc +echo '#!/bin/sh -e' >cc echo 'gcc $@' >>cc chmod +x cc export PATH="$PATH:$PWD" @@ -65,14 +65,13 @@ export LIBGL_ALWAYS_SOFTWARE=1 ``` - Modify the generated ffmpeg.make (in build dir) if using multiple threads (base system `make` doesn't use `-j4`, so change for `gmake`). -- If using OpenIndiana, due to a bug in SDL2's CMake configuration, audio driver defaults to SunOS ``, which does not exist on OpenIndiana. Using external or bundled SDL2 may solve this. - System OpenSSL generally does not work. Instead, use `-DYUZU_USE_BUNDLED_OPENSSL=ON` to use a bundled static OpenSSL, or build a system dependency from source. ## OmniOS Install `developer/gcc14` on OmniOS using pkgsrc. -Since so many dependencies are missing on `OmniOS`, you may wish to use `-DCPMUTIL_FORCE_BUNDLED=ON -DYUZU_USE_EXTERNAL_SDL2=ON` +Since so many dependencies are missing on `OmniOS`, you may wish to use `-DCPMUTIL_FORCE_BUNDLED=ON` For OmniOS you are required to build glslang yourself: ```sh diff --git a/docs/CrossCompile.md b/docs/CrossCompile.md index 1f6ef447e6..06bac5d9db 100644 --- a/docs/CrossCompile.md +++ b/docs/CrossCompile.md @@ -10,3 +10,81 @@ A painless guide for cross compilation (or to test NCE) from a x86_64 system wit - Download Debian 13: `wget https://cdimage.debian.org/debian-cd/current/arm64/iso-cd/debian-13.0.0-arm64-netinst.iso` - Create a system disk: `qemu-img create -f qcow2 debian-13-arm64-ci.qcow2 30G` - Run the VM: `qemu-system-aarch64 -M virt -m 2G -cpu max -bios /usr/local/share/qemu/edk2-aarch64-code.fd -drive if=none,file=debian-13.0.0-arm64-netinst.iso,format=raw,id=cdrom -device scsi-cd,drive=cdrom -drive if=none,file=debian-13-arm64-ci.qcow2,id=hd0,format=qcow2 -device virtio-blk-device,drive=hd0 -device virtio-gpu-pci -device usb-ehci -device usb-kbd -device intel-hda -device hda-output -nic user,model=virtio-net-pci` + +## Gentoo + +Gentoo's cross-compilation setup is relatively easy, provided you're already familiar with portage. A [cross toolchain file](../CMakeModules/toolchains/GentooCross.cmake) is provided. Throughout this section, replace `aarch64` with whatever target architecture you desire. + +### Crossdev + +First, emerge crossdev via `sudo emerge -a sys-devel/crossdev`. + +Now, set up the environment depending on the target architecture; e.g. + +```sh +sudo crossdev aarch64 +``` + +### QEMU + +If you don't have a host Gentoo system of your target architecture, you should install a QEMU user setup for testing. To do so, enable the relevant USE flags for `app-emulation/qemu`: + +```txt +app-emulation/qemu static-user qemu_user_targets_aarch64 +``` + +To use cross-emerged shared libraries, you will also need to tell qemu where the sysroot is. You can do this with an alias: + +```sh +alias qemu-aarch64="qemu-aarch64 -L /usr/aarch64-unknown-linux-gnu" +``` + +### Dependencies + +Dependencies are the same [as normal Gentoo](./Deps.md#Commands); simply replace the `emerge` command with `emerge--unknown-linux-gnu` (e.g. `emerge-aarch64-unknown-linux-gnu`). However, there are a few caveats: + +#### Enabling GURU + +Since Crossdev sysroots are effectively isolated from the system w.r.t Portage, you must manually enable GURU in your sysroot. Run the following as root: + +```sh +mkdir -p /usr/aarch64-unknown-linux-gnu/etc/portage/repos.conf +cat << EOF > /usr/aarch64-unknown-linux-gnu/etc/portage/repos.conf/guru.conf +[guru] +location = /var/db/repos/guru +auto-sync = no +priority = 1 +EOF +``` + +#### Package Errata + +Crossdev is not perfect, and you may face some challenges with package that are not properly keyworded or have issues on specific architectures. These behaviors are, unfortunately, not well documented, and certain build systems such as Meson--and certain troublesome packages like GTK--are generally unfriendly towards cross-compilation. + +Thus, it may be desirable to emerge a minimal set of dependencies and allow Eden's build system to handle the rest for you. At a minimum, you *only* need standard system libraries (Crossdev does this for you) and Qt: + +```sh +sudo emerge-aarch64-unknown-linux-gnu dev-qt/qtbase:6 dev-qt/qtcharts:6 +``` + +From here, CPMUtil will take care of everything else. For extra insurance, you may want to set `-DCPMUTIL_FORCE_BUNDLED=ON` in your configure command. + +### Building + +From here, building is relatively standard. The [cross toolchain file](../CMakeModules/toolchains/GentooCross.cmake) contains a few additional configurations, but generally all you need to do is set `CROSS_TARGET` and `CMAKE_TOOLCHAIN_FILE`. Disabling OpenGL is strongly recommended as well. + +```sh +cmake -S . -B build/aarch64 -DCMAKE_TOOLCHAIN_FILE=CMakeModules/toolchains/GentooCross.cmake -GNinja -DCROSS_TARGET=aarch64 -DENABLE_OPENGL=OFF +``` + +With that done, you can build as normal: + +```sh +cmake --build build/aarch64 +``` + +And finally, run the compiled executable with QEMU! + +```sh +qemu-aarch64 build/aarch64/bin/eden +``` diff --git a/docs/Debug.md b/docs/Debug.md index 335806d52b..026e920d9b 100644 --- a/docs/Debug.md +++ b/docs/Debug.md @@ -69,29 +69,6 @@ Expressions can be `variable_names` or `1234` (numbers) or `*var` (dereference o For more information type `info gdb` and read [the man page](https://man7.org/linux/man-pages/man1/gdb.1.html). -## Simple checklist for debugging black screens using Renderdoc +# RenderDoc (Graphic Debugging Tool) -Renderdoc is a free, cross platform, multi-graphics API debugger. It is an invaluable tool for diagnosing issues with graphics applications, and includes support for Vulkan. Get it at [renderdoc.org](https://renderdoc.org). - -Before using renderdoc to diagnose issues, it is always good to make sure there are no validation errors. Any errors means the behavior of the application is undefined. That said, renderdoc can help debug validation errors if you do have them. - -When debugging a black screen, there are many ways the application could have setup Vulkan wrong. -Here is a short checklist of items to look at to make sure are appropriate: - -- Draw call counts are correct (aka not zero, or if rendering many triangles, not 3) -- Vertex buffers are bound -- vertex attributes are correct - Make sure the size & offset of each attribute matches what should it should be -- Any bound push constants and descriptors have the right data - including: - - Matrices have correct values - double check the model, view, & projection matrices are uploaded correctly -- Pipeline state is correct - - viewport range is correct - x,y are 0,0; width & height are screen dimensions, minDepth is 0, maxDepth is 1, NDCDepthRange is 0,1 - - Fill mode matches expected - usually solid - - Culling mode makes sense - commonly back or none - - The winding direction is correct - typically CCW (counter clockwise) - - Scissor region is correct - usually same as viewport's x,y,width, &height -- Blend state is correct -- Depth state is correct - typically enabled with Function set to Less than or Equal -- Swapchain images are bound when rendering to the swapchain -- Image being rendered to is the same as the one being presented when rendering to the swapchain - -Alternatively, a [RenderDoc Extension](https://github.com/baldurk/renderdoc-contrib/tree/main/baldurk/whereismydraw) ([Archive](https://web.archive.org/web/20250000000000*/https://github.com/baldurk/renderdoc-contrib/tree/main/baldurk/whereismydraw)) exists which automates doing a lot of these manual steps. +Guidelines for graphical debugging using RenderDoc: **[RenderDoc usage](./RenderDoc.md)** \ No newline at end of file diff --git a/docs/Deps.md b/docs/Deps.md index 744713d4f0..612e423d9f 100644 --- a/docs/Deps.md +++ b/docs/Deps.md @@ -16,7 +16,7 @@ To build Eden, you MUST have a C++ compiler. The following additional tools are also required: -* **[CMake](https://www.cmake.org/)** 3.22+ - already included with the Android SDK +* **[CMake](https://www.cmake.org/)** 3.31+ - already included with the Android SDK * **[Git](https://git-scm.com/)** for version control * **[Windows installer](https://gitforwindows.org)** * **[Python3](https://www.python.org/downloads/)** 3.10+ - necessary to download external repositories @@ -35,6 +35,11 @@ If you are on desktop and plan to use the Qt frontend, you *must* install Qt 6, * For help setting up Qt Creator, run `./install.sh -h qtcreator` +* If you're using clang-cl and want to still use MSVC + * Check the option to add "C++ clang compiler for Windows" on Visual Studio installer and uncheck "x64/x86 build tool for MSVC" while selecting "C++ desktop developement tools" and change Visual Studio to 2026, from 2022. + * At qt creator section generator tab change Visual Studio 17 2022 to 2026. + * Finally, to use clang-cl: `cmake -S . -B build -G "Visual Studio 17 2026" -T ClangCL` + If you are on **Windows** and building with **MSVC** or **clang-cl**, you may go [back home](Build.md) and continue. ## Externals @@ -42,7 +47,7 @@ If you are on **Windows** and building with **MSVC** or **clang-cl**, you may go The following are handled by Eden's externals: * [FFmpeg](https://ffmpeg.org/) (should use `-DYUZU_USE_EXTERNAL_FFMPEG=ON`) -* [SDL2](https://www.libsdl.org/download-2.0.php) 2.0.18+ (should use `-DYUZU_USE_EXTERNAL_SDL2=ON` OR `-DYUZU_USE_BUNDLED_SDL2=ON` to reduce compile time) +* [SDL3](https://www.libsdl.org/download-2.0.php) 3.2.10+ (Use `-DYUZU_USE_BUNDLED_SDL2=ON` to reduce compile time) All other dependencies will be downloaded and built by [CPM](https://github.com/cpm-cmake/CPM.cmake/) if `YUZU_USE_CPM` is on, but will always use system dependencies if available (UNIX-like only): @@ -118,7 +123,7 @@ sudo emerge -a \ dev-util/spirv-tools dev-util/spirv-headers dev-util/vulkan-headers \ dev-util/vulkan-utility-libraries dev-util/glslang \ media-gfx/renderdoc media-libs/libva media-libs/opus media-video/ffmpeg \ - media-libs/VulkanMemoryAllocator media-libs/libsdl2 media-libs/cubeb \ + media-libs/VulkanMemoryAllocator media-libs/libsdl3 media-libs/cubeb \ net-libs/enet \ sys-libs/zlib \ dev-cpp/nlohmann_json dev-cpp/simpleini dev-cpp/cpp-httplib dev-cpp/cpp-jwt \ @@ -137,7 +142,8 @@ Required USE flags: * `dev-qt/qtbase network concurrent dbus gui widgets` * `dev-libs/quazip qt6` -* `media-libs/libsdl2 haptic joystick sound video` +* `media-libs/libsdl3 haptic joystick sound video` + * Adding `X vulkan udev opengl` is recommended but not required * `dev-cpp/cpp-httplib ssl` [Caveats](./Caveats.md#gentoo-linux) @@ -148,7 +154,7 @@ Required USE flags: Arch Linux ```sh -sudo pacman -Syu --needed base-devel boost catch2 cmake enet ffmpeg fmt git glslang libzip lz4 ninja nlohmann-json openssl opus qt6-base qt6-multimedia qt6-charts sdl2 zlib zstd zip unzip vulkan-headers vulkan-utility-libraries libusb spirv-tools spirv-headers +sudo pacman -Syu --needed base-devel boost catch2 cmake enet ffmpeg fmt git glslang libzip lz4 ninja nlohmann-json openssl opus qt6-base qt6-multimedia qt6-charts sdl3 zlib zstd zip unzip vulkan-headers vulkan-utility-libraries libusb spirv-tools spirv-headers ``` * Building with QT Web Engine requires `qt6-webengine` as well. @@ -161,10 +167,10 @@ sudo pacman -Syu --needed base-devel boost catch2 cmake enet ffmpeg fmt git glsl Ubuntu, Debian, Mint Linux ```sh -sudo apt-get install autoconf cmake g++ gcc git glslang-tools libglu1-mesa-dev libhidapi-dev libpulse-dev libtool libudev-dev libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-render-util0 libxcb-xinerama0 libxcb-xkb1 libxext-dev libxkbcommon-x11-0 mesa-common-dev nasm ninja-build qt6-base-private-dev catch2 libfmt-dev liblz4-dev nlohmann-json3-dev libzstd-dev libssl-dev libavfilter-dev libavcodec-dev libswscale-dev pkg-config zlib1g-dev libva-dev libvdpau-dev qt6-tools-dev qt6-charts-dev libvulkan-dev spirv-tools spirv-headers libusb-1.0-0-dev libxbyak-dev libboost-dev libboost-fiber-dev libboost-context-dev libsdl2-dev libopus-dev libasound2t64 vulkan-utility-libraries-dev +sudo apt-get install autoconf cmake g++ gcc git glslang-tools libglu1-mesa-dev libhidapi-dev libpulse-dev libtool libudev-dev libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-render-util0 libxcb-xinerama0 libxcb-xkb1 libxext-dev libxkbcommon-x11-0 mesa-common-dev nasm ninja-build qt6-base-private-dev catch2 libfmt-dev liblz4-dev nlohmann-json3-dev libzstd-dev libssl-dev libavfilter-dev libavcodec-dev libswscale-dev pkg-config zlib1g-dev libva-dev libvdpau-dev qt6-tools-dev qt6-charts-dev libvulkan-dev spirv-tools spirv-headers libusb-1.0-0-dev libxbyak-dev libboost-dev libboost-fiber-dev libboost-context-dev libsdl3-dev libopus-dev libasound2t64 vulkan-utility-libraries-dev ``` -* Ubuntu 22.04, Linux Mint 20, or Debian 12 or later is required. +* Ubuntu 26.04, Linux Mint 22.3, or Debian 13 or later is required. * To enable QT Web Engine, add `-DYUZU_USE_QT_WEB_ENGINE=ON` when running CMake. @@ -185,13 +191,13 @@ AlmaLinux (use `YUZU_USE_CPM=ON`): sudo dnf install epel-release dnf-utils # (run rpmfusion installation afterwards) # vvv - This will work for most systems -sudo dnf install autoconf cmake libtool libudev cmake gcc gcc-c++ qt6-qtbase-devel zlib-devel openssl-devel boost SDL2 ffmpeg-devel libdrm glslang jq patch +sudo dnf install autoconf cmake libtool libudev cmake gcc gcc-c++ qt6-qtbase-devel zlib-devel openssl-devel boost SDL3 ffmpeg-devel libdrm glslang jq patch # Qt6 private GUI must be taken from CRB repos sudo dnf config-manager --enable crb sudo dnf install qt6-qtbase-private-devel ``` -For systems like OpenEuler or derivates, don't forget to also install: `SDL2-devel pkg-config fmt-dev nlohmann-json-dev`. +For systems like OpenEuler or derivates, don't forget to also install: `SDL3-devel pkg-config fmt-dev nlohmann-json-dev`. * [RPM Fusion](https://rpmfusion.org/Configuration) is required for `ffmpeg-devel` * Fedora 32 or later is required. @@ -208,7 +214,7 @@ First, enable the community repository; [see here](https://wiki.alpinelinux.org/ # Enable the community repository setup-apkrepos -c # Install -apk add g++ git cmake make mesa-dev qt6-qtbase-dev qt6-qtbase-private-dev libquazip1-qt6 ffmpeg-dev qt6-charts-dev libusb-dev libtool boost-dev sdl2-dev zstd-dev vulkan-utility-libraries spirv-tools-dev openssl-dev nlohmann-json lz4-dev opus-dev jq patch +apk add g++ git cmake make mesa-dev qt6-qtbase-dev qt6-qtbase-private-dev libquazip1-qt6 ffmpeg-dev qt6-charts-dev libusb-dev libtool boost-dev sdl3-dev zstd-dev vulkan-utility-libraries spirv-tools-dev openssl-dev nlohmann-json lz4-dev opus-dev jq patch ``` @@ -216,7 +222,7 @@ apk add g++ git cmake make mesa-dev qt6-qtbase-dev qt6-qtbase-private-dev libqua Void Linux ```sh -xbps-install -Su git make cmake clang pkg-config patch SPIRV-Tools-devel SPIRV-Headers lz4 liblz4-devel boost-devel ffmpeg6-devel catch2 Vulkan-Utility-Libraries Vulkan-Headers glslang openssl-devel SDL2-devel quazip-qt6-devel qt6-base-devel qt6-qt5compat-devel qt6-charts-devel fmt-devel json-c++ libenet-devel libusb-devel +xbps-install -Su git make cmake clang pkg-config patch SPIRV-Tools-devel SPIRV-Headers lz4 liblz4-devel boost-devel ffmpeg6-devel catch2 Vulkan-Utility-Libraries Vulkan-Headers glslang openssl-devel SDL3-devel quazip-qt6-devel qt6-base-devel qt6-qt5compat-devel qt6-charts-devel fmt-devel json-c++ libenet-devel libusb-devel ``` Yes, `nlohmann-json` is just named `json-c++`. Why? @@ -237,7 +243,7 @@ If you're going for a pure build (i.e no downloaded deps), use `-DYUZU_USE_CPM=O Install dependencies from **[Homebrew](https://brew.sh/)** ```sh -brew install autoconf automake boost ffmpeg fmt glslang hidapi libtool libusb lz4 ninja nlohmann-json openssl pkg-config qt@6 sdl2 speexdsp zlib zstd cmake Catch2 molten-vk vulkan-loader spirv-tools +brew install autoconf automake boost ffmpeg fmt glslang hidapi libtool libusb lz4 ninja nlohmann-json openssl pkg-config qt@6 sdl3 speexdsp zlib zstd cmake Catch2 molten-vk vulkan-loader spirv-tools ``` If you are compiling on Intel Mac, or are using a Rosetta Homebrew installation, you must replace all references of `/opt/homebrew` with `/usr/local`. @@ -254,7 +260,7 @@ brew install molten-vk
FreeBSD -As root run: `pkg install devel/cmake devel/sdl20 devel/boost-libs devel/catch2 devel/libfmt devel/nlohmann-json devel/ninja devel/nasm devel/autoconf devel/pkgconf devel/qt6-base devel/qt6-charts devel/simpleini net/enet multimedia/ffnvcodec-headers multimedia/ffmpeg audio/opus archivers/liblz4 lang/gcc12 graphics/glslang graphics/vulkan-utility-libraries graphics/spirv-tools www/cpp-httplib devel/unordered-dense vulkan-headers quazip-qt6` +As root run: `pkg install devel/cmake sdl3 devel/boost-libs devel/catch2 devel/libfmt devel/nlohmann-json devel/ninja devel/nasm devel/autoconf devel/pkgconf devel/qt6-base devel/qt6-charts devel/simpleini net/enet multimedia/ffnvcodec-headers multimedia/ffmpeg audio/opus archivers/liblz4 lang/gcc12 graphics/glslang graphics/vulkan-utility-libraries graphics/spirv-tools www/cpp-httplib devel/unordered-dense vulkan-headers quazip-qt6` If using FreeBSD 12 or prior, use `devel/pkg-config` instead. @@ -264,7 +270,11 @@ If using FreeBSD 12 or prior, use `devel/pkg-config` instead.
NetBSD -For NetBSD +10.1: `pkgin install git cmake boost fmtlib SDL2 catch2 libjwt spirv-headers spirv-tools ffmpeg7 libva nlohmann-json jq libopus qt6 cpp-httplib lz4 vulkan-headers nasm autoconf enet pkg-config libusb1 libcxx`. +For NetBSD +10.1: + +```sh +pkgin install git cmake boost fmtlib SDL3 catch2 libjwt spirv-headers spirv-tools ffmpeg7 libva nlohmann-json jq libopus qt6 cpp-httplib lz4 vulkan-headers nasm autoconf enet pkg-config libusb1 libcxx frozen +``` [Caveats](./Caveats.md#netbsd). @@ -274,7 +284,7 @@ For NetBSD +10.1: `pkgin install git cmake boost fmtlib SDL2 catch2 libjwt spirv ```sh pkg_add -u -pkg_add cmake nasm git boost unzip--iconv autoconf-2.72p0 bash ffmpeg glslang gmake qt6 jq fmt nlohmann-json enet boost vulkan-utility-libraries vulkan-headers spirv-headers spirv-tools catch2 sdl2 libusb1-1.0.29 +pkg_add cmake nasm git boost unzip--iconv autoconf-2.72p0 bash ffmpeg glslang gmake qt6 jq fmt nlohmann-json enet boost vulkan-utility-libraries vulkan-headers spirv-headers spirv-tools catch2 sdl3 libusb1-1.0.29 quazip-qt6 ``` [Caveats](./Caveats.md#openbsd). @@ -284,7 +294,7 @@ pkg_add cmake nasm git boost unzip--iconv autoconf-2.72p0 bash ffmpeg glslang gm DragonFlyBSD ```sh -pkg install gcc14 git cmake unzip nasm autoconf bash pkgconf ffmpeg glslang gmake jq nlohmann-json enet spirv-tools sdl2 vulkan-utility-libraries vulkan-headers catch2 libfmt openssl liblz4 boost-libs cpp-httplib qt6-base qt6-charts quazip-qt6 unordered-dense libva-vdpau-driver libva-utils libva-intel-driver +pkg install gcc14 git cmake unzip nasm autoconf bash pkgconf ffmpeg glslang gmake jq nlohmann-json enet spirv-tools sdl3 vulkan-utility-libraries vulkan-headers catch2 libfmt openssl liblz4 boost-libs cpp-httplib qt6-base qt6-charts quazip-qt6 unordered-dense libva-vdpau-driver libva-utils libva-intel-driver ``` [Caveats](./Caveats.md#dragonflybsd). @@ -294,7 +304,7 @@ pkg install gcc14 git cmake unzip nasm autoconf bash pkgconf ffmpeg glslang gmak OpenIndiana ```sh -sudo pkg install git cmake qt6 boost glslang libzip library/lz4 libusb-1 nlohmann-json openssl opus sdl2 zlib compress/zstd unzip pkg-config nasm autoconf mesa library/libdrm header-drm developer/fmt +sudo pkg install git cmake qt6 boost glslang libzip library/lz4 libusb-1 nlohmann-json openssl opus sdl3 zlib compress/zstd unzip pkg-config nasm autoconf mesa library/libdrm header-drm developer/fmt ``` [Caveats](./Caveats.md#openindiana). @@ -318,7 +328,7 @@ sudo pkgin install git cmake autoconf build-essential libusb-1 nasm gcc13 ```sh BASE="git make autoconf libtool automake-wrapper jq patch" -MINGW="qt6-base qt6-charts qt6-tools qt6-translations qt6-svg cmake toolchain clang python-pip openssl vulkan-memory-allocator vulkan-devel glslang boost fmt lz4 nlohmann-json zlib zstd enet opus libusb unordered_dense openssl SDL2" +MINGW="qt6-base qt6-charts qt6-tools qt6-translations qt6-svg cmake toolchain clang python-pip openssl vulkan-memory-allocator vulkan-devel glslang boost fmt lz4 nlohmann-json zlib zstd enet opus libusb unordered_dense openssl SDL3" # Either x86_64 or clang-aarch64 (Windows on ARM) packages="$BASE" for pkg in $MINGW; do @@ -344,7 +354,7 @@ pacman -Syuu --needed --noconfirm $packages HaikuOS ```sh -pkgman install git cmake patch libfmt_devel nlohmann_json lz4_devel opus_devel boost1.90_devel vulkan_devel qt6_base_devel qt6_declarative_devel libsdl2_devel ffmpeg7_devel libx11_devel enet_devel catch2_devel quazip1_qt5_devel qt6_5compat_devel glslang qt6_devel qt6_charts_devel +pkgman install git cmake patch libfmt_devel nlohmann_json lz4_devel opus_devel boost1.90_devel vulkan_devel qt6_base_devel qt6_declarative_devel libsdl3_devel ffmpeg7_devel libx11_devel enet_devel catch2_devel quazip1_qt5_devel qt6_5compat_devel glslang qt6_devel qt6_charts_devel cubeb_devel simpleini quazip_qt6_devel ``` [Caveats](./Caveats.md#haikuos). @@ -355,9 +365,11 @@ pkgman install git cmake patch libfmt_devel nlohmann_json lz4_devel opus_devel b ```sh sudo pkg update -sudo pkg install git cmake ffmpeg6 sdl2 zlib llvm18 +sudo pkg install git cmake ffmpeg6 zlib llvm18 ``` +RedoxOS currently does not support SDL3. You will have to compile it yourself and pray. + [Caveats](./Caveats.md#redoxos).
diff --git a/docs/HosKernel.md b/docs/HosKernel.md new file mode 100644 index 0000000000..87df2072a5 --- /dev/null +++ b/docs/HosKernel.md @@ -0,0 +1,31 @@ +# HOS Kernel + +In brief, the HOS kernel is a microkernel, all services and programs run in userspace, the primary way to do communication between these is via `HIPC` (not covered here); otherwise most of the primitives reside in the forms of syscalls invoked via `svc #imm`. The kernel supports both 32-bit and 64-bit programs, and has the capacity to use 32, 36 and 39 bits of address space for spawned processes. Most of the networking stack is based off FreeBSD's network stack. + +The emulator implements the majority of the syscalls pertaining to the HOS kernel itself. When we talk about the HOS Kernel (in the context of the emulator) we are strictly speaking about the mechanisms from which syscalls are handled (and it's subsequent side effects, such as the page table book-keeping). The emulator at it's current state is unable to load a custom low-level kernel and do supervisor-level emulation. + +Most programs in NX eventually invoke an `svc`, which, depending on it's immediate value, will go on to be dispatched into one of the specific syscall handlers. + +These can be seen in [svc.cpp](/src/core/hle/kernel/svc.cpp). All of these correspond to syscalls which userspace programs may perform. + +In turn, these syscalls create the mechanisms that allows programs to use CMIF/TIPC as their primary IPC form to contact other services/processes running on the system, the details of which will not be covered here, but you can consult the relevant [SwitchBrew article: 'HIPC'](https://switchbrew.org/wiki/HIPC). + +From the point of view of the programs, no special devices (such as PCIE, Realtek drivers, Bluetooth or USB) has to be handled by the emulator; this is because most of the fun occurs in specialized services such as `usb:u` or `pcie` services. Which aren't emulated (yet). + +Due to the nature of syscalls, many of them interact with memory. The emulated kernel has an internal tree-like structure, borrowed from FreeBSD's intrusive red-black tree; this is used to track and find mappings added or removed. Thus most of the process space is emulated in this way. + +The kernel keeps it's own separate pagetable, in a traditional sense, each process has it's own pagetable, this is true for HOS as well. + +Every process keeps it's own tracking of the following structures: +- Name (13 characters) +- 64-bit ID +- A handle table +- Exclusive monitor +- Threads +- Held locks +- Thread local pages +- A page table for each process + +The emulator willingly restricts itself to only use 4 threads (to emulate 4 cores), this is because most existing applications do not benefit greatly from the added core count, and in fact can be detrimental due to extra contention. This translates equitatively to about 4 `ArmInterface` slots for each process, these are then redirected to whatever is the last `pc` of the last thread running on the core is meant to be; proceed to run it, then when returning (due to halt or interruption), proceed to reschedule the thread. + +The scheduler as-is isn't 100% faithful to the original (for example the original is cooperative and not preemptive), and has great timing variance (especially due to the fact the emulator can run in systems with wildly different timings). diff --git a/docs/NvidiaGpu.md b/docs/NvidiaGpu.md index d7fe3d86d3..a595f61a66 100644 --- a/docs/NvidiaGpu.md +++ b/docs/NvidiaGpu.md @@ -853,6 +853,8 @@ Texture Query. Vote Across SIMD Thread Group +`VOTE_vtg` is a kepler leftover. + # VSET `0100 000- ---- ----` diff --git a/docs/Options.md b/docs/Options.md index 3eb6effe92..bd353b16ca 100644 --- a/docs/Options.md +++ b/docs/Options.md @@ -29,8 +29,7 @@ These options control dependencies. - `YUZU_TZDB_PATH` (string) Path to a pre-downloaded timezone database (useful for nixOS and Gentoo) - `YUZU_USE_BUNDLED_MOLTENVK` (ON, macOS only) Download bundled MoltenVK lib - `YUZU_USE_BUNDLED_OPENSSL` (ON for MSVC, Android, Solaris, and OpenBSD) Download bundled OpenSSL build -- `YUZU_USE_EXTERNAL_SDL2` (OFF) Compiles SDL2 from source -- `YUZU_USE_BUNDLED_SDL2` (ON for MSVC) Download a prebuilt SDL2 +- `YUZU_USE_BUNDLED_SDL3` (ON for MSVC) Download a prebuilt SDL3 ### Miscellaneous @@ -44,6 +43,9 @@ These options control dependencies. - UNIX may be better off appending `-flto=thin` to compiler args - `USE_FASTER_LINKER` (OFF) Check if a faster linker is available - Not recommended outside of Linux +- `YUZU_INSTALL_UDEV_RULES` (OFF) Install udev rules to enable hidraw access + - Needed for gyroscopes + - Only available on Linux ### Flavors @@ -60,7 +62,7 @@ These options control executables and build flavors. **Desktop only**: -- `YUZU_CMD` (ON) Compile the SDL2 frontend (eden-cli) +- `YUZU_CMD` (ON) Compile the SDL-based frontend (eden-cli) - `YUZU_ROOM` (OFF) Compile dedicated room functionality into the main executable - `YUZU_ROOM_STANDALONE` (OFF) Compile a separate executable for room functionality - `YUZU_STATIC_ROOM` (OFF) Compile the room executable *only* as a static, portable executable @@ -96,5 +98,6 @@ The following options were a part of Eden at one point, but have since been reti - `ENABLE_SDL2` - While technically possible to *not* use SDL2 on desktop, this is **NOT** a supported configuration under any means, and adding this matrix to our build system was not worth the effort. - `YUZU_USE_CPM` - This option once had a purpose, but that purpose has long since passed us by. *All* builds use CPMUtil to manage dependencies now. - If you want to *force* the usage of system dependencies, use `-DCPMUTIL_FORCE_SYSTEM=ON`. +- `YUZU_USE_EXTERNAL_SDL` - This is now handled automatically. It was included even after CPM for purposes that have not applied for a very long time. See `src/dynarmic/CMakeLists.txt` for additional options--usually, these don't need changed diff --git a/docs/README.md b/docs/README.md index 68775f99d8..985b5cb7e0 100644 --- a/docs/README.md +++ b/docs/README.md @@ -2,21 +2,27 @@ Are you just a casual user? Take a look at our [User Handbook](./user) then! +If you want to register/signup as a contributor, take a gander at the [signup guide](./SIGNUP.md). + This contains documentation created by developers. This contains build instructions, guidelines, instructions/layouts for [cool stuff we made](./CPMUtil), and more. -- **[General Build Instructions](Build.md)** -- **[CMake Options](Options.md)** -- **[Cross Compiling](CrossCompile.md)** -- **[Development Guidelines](Development.md)** -- **[Dependencies](Deps.md)** +- **[General Build Instructions](./Build.md)** +- **[CMake Options](./Options.md)** +- **[Cross Compiling](./CrossCompile.md)** +- **[Development Guidelines](./Development.md)** +- **[Dependencies](./Deps.md)** - **[Debug Guidelines](./Debug.md)** +- **[RenderDoc usage](./RenderDoc.md)** - **[CPM - CMake Package Manager](./CPMUtil)** -- **[Platform-Specific Caveats](Caveats.md)** +- **[Platform-Specific Caveats](./Caveats.md)** - **[The NVIDIA SM86 (Maxwell) GPU](./NvidiaGpu.md)** -- **[Dynarmic](./dynarmic)** - **[Cross compilation](./CrossCompile.md)** - **[Driver Bugs](./DriverBugs.md)** - **[Building Older Commits](./build/OlderCommits.md)** +- Subsystems: + - **[Dynarmic](./dynarmic/README.md)** + - **[HOS Kernel](./HosKernel.md)** + - **[Settings](./Settings.md)** ## Policies @@ -25,3 +31,12 @@ Policies and information on development. - **[AI and LLM Usage](./policies/AI.md)** - **[Release Policy](./policies/Release.md)** - **[Coding guidelines](./policies/Coding.md)** +- **[Coding Style guidelines](./policies/CodingStyle.md)** + +## Externals + +Other useful resources in general, take a quick read if you need. + +- **[SwitchBrew](https://switchbrew.org/wiki/Main_Page)** +- **[IPS file format](https://zerosoft.zophar.net/ips.php)** +- **[IPSwitch file format](https://github.com/3096/ipswitch)** diff --git a/docs/RenderDoc.md b/docs/RenderDoc.md new file mode 100644 index 0000000000..3495d18dad --- /dev/null +++ b/docs/RenderDoc.md @@ -0,0 +1,52 @@ +# RenderDoc + +Renderdoc is a free, cross platform, multi-graphics API debugger. It is an invaluable tool for diagnosing issues with graphics applications, and includes support for Vulkan. Get it at [renderdoc.org](https://renderdoc.org). + +RenderDoc can capture Eden's Vulkan output when its Vulkan layer is loaded before Eden creates the Vulkan device. Before using renderdoc to diagnose issues, it is always good to make sure there are no validation errors. Any errors means the behavior of the application is undefined. That said, renderdoc can help debug validation errors if you do have them. + +## Usage on Windows + +You can either use RenderDoc UI to launch eden, or you can make eden attach it internally: + +On Windows PowerShell: +```powershell +$env:ENABLE_VULKAN_RENDERDOC_CAPTURE='1' +.\eden.exe +``` +When RenderDoc is attached, Eden logs the default Windows capture folder: +```text +%LOCALAPPDATA%\Temp\RenderDoc +``` + +Press RenderDoc's capture hotkey, usually `F12`, to capture a frame. To stop using RenderDoc, close Eden and launch it again without `ENABLE_VULKAN_RENDERDOC_CAPTURE`. + +## Eden Hotkey + +Eden also has a separate `Toggle Renderdoc Capture` hotkey behind the debug setting `renderdoc_hotkey`. +That hotkey does not load or unload RenderDoc. It only toggles Eden's own manual capture through RenderDoc's API: + +- first press: starts a capture +- second press: ends that capture + +## Simple checklist for debugging black screens using Renderdoc + +When debugging a black screen, there are many ways the application could have setup Vulkan wrong. +Here is a short checklist of items to look at to make sure are appropriate: + +- Draw call counts are correct (aka not zero, or if rendering many triangles, not 3) +- Vertex buffers are bound +- vertex attributes are correct - Make sure the size & offset of each attribute matches what should it should be +- Any bound push constants and descriptors have the right data - including: + - Matrices have correct values - double check the model, view, & projection matrices are uploaded correctly +- Pipeline state is correct + - viewport range is correct - x,y are 0,0; width & height are screen dimensions, minDepth is 0, maxDepth is 1, NDCDepthRange is 0,1 + - Fill mode matches expected - usually solid + - Culling mode makes sense - commonly back or none + - The winding direction is correct - typically CCW (counter clockwise) + - Scissor region is correct - usually same as viewport's x,y,width, &height +- Blend state is correct +- Depth state is correct - typically enabled with Function set to Less than or Equal +- Swapchain images are bound when rendering to the swapchain +- Image being rendered to is the same as the one being presented when rendering to the swapchain + +Alternatively, a [RenderDoc Extension](https://github.com/baldurk/renderdoc-contrib/tree/main/baldurk/whereismydraw) ([Archive](https://web.archive.org/web/20250000000000*/https://github.com/baldurk/renderdoc-contrib/tree/main/baldurk/whereismydraw)) exists which automates doing a lot of these manual steps. diff --git a/docs/Settings.md b/docs/Settings.md new file mode 100644 index 0000000000..4bae71f44e --- /dev/null +++ b/docs/Settings.md @@ -0,0 +1,327 @@ +# Settings + +> [!WARNING] +> This guide is intended for developers ONLY. If you're looking for configuring the emulator itself, please read **[the user handbook](./user/README.md)**. + +Settings on the emulator are very important, toggles and such can be used to guard and/or add branches to paths where some games may crash while others won't, and viceversa. + +However, this process can be tedious for those unfamiliar; this document serves as a outline/documentation for the settings subsystem. + +## Index + +* [Adding Debug Knobs](#adding-debug-knobs) + * [Advantages](#advantages) + * [Usage](#usage) + * [Accessing Debug Knobs (dev side)](#accessing-debug-knobs-dev-side) + * [Setting Debug Knobs (user side)](#setting-debug-knobs-user-side) + * [Bit Manipulation Examples](#bit-manipulation-examples) + * [Terminology and user communication](#terminology-and-user-communication) + * [Examples](#examples) + * [Example 1: Conditional Debug Logging](#example-1-conditional-debug-logging) + * [Example 2: Performance Tuning](#example-2-performance-tuning) + * [Example 3: Feature Gating](#example-3-feature-gating) + * [Best Practices](#best-practices) +* [Adding Boolean Settings Toggles](#adding-boolean-settings-toggles) + * [Step 1 - Common Setting](#step-1-common-setting) + * [Step 2 - Qt Toggle](#step-2-qt-toggle) + * [Step 3 - Kotlin (Android)](#step-3-kotlin-android) + * [Step 3.1 - BooleanSetting.kt](#step-3-1-booleansetting-kt) + * [Step 3.2 - SettingsItem.kt](#step-3-2-settingsitem-kt) + * [Step 3.3 - SettingsFragmentPresenter.kt](#step-3-3-settingsfragmentpresenter-kt) + * [Step 3.4 - Localization](#step-3-4-localization) + * [Step 4 - Use Your Toggle](#step-4-use-your-toggle) + * [Best Practices](#best-practices) + +## Adding Boolean Settings Toggles + +This guide will walk you through adding a new boolean toggle setting to Eden's configuration across both Qt's (PC) and Kotlin's (Android) UIs. + +--- + +### Step 1 - Common Setting + +Firstly add your desired toggle: + +Example: `src/common/setting.h` +```cpp +SwitchableSetting your_setting_name{linkage, false, "your_setting_name", Category::RendererExtensions}; +``` + +Remember to add your toggle to the appropriate category, for example: + +Common Categories: + +* Category::Renderer +* Category::RendererAdvanced +* Category::RendererExtensions +* Category::System +* Category::Core + +> [!WARNING] +> If you wish for your toggle to be `on by default` then change `false` to `true` after `linkage,`. + +--- + +### Step 2 - Qt Toggle + +Add the toggle to the Qt UI, where you wish for it to appear and place it there. + +Example: `src/qt_common/config/shared_translation.cpp` +```cpp +INSERT(Settings, + your_setting_name, + tr("Your Setting Display Name"), + tr("Detailed description of what this setting does.\n" + "You can use multiple lines.\n" + "Explain any caveats or requirements.")); +``` + +#### Make sure to: + +* Keep display naming consistant +* Put detailed info in the description +* Use `\n` for line breaks in descriptions + +--- + +### Step 3 - Kotlin (Android) + +#### Step 3.1 - BooleanSetting.kt + +Add where it should be in the settings. + +Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt` +```kts +RENDERER_YOUR_SETTING_NAME("your_setting_name"), +``` + +#### Make sure to: + +* Ensure the prefix naming matches the intended category. + +--- + +#### Step 3.2 - SettingsItem.kt + +Add the toggle to the Kotlin (Android) UI + +Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt` +```kts +put( + SwitchSetting( + BooleanSetting.RENDERER_YOUR_SETTING_NAME, + titleId = R.string.your_setting_name, + descriptionId = R.string.your_setting_name_description + ) +) +``` + +--- + +#### Step 3.3 - SettingsFragmentPresenter.kt + +Add your setting within the right category. + +Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt` +```kts +add(BooleanSetting.RENDERER_YOUR_SETTING_NAME.key) +``` + +> [!WARNING] +> Remember, placing matters! Settings appear in the order of where you add them. + +--- + +#### Step 3.4 - Localization + +Add your setting and description in the appropriate place. + +Example: `src/android/app/src/main/res/values/strings.xml` +```xml +Your Setting Display Name +Detailed description of what this setting does. Explain any caveats, requirements, or warnings here. +``` + +--- + +### Step 4 - Use Your Toggle! + +Now the UI part is done find a place in the code for the toggle, +And use it to your heart's desire! + +Example: +```cpp +const bool your_value = Settings::values.your_setting_name.GetValue(); + +if (your_value) { + // Do something when enabled +} +``` + +If you wish to do something only when the toggle is disabled, +Use `if (!your_value) {` instead of `if (your_value) {`. + +--- + +### Best Practices + +* Naming - Use clear, descriptive names. Something for both the devs and the users. +* Defaults - Choose safe default values (usually false for new features). +* Documentation - Write clear descriptions explaining when and why to use the setting. +* Categories - Put settings in the appropriate category. +* Order - Place related settings near each other. +* Testing - Always test on both PC and Android before committing when possible. + +Thank you for reading, I hope this guide helped you making your toggle! + +## Adding Debug Knobs + +Debug Knobs is a 16-bit integer setting (`debug_knobs`) in the Eden Emulator that serves as a bitmask for gating various testing and debugging features. This allows developers and advanced users to enable or disable specific debug behaviors without requiring deploying of complete but temporary toggles. + +The setting ranges from 0 to 65535 (0x0000 to 0xFFFF), where each bit represents a different debug feature flag. + +--- + +### Advantages + +The main advantage is to avoid deploying new disposable toggles (those made only for testing stage, and are disposed once new feature gets good to merge). This empowers devs to be free of all frontend burocracy and hassle of new toggles. + +Common advantages recap: + +* **Fine-Grained Control**: Enable or disable up to 16 individual debug features independently using bit manipulation on a single build +* **Runtime Configuration**: Change debug behavior at runtime the same way as new toggles would do +* **Safe incremental development**: New debug features can be added while impact can be isolated from previous deployments + +### Usage + +#### Accessing Debug Knobs (dev side) + +Use the `Settings::getDebugKnobAt(u8 i)` function to check if a specific bit is set: + +```cpp +//cpp side +#include "common/settings.h" + +// Check if bit 0 is set +bool feature_enabled = Settings::getDebugKnobAt(0); + +// Check if bit 15 is set +bool another_feature = Settings::getDebugKnobAt(15); +``` + +```kts +//kotlin side +import org.yuzu.yuzu_emu.features.settings.model.Settings + +// Check if bit x is set +bool feature_enabled = Settings.getDebugKnobAt(x); //x as integer from 0 to 15 +``` + +The function returns `true` if the specified bit (0-15) is set in the `debug_knobs` value, `false` otherwise. + +#### Setting Debug Knobs (user side) + +Developers must inform which knobs are tied to each functionality to be tested. + +The debug knobs value can be set through: + +1. **Desktop UI**: In the Debug configuration tab, there's a spinbox for "Debug knobs" (0-65535) +2. **Android UI**: Available as an integer setting in the Debug section +3. **Configuration Files**: Set the `debug_knobs` value in the emulator's configuration + +#### Bit Manipulation Examples + +To enable specific features, calculate the decimal value by setting the appropriate bits: + +* **Enable only bit 0**: Value = 1 (2^0) +* **Enable only bit 1**: Value = 2 (2^1) +* **Enable bits 0 and 1**: Value = 3 (2^0 + 2^1) +* **Enable bit 15**: Value = 32768 (2^15) + +### Terminology and user communication + +There are two main confusions when talking about knobs: + +#### Whether it's zero-based or one-based + +Sometimes when an user reports: knobs 1 and 2 gets better performance, dev may get confuse whether he means the knobs 1 and 2 literally, or the 1st and 2nd knobs (knobs 0 and 1). + +Debug knobs are **zero-based**, which means: +* The first knob is the knob(0) (or knob0 henceforth), and the last one is the 15 (knob15, likewise) +* You can talk: "knob0 is enabled/disabled", "In this video i was using only knobs 0 and 2", etc. + +#### Whether one is talking about the knob itself or about the entire parameter value (which represents all knobs) + +Sometimes when an user reports: knob 3 results, it's unclear whether he's referring to knob setting with value 3 (which means both knob 0 and 1 are enabled), or to knob(3) specifically. +Whenever you're instructing tests or reporting results, be precise about whether one you're talking to avoid confusion: + +#### Setting based terminology + +ALWAYS use the word in PLURAL (knobs), without mentioning which one, to refer to the setting, aka multiple knobs at once: +Examples: +- **knobs=0**: no knobs enabled +- **knobs=1**: knob0 enabled, others disabled +- **knobs=2**: knob1 enabled, others disabled +- **knobs=3**: knobs 0 and 1 enabled, others disabled + +... + +#### Knob based terminology + +Use the word in SINGULAR (knob), or in plural but referring which ones, when meaning multiple knobs at once: +Examples: +- **knob0**: knob 0 enabled, others disabled +- **knob1**: knob 1 enabled, others disabled +- **knobs 0 and 1**: knobs 0 and 1 enabled, others disabled + +... + +### Examples + +#### Example 1: Conditional Debug Logging + +```cpp +void SomeFunction() { + if (Settings::getDebugKnobAt(0)) { + LOG_DEBUG(Common, "Debug feature 0 is enabled"); + // Additional debug code here + } + + if (Settings::getDebugKnobAt(1)) { + LOG_DEBUG(Common, "Debug feature 1 is enabled"); + // Different debug behavior + } +} +``` + +#### Example 2: Performance Tuning + +```cpp +bool UseOptimizedPath() { + // Skip optimization if debug bit 2 is set for testing + return !Settings::getDebugKnobAt(2); +} +``` + +#### Example 3: Feature Gating + +```cpp +void ExperimentalFeature() { + static constexpr u8 EXPERIMENTAL_FEATURE_BIT = 3; + + if (!Settings::getDebugKnobAt(EXPERIMENTAL_FEATURE_BIT)) { + // Fallback to stable implementation + StableImplementation(); + return; + } + + // Experimental implementation + ExperimentalImplementation(); +} +``` + +### Best Practices + +* This setting is intended for development and testing purposes only +* Knobs must be unwired before PR creation +* The setting is per-game configurable, allowing different debug setups for different titles diff --git a/docs/policies/Coding.md b/docs/policies/Coding.md index c50f93b142..062e0beda4 100644 --- a/docs/policies/Coding.md +++ b/docs/policies/Coding.md @@ -1,126 +1,70 @@ # Coding guidelines -These are mostly "suggestions", if you feel like your code is readable, comprehensible to others; and most importantly doesn't result in unreadable spaghetti you're fine to go. +These are **not** stylistic guidelines, they're, for the most part, suggestions on how to architecture new systems or improve upon the existing codebase. -But for new developers you may find that following these guidelines will make everything x10 easier. +# Foreword -## Naming conventions +Don't try to micro-optimize out of the get go, while yes, most of the code is pretty, subpar, most of these are aftertoughts and details that can be glossed over **generally**. -Simply put, types/classes are named as `PascalCase`, same for methods and functions like `AddElement`. Variables are named `like_this_snake_case` and constants are `IN_SCREAMING_CASE`. +Architectural issues are more important, for example an API returning a `std::string` is not as efficient as one that operates on `std::string_view` directly (cost of constructing an `std::string` w/o small-string optimization and all of that). -Except for Qt MOC where `functionName` is preferred. +Regardless of the details, try to keep things simple. As a general rule of thumb. -Template typenames prefer short names like `T`, `I`, `U`, if a longer name is required either `Iterator` or `perform_action` are fine as well. Do not use names like `SS` as systems like solaris define it for registers, in general do not use any of the following for short names: +# C++ guidelines -- `SS`, `DS`, `GS`, `FS`: Segment registers, defined by Solaris `` -- `EAX`, `EBX`, `ECX`, `EDX`, `ESI`, `EDI`, `ESP`, `EBP`, `EIP`: Registers, defined by Solaris. -- `X`: Defined by some utility headers, avoid. -- `_`: Defined by gettext, avoid. -- `N`, `M`, `S`: Preferably don't use this for types, use it for numeric constants. -- `TR`: Used by some weird `` whom define the Task Register as a logical register to provide to the user... (Need to remember which OS in specific). +Everyone has their own way of viewing good/bad C++ practices, my general outline: -Macros must always be in `SCREAMING_CASE`. Do not use short letter macros as systems like Solaris will conflict with them; a good rule of thumb is >5 characters per macro - i.e `THIS_MACRO_IS_GOOD`, `AND_ALSO_THIS_ONE`. +- At your disposal you may use `boost::container::static_vector<>` (beware it has a ctor/initialization cost which goes up the more elements you add). + - Or you may use `boost::container::small_vector<>` (which has an initialization cost as well, and will use extra book-keeping for heap, try to keep a balance). +- Don't use `[[likely]]` or `[[unlikely]]`; PGO builds exist for that. +- Don't use inline assembly to try to outsmart the compiler unless you're 100% sure the assembly you're writing is actually good. + - And if so, try to restructure your C++ code so the compiler vectorizes it/makes it better, right? + - Or if that fails, use intrinsics instead of raw `asm volatile`. +- Use `std::optional<>` instead of `std::unique_ptr<>` if possible. + - `std::unique_ptr<>` carries indirection cost due to it being memory allocated on the heap. + - It isn't often that objects that contain `std::unique_ptr<>`, are allocated on the heap themselves, allocating even more things on the heap seems redundant. +- Avoid `std::recursive_mutex` at all costs. + - It's basically implemented as a linked list most of the time and has HEAVY performance penalties. +- Exploit the fact `std::atomic/std::atomic` is basically free on most arches that matter. + - In x86_64, an atomic `uint32_t` is basically `mov [m32], r32`, which is essentially free/cheap. +- Avoid template parameters unless you really need them. + - For small inlineable functions this is fine, for more complex ones, please consider the generated assembly. +- Dont make your own memcpy/memset/strcpy/strncpy/etc. + - Seriously DON'T DO THIS. You will NOT beat the compiler. + - Nor 30 years of writing optimized `mem*`. + - If your code is slow, don't blame `mem*`, blame your code. +- Try to avoid using `virtual` since vtable indirection has a cost +- Avoid `dynamic_cast` and `typeid` at all costs. + - The reason is because the project has `-fno-rtti` disabled by default, due to the costs of dynamic polymorphism. +- Always copy-on-value for objects with `sizeof(void *) >= sizeof(T) * 2`, i.e objects sized as 2 pointers or less, for bigger objects you can use ref/pointer as usual. +- Try using move semantics instead of references, whenever possible. +- Remember function parameters are extremelly cheap as fuck, don't be afraid to place upto 8 parameters on a given function. +- Don't save a reference in structures of a parent object, i.e: + ```c++ + struct Child { + Parent& parent; + void Mehod() { + parent.Something(); + } + }; + ``` + - Instead you can do the following: + ```c++ + struct Child { + void Mehod(Parent& parent) { + parent.Something(); + } + }; + ``` + - This reduces the amount of pointers you have lying around, and also works better because of the aforementioned cheapness of parameter functions. -Try not using hungarian notation, if you're able. +# Engineering guidelines -## Formatting +Coding isn't also writing stuff but architecturing stuff, consider the following: -Formatting is extremelly lax, the general rule of thumb is: Don't add new lines just to increase line count. The less lines we have to look at, the better. This means also packing densely your code while not making it a clusterfuck. Strike a balance of "this is a short and comprehensible piece of code" and "my eyes are actually happy to see this!". Don't just drop the entire thing in a single line and call it "dense code", that's just spaghetti posing as code. In general, be mindful of what other devs need to look at. - -Do not put if/while/etc braces after lines: - -```c++ -// no dont do this -// this is more lines of code for no good reason (why braces need their separate lines?) -// and those take space in someone's screen, cumulatively -if (thing) -{ //<-- - some(); // ... -} //<-- 2 lines of code for basically "opening" and "closing" an statment - -// do this -if (thing) { //<-- [...] and with your brain you can deduce it's this piece of code - // that's being closed - some(); // ... -} //<-- only one line, and it's clearer since you know its closing something [...] - -// or this, albeit the extra line isn't needed (at your discretion of course) -if (thing) - some(); // ... - -// this is also ok, keeps things in one line and makes it extremely clear -if (thing) some(); - -// NOT ok, don't be "clever" and use the comma operator to stash a bunch of statments -// in a single line, doing this will definitely ruin someone's day - just do the thing below -// vvv -if (thing) some(), thing(), a2(a1(), y1(), j1()), do_complex_shit(wa(), wo(), ploo()); -// ... and in general don't use the comma operator for "multiple statments", EXCEPT if you think -// that it makes the code more readable (the situation may be rare however) - -// Wow so much clearer! Now I can actually see what each statment is meant to do! -if (thing) { - some(); - thing(); - a2(a1(), y1(), j1()); - do_complex_shit(wa(), wo(), ploo()); -} -``` - -Brace rules are lax, if you can get the point across, do it: - -```c++ -// this is fine -do { - if (thing) { - return 0; - } -} while (other); - -// this is also ok --- albeit a bit more dense -do if (thing) return 0; while (other); - -// ok as well -do { - if (thing) return 0; -} while (other); -``` - -There is no 80-column limit but preferably be mindful of other developer's readability (like don't just put everything onto one line). - -```c++ -// someone is going to be mad due to this -SDL_AudioSpec obtained; -device_name.empty() ? device = SDL_OpenAudioDevice(nullptr, capture, &spec, &obtained, false) : device = SDL_OpenAudioDevice(device_name.c_str(), capture, &spec, &obtained, false); - -// maybe consider this -SDL_AudioSpec obtained; -if (device_name.empty()) { - device = SDL_OpenAudioDevice(nullptr, capture, &spec, &obtained, false); -} else { - device = SDL_OpenAudioDevice(device_name.c_str(), capture, &spec, &obtained, false); -} - -// or this is fine as well -SDL_AudioSpec obtained; -device = SDL_OpenAudioDevice(device_name.empty() ? nullptr : device_name.c_str(), capture, &spec, &obtained, false); -``` - -A note about operators: Use them sparingly, yes, the language is lax on them, but some usages can be... tripping to say the least. - -```c++ -a, b, c; //<-- NOT OK multiple statments with comma operator is definitely a recipe for disaster -return c ? a : b; //<-- OK ternaries at end of return statments are clear and fine -return a, b; //<-- NOT OK return will take value of `b` but also evaluate `a`, just use a separate statment -void f(int a[]) //<-- OK? if you intend to use the pointer as an array, otherwise just mark it as * -``` - -And about templates, use them sparingly, don't just do meta-templating for the sake of it, do it when you actually need it. This isn't a competition to see who can make the most complicated and robust meta-templating system. Just use what works, and preferably stick to the standard libary instead of reinventing the wheel. Additionally: - -```c++ -// NOT OK This will create (T * N * C * P) versions of the same function. DO. NOT. DO. THIS. -template inline void what() const noexcept; - -// OK use parameters like a normal person, don't be afraid to use them :) -template inline void what(size_t n, size_t c, size_t p) const noexcept; -``` +- Try to reduce dependency on... dependencies + - While some dependencies are useful `boost::container` and `fmt` to name a few, remember each dependency added incurs a cost. + - It may also be subpar with a hand rolled implementation, biggest exemplar of this is `spirv-tools` providing subpar SPIRV optimizations in comparison to the in-house optimizer. +- Try to rely less on indirection for architecturing systems + - If the underlying HLE kernel emulation requires it, try making a solution that keeps things local + - For example, there isn't a need for file descriptors to each be a pointer, when they could be a fixed table size with elements that may be emplaced at will. diff --git a/docs/policies/CodingStyle.md b/docs/policies/CodingStyle.md new file mode 100644 index 0000000000..659e8f7384 --- /dev/null +++ b/docs/policies/CodingStyle.md @@ -0,0 +1,126 @@ +# Coding Style guidelines + +These are mostly "suggestions", if you feel like your code is readable, comprehensible to others; and most importantly doesn't result in unreadable spaghetti you're fine to go. + +But for new developers you may find that following these guidelines will make everything x10 easier. + +## Naming conventions + +Simply put, types/classes are named as `PascalCase`, same for methods and functions like `AddElement`. Variables are named `like_this_snake_case` and constants are `IN_SCREAMING_CASE`. + +Except for Qt MOC where `functionName` is preferred. + +Template typenames prefer short names like `T`, `I`, `U`, if a longer name is required either `Iterator` or `perform_action` are fine as well. Do not use names like `SS` as systems like solaris define it for registers, in general do not use any of the following for short names: + +- `SS`, `DS`, `GS`, `FS`: Segment registers, defined by Solaris `` +- `EAX`, `EBX`, `ECX`, `EDX`, `ESI`, `EDI`, `ESP`, `EBP`, `EIP`: Registers, defined by Solaris. +- `X`: Defined by some utility headers, avoid. +- `_`: Defined by gettext, avoid. +- `N`, `M`, `S`: Preferably don't use this for types, use it for numeric constants. +- `TR`: Used by some weird `` whom define the Task Register as a logical register to provide to the user... (Need to remember which OS in specific). + +Macros must always be in `SCREAMING_CASE`. Do not use short letter macros as systems like Solaris will conflict with them; a good rule of thumb is >5 characters per macro - i.e `THIS_MACRO_IS_GOOD`, `AND_ALSO_THIS_ONE`. + +Try not using hungarian notation, if you're able. + +## Formatting + +Formatting is extremelly lax, the general rule of thumb is: Don't add new lines just to increase line count. The less lines we have to look at, the better. This means also packing densely your code while not making it a clusterfuck. Strike a balance of "this is a short and comprehensible piece of code" and "my eyes are actually happy to see this!". Don't just drop the entire thing in a single line and call it "dense code", that's just spaghetti posing as code. In general, be mindful of what other devs need to look at. + +Do not put if/while/etc braces after lines: + +```c++ +// no dont do this +// this is more lines of code for no good reason (why braces need their separate lines?) +// and those take space in someone's screen, cumulatively +if (thing) +{ //<-- + some(); // ... +} //<-- 2 lines of code for basically "opening" and "closing" an statment + +// do this +if (thing) { //<-- [...] and with your brain you can deduce it's this piece of code + // that's being closed + some(); // ... +} //<-- only one line, and it's clearer since you know its closing something [...] + +// or this, albeit the extra line isn't needed (at your discretion of course) +if (thing) + some(); // ... + +// this is also ok, keeps things in one line and makes it extremely clear +if (thing) some(); + +// NOT ok, don't be "clever" and use the comma operator to stash a bunch of statments +// in a single line, doing this will definitely ruin someone's day - just do the thing below +// vvv +if (thing) some(), thing(), a2(a1(), y1(), j1()), do_complex_shit(wa(), wo(), ploo()); +// ... and in general don't use the comma operator for "multiple statments", EXCEPT if you think +// that it makes the code more readable (the situation may be rare however) + +// Wow so much clearer! Now I can actually see what each statment is meant to do! +if (thing) { + some(); + thing(); + a2(a1(), y1(), j1()); + do_complex_shit(wa(), wo(), ploo()); +} +``` + +Brace rules are lax, if you can get the point across, do it: + +```c++ +// this is fine +do { + if (thing) { + return 0; + } +} while (other); + +// this is also ok --- albeit a bit more dense +do if (thing) return 0; while (other); + +// ok as well +do { + if (thing) return 0; +} while (other); +``` + +There is no 80-column limit but preferably be mindful of other developer's readability (like don't just put everything onto one line). + +```c++ +// someone is going to be mad due to this +SDL_AudioSpec obtained; +device_name.empty() ? device = SDL_OpenAudioDevice(nullptr, capture, &spec, &obtained, false) : device = SDL_OpenAudioDevice(device_name.c_str(), capture, &spec, &obtained, false); + +// maybe consider this +SDL_AudioSpec obtained; +if (device_name.empty()) { + device = SDL_OpenAudioDevice(nullptr, capture, &spec, &obtained, false); +} else { + device = SDL_OpenAudioDevice(device_name.c_str(), capture, &spec, &obtained, false); +} + +// or this is fine as well +SDL_AudioSpec obtained; +device = SDL_OpenAudioDevice(device_name.empty() ? nullptr : device_name.c_str(), capture, &spec, &obtained, false); +``` + +A note about operators: Use them sparingly, yes, the language is lax on them, but some usages can be... tripping to say the least. + +```c++ +a, b, c; //<-- NOT OK multiple statments with comma operator is definitely a recipe for disaster +return c ? a : b; //<-- OK ternaries at end of return statments are clear and fine +return a, b; //<-- NOT OK return will take value of `b` but also evaluate `a`, just use a separate statment +void f(int a[]) //<-- OK? if you intend to use the pointer as an array, otherwise just mark it as * +``` + +And about templates, use them sparingly, don't just do meta-templating for the sake of it, do it when you actually need it. This isn't a competition to see who can make the most complicated and robust meta-templating system. Just use what works, and preferably stick to the standard libary instead of reinventing the wheel. Additionally: + +```c++ +// NOT OK This will create (T * N * C * P) versions of the same function. DO. NOT. DO. THIS. +template inline void what() const noexcept; + +// OK use parameters like a normal person, don't be afraid to use them :) +template inline void what(size_t n, size_t c, size_t p) const noexcept; +``` diff --git a/docs/user/AddingBooleanToggles.md b/docs/user/AddingBooleanToggles.md deleted file mode 100644 index 1af5fd932d..0000000000 --- a/docs/user/AddingBooleanToggles.md +++ /dev/null @@ -1,159 +0,0 @@ -# User Handbook - Adding Boolean Settings Toggles - -> [!WARNING] -> This guide is intended for developers ONLY. If you are not a developer, this likely irrelevant to yourself. -> -> If you want to add temporary toggles, please refer to **[Adding Debug Knobs](AddingDebugKnobs.md)** - -This guide will walk you through adding a new boolean toggle setting to Eden's configuration across both Qt's (PC) and Kotlin's (Android) UIs. - -## Index - -1. [Step 1 - Common Setting](#step-1-common-setting) -2. [Step 2 - Qt Toggle](#step-2-qt-toggle) -3. [Step 3 - Kotlin (Android)](#step-3-kotlin-android) - - * [Step 3.1 - BooleanSetting.kt](#step-3-1-booleansetting-kt) - * [Step 3.2 - SettingsItem.kt](#step-3-2-settingsitem-kt) - * [Step 3.3 - SettingsFragmentPresenter.kt](#step-3-3-settingsfragmentpresenter-kt) - * [Step 3.4 - Localization](#step-3-4-localization) -4. [Step 4 - Use Your Toggle](#step-4-use-your-toggle) -5. [Best Practices](#best-practices) - ---- - -## Step 1 - Common Setting - -Firstly add your desired toggle: - -Example: `src/common/setting.h` -```cpp -SwitchableSetting your_setting_name{linkage, false, "your_setting_name", Category::RendererExtensions}; -``` - -### Remember to add your toggle to the appropriate category, for example: - -Common Categories: - -* Category::Renderer -* Category::RendererAdvanced -* Category::RendererExtensions -* Category::System -* Category::Core - -> [!WARNING] -> If you wish for your toggle to be `on by default` then change `false` to `true` after `linkage,`. - ---- - -## Step 2 - Qt Toggle - -Add the toggle to the Qt UI, where you wish for it to appear and place it there. - -Example: `src/qt_common/config/shared_translation.cpp` -```cpp -INSERT(Settings, - your_setting_name, - tr("Your Setting Display Name"), - tr("Detailed description of what this setting does.\n" - "You can use multiple lines.\n" - "Explain any caveats or requirements.")); -``` - -### Make sure to: - -* Keep display naming consistant -* Put detailed info in the description -* Use `\n` for line breaks in descriptions - ---- - -## Step 3 - Kotlin (Android) - -### Step 3.1 - BooleanSetting.kt - -Add where it should be in the settings. - -Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt` -```kts -RENDERER_YOUR_SETTING_NAME("your_setting_name"), -``` - -### Make sure to: - -* Ensure the prefix naming matches the intended category. - ---- - -### Step 3.2 - SettingsItem.kt - -Add the toggle to the Kotlin (Android) UI - -Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt` -```kts -put( - SwitchSetting( - BooleanSetting.RENDERER_YOUR_SETTING_NAME, - titleId = R.string.your_setting_name, - descriptionId = R.string.your_setting_name_description - ) -) -``` - ---- - -### Step 3.3 - SettingsFragmentPresenter.kt - -Add your setting within the right category. - -Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt` -```kts -add(BooleanSetting.RENDERER_YOUR_SETTING_NAME.key) -``` - -> [!WARNING] -> Remember, placing matters! Settings appear in the order of where you add them. - ---- - -### Step 3.4 - Localization - -Add your setting and description in the appropriate place. - -Example: `src/android/app/src/main/res/values/strings.xml` -```xml -Your Setting Display Name -Detailed description of what this setting does. Explain any caveats, requirements, or warnings here. -``` - ---- - -## Step 4 - Use Your Toggle! - -Now the UI part is done find a place in the code for the toggle, -And use it to your heart's desire! - -Example: -```cpp -const bool your_value = Settings::values.your_setting_name.GetValue(); - -if (your_value) { - // Do something when enabled -} -``` - -If you wish to do something only when the toggle is disabled, -Use `if (!your_value) {` instead of `if (your_value) {`. - ---- - -## Best Practices - -* Naming - Use clear, descriptive names. Something for both the devs and the users. -* Defaults - Choose safe default values (usually false for new features). -* Documentation - Write clear descriptions explaining when and why to use the setting. -* Categories - Put settings in the appropriate category. -* Order - Place related settings near each other. -* Testing - Always test on both PC and Android before committing when possible. - -### Thank you for reading, I hope this guide helped you making your toggle! diff --git a/docs/user/AddingDebugKnobs.md b/docs/user/AddingDebugKnobs.md deleted file mode 100644 index 83db52b375..0000000000 --- a/docs/user/AddingDebugKnobs.md +++ /dev/null @@ -1,167 +0,0 @@ -# User Handbook - Adding Debug Knobs - -Debug Knobs is a 16-bit integer setting (`debug_knobs`) in the Eden Emulator that serves as a bitmask for gating various testing and debugging features. This allows developers and advanced users to enable or disable specific debug behaviors without requiring deploying of complete but temporary toggles. - -The setting ranges from 0 to 65535 (0x0000 to 0xFFFF), where each bit represents a different debug feature flag. - -## Index - -1. [Advantages](#advantages) -2. [Usage](#usage) - - * [Accessing Debug Knobs (dev side)](#accessing-debug-knobs-dev-side) - * [Setting Debug Knobs (user side)](#setting-debug-knobs-user-side) - * [Bit Manipulation Examples](#bit-manipulation-examples) -3. [Terminology and user communication](#terminology-and-user-communication) -4. [Examples](#examples) - - * [Example 1: Conditional Debug Logging](#example-1-conditional-debug-logging) - * [Example 2: Performance Tuning](#example-2-performance-tuning) - * [Example 3: Feature Gating](#example-3-feature-gating) -5. [Best Practices](#best-practices) - ---- - -## Advantages - -The main advantage is to avoid deploying new disposable toggles (those made only for testing stage, and are disposed once new feature gets good to merge). This empowers devs to be free of all frontend burocracy and hassle of new toggles. - -Common advantages recap: - -* **Fine-Grained Control**: Enable or disable up to 16 individual debug features independently using bit manipulation on a single build -* **Runtime Configuration**: Change debug behavior at runtime the same way as new toggles would do -* **Safe incremental development**: New debug features can be added while impact can be isolated from previous deployments - -## Usage - -### Accessing Debug Knobs (dev side) - -Use the `Settings::getDebugKnobAt(u8 i)` function to check if a specific bit is set: - -```cpp -//cpp side -#include "common/settings.h" - -// Check if bit 0 is set -bool feature_enabled = Settings::getDebugKnobAt(0); - -// Check if bit 15 is set -bool another_feature = Settings::getDebugKnobAt(15); -``` - -```kts -//kotlin side -import org.yuzu.yuzu_emu.features.settings.model.Settings - -// Check if bit x is set -bool feature_enabled = Settings.getDebugKnobAt(x); //x as integer from 0 to 15 -``` - -The function returns `true` if the specified bit (0-15) is set in the `debug_knobs` value, `false` otherwise. - -### Setting Debug Knobs (user side) - -Developers must inform which knobs are tied to each functionality to be tested. - -The debug knobs value can be set through: - -1. **Desktop UI**: In the Debug configuration tab, there's a spinbox for "Debug knobs" (0-65535) -2. **Android UI**: Available as an integer setting in the Debug section -3. **Configuration Files**: Set the `debug_knobs` value in the emulator's configuration - -### Bit Manipulation Examples - -To enable specific features, calculate the decimal value by setting the appropriate bits: - -* **Enable only bit 0**: Value = 1 (2^0) -* **Enable only bit 1**: Value = 2 (2^1) -* **Enable bits 0 and 1**: Value = 3 (2^0 + 2^1) -* **Enable bit 15**: Value = 32768 (2^15) - -## Terminology and user communication - -There are two main confusions when talking about knobs: - -### Whether it's zero-based or one-based - -Sometimes when an user reports: knobs 1 and 2 gets better performance, dev may get confuse whether he means the knobs 1 and 2 literally, or the 1st and 2nd knobs (knobs 0 and 1). - -Debug knobs are **zero-based**, which means: -* The first knob is the knob(0) (or knob0 henceforth), and the last one is the 15 (knob15, likewise) -* You can talk: "knob0 is enabled/disabled", "In this video i was using only knobs 0 and 2", etc. - -### Whether one is talking about the knob itself or about the entire parameter value (which represents all knobs) - -Sometimes when an user reports: knob 3 results, it's unclear whether he's referring to knob setting with value 3 (which means both knob 0 and 1 are enabled), or to knob(3) specifically. -Whenever you're instructing tests or reporting results, be precise about whether one you're talking to avoid confusion: - -### Setting based terminology - -ALWAYS use the word in PLURAL (knobs), without mentioning which one, to refer to the setting, aka multiple knobs at once: -Examples: -- **knobs=0**: no knobs enabled -- **knobs=1**: knob0 enabled, others disabled -- **knobs=2**: knob1 enabled, others disabled -- **knobs=3**: knobs 0 and 1 enabled, others disabled - -... - -### Knob based terminology - -Use the word in SINGULAR (knob), or in plural but referring which ones, when meaning multiple knobs at once: -Examples: -- **knob0**: knob 0 enabled, others disabled -- **knob1**: knob 1 enabled, others disabled -- **knobs 0 and 1**: knobs 0 and 1 enabled, others disabled - -... - -## Examples - -### Example 1: Conditional Debug Logging - -```cpp -void SomeFunction() { - if (Settings::getDebugKnobAt(0)) { - LOG_DEBUG(Common, "Debug feature 0 is enabled"); - // Additional debug code here - } - - if (Settings::getDebugKnobAt(1)) { - LOG_DEBUG(Common, "Debug feature 1 is enabled"); - // Different debug behavior - } -} -``` - -### Example 2: Performance Tuning - -```cpp -bool UseOptimizedPath() { - // Skip optimization if debug bit 2 is set for testing - return !Settings::getDebugKnobAt(2); -} -``` - -### Example 3: Feature Gating - -```cpp -void ExperimentalFeature() { - static constexpr u8 EXPERIMENTAL_FEATURE_BIT = 3; - - if (!Settings::getDebugKnobAt(EXPERIMENTAL_FEATURE_BIT)) { - // Fallback to stable implementation - StableImplementation(); - return; - } - - // Experimental implementation - ExperimentalImplementation(); -} -``` - -## Best Practices - -* This setting is intended for development and testing purposes only -* Knobs must be unwired before PR creation -* The setting is per-game configurable, allowing different debug setups for different titles diff --git a/docs/user/Architectures.md b/docs/user/Architectures.md index 45f9e85c4f..a03dbc0da8 100644 --- a/docs/user/Architectures.md +++ b/docs/user/Architectures.md @@ -40,7 +40,7 @@ Windows/riscv64 doesn't exist, and may never (until corporate greed no longer co Android/riscv64 is interesting. While support for it may be added if and when RISC-V phones/handhelds ever go mainstream, arm64 devices will always be preferred due to NCE. -Only Fedora/riscv64 has been tested, but in theory, every riscv64 distribution that has *at least* the standard build tools, Qt, FFmpeg, and SDL2 should work. +Only Fedora/riscv64 has been tested, but in theory, every riscv64 distribution that has *at least* the standard build tools, Qt, FFmpeg, and SDL3 should work. ## Other diff --git a/docs/user/CFW.md b/docs/user/CFW.md index ea224d3d36..8f37672eb1 100644 --- a/docs/user/CFW.md +++ b/docs/user/CFW.md @@ -9,3 +9,11 @@ At the moment of writing, we do not support CFW such as Atmosphere, due to: We do however, maintain HLE compatibility with the former mentioned CFW, applications that require Atmosphere to run will run fine in the emulator without any adjustments. If they don't run - then that's a bug! + +## Atmosphere + +Fusee Galee, the bootloader and other low-level mechanisms are not emulated at the moment. + +Having OFW is recommended, but may not be required (untested). + +Extract the contents of Atmosphere into `sdmc`. Then to launch simply use `-hlaunch` instead (orthogonal to `-qlaunch`). diff --git a/docs/user/CommandLine.md b/docs/user/CommandLine.md index cd98d88b19..6e1f0f237c 100644 --- a/docs/user/CommandLine.md +++ b/docs/user/CommandLine.md @@ -1,17 +1,22 @@ # User Handbook - Command Line -There are two main applications, an SDL2 based app (`eden-cli`) and a Qt based app (`eden`); both accept command line arguments. +There are two main applications, an SDL-based app (`eden-cli`) and a Qt based app (`eden`); both accept command line arguments. ## eden + - `./eden `: Running with a single argument and nothing else, will make the emulator look for the given file and load it, this behaviour is similar to `eden-cli`; allows dragging and dropping games into the application. - `-g `: Alternate way to specify what to load, overrides. However let it be noted that arguments that use `-` will be treated as options/ignored, if your game, for some reason, starts with `-`, in order to safely handle it you may need to specify it as an argument. - `-f`: Use fullscreen. - `-u `: Select the index of the user to load as. - `-input-profile `: Specifies input profile name to use (for player #0 only). - `-qlaunch`: Launch QLaunch. +- `-hlaunch`: Launch homebrew launcher `nx-hbloader`. + - Requires a copy of Atmosphere to be extracted onto `sdmc`. + - This is a shorthand for `/sdmc/atmosphere/hbl.nsp`. - `-setup`: Launch setup applet. ## eden-cli + - `--debug/-d`: Enter debug mode, allow gdb stub at port `1234` - `--config/-c`: Specify alternate configuration file. - `--fullscreen/-f`: Set fullscreen. diff --git a/docs/user/Graphics.md b/docs/user/Graphics.md index ad359b9049..4d3b1da3c7 100644 --- a/docs/user/Graphics.md +++ b/docs/user/Graphics.md @@ -44,6 +44,10 @@ Various graphical filters exist - each of them aimed at a specific target/image - **MMPX**: Nearest-neighbour filter aimed at providing higher pixel-art quality. - **Pros**: Offers decent pixel-art upscaling. - **Cons**: Only works for pixel-art. +- **SGSR**: Uses Snapdragon Studios Game Super Resolution to enhance image quality (similar to FSR, but for Adreno devices). + - **Pros**: Optimized for Adreno devices. + - **Cons**: Doesn't play nicely with non-Adreno devices. +- **SGSR Edge**: Almost the same pipeline as SGSR, but with improved edge detection. ### Anisotropy values diff --git a/docs/user/Multiplayer.md b/docs/user/Multiplayer.md index 57c4495f46..40e5dedc19 100644 --- a/docs/user/Multiplayer.md +++ b/docs/user/Multiplayer.md @@ -1,4 +1,4 @@ -# Multiplayer +# User Handbook - Multiplayer Use this guide to answer questions regarding and to start using the multiplayer functionality of Eden. ## Multiplayer FAQ diff --git a/docs/user/README.md b/docs/user/README.md index c1c4cd200a..0b1a434a37 100644 --- a/docs/user/README.md +++ b/docs/user/README.md @@ -25,12 +25,12 @@ A copy of this handbook is [available online](https://git.eden-emu.dev/eden-emu/ - **[Importing Saves](./ImportingSaves.md)** - **[Installing Atmosphere Mods](./InstallingAtmosphereMods.md)** - **[Installing Updates & DLCs](./InstallingUpdatesDLC.md)** -- **[Alter Date & Time](./AlterDateTime.md)** +- **[Multiplayer](./Multiplayer.md)** ## 3rd-party Integration - **[Configuring Steam ROM Manager](./SteamROM.md)** -- **[Server hosting](ServerHosting.md)** +- **[Server hosting](./ServerHosting.md)** - **[Syncthing Guide](./SyncthingGuide.md)** - **[Third Party](./ThirdParty.md)** - **[Obtainium](./ThirdParty.md#configuring-obtainium)** @@ -40,12 +40,13 @@ A copy of this handbook is [available online](https://git.eden-emu.dev/eden-emu/ ## Advanced +- **[Command Line](./CommandLine.md)** - **[Custom Firmware](./CFW.md)** +- **[Alter Date & Time](./AlterDateTime.md)** - **[How To Access Logs](./HowToAccessLogs.md)** - **[Gyro Controls](./GyroControls.md)** -- **[Platforms and Architectures](Architectures.md)** -- **[Command Line](CommandLine.md)** -- **[Native Application Development](Native.md)** -- **[Adding Boolean Settings Toggles](AddingBooleanToggles.md)** +- **[Platforms and Architectures](./Architectures.md)** +- **[Native Application Development](./Native.md)** +- **[Adding Boolean Settings Toggles](./AddingBooleanToggles.md)** - **[Adding Debug Knobs](./AddingDebugKnobs.md)** -- **[Testing](Testing.md)** +- **[Testing](./Testing.md)** diff --git a/docs/user/Settings.md b/docs/user/Settings.md index 9153a27e4d..a71972934c 100644 --- a/docs/user/Settings.md +++ b/docs/user/Settings.md @@ -30,7 +30,6 @@ Before touching the settings, please see the game boots with stock options. We t ## CPU -- `CPU/Virtual table bouncing`: Some games have the tendency to crash on loading due to an indirect bad jump (Pokemon ZA being the worst offender); this option lies to the game and tells it to just pretend it never executed a given function. This is fine for most casual users, but developers of switch applications **must** disable this. This temporary "hack" should hopefully be gone in 6-7 months from now on. - `Fastmem`, aka. `CPU/Enable Host MMU`: Enables "fastmem"; a detailed description of fastmem can be found [here](../dynarmic/Design.md#fast-memory-fastmem). - `CPU/Unsafe FMA`: Enables deliberate innacurate FMA behaviour which may affect how FMA returns any given operation - this may introduce tiny floating point errors which can cascade in sensitive code (i.e FFmpeg). - `CPU/Faster FRSQRTE and FRECPE`: Introduces accuracy errors on square root and reciprocals in exchange for less checks - this introduces inaccuracies with some cases but it's mostly safe. diff --git a/docs/user/SteamROM.md b/docs/user/SteamROM.md index a782b51969..2ea069bf09 100644 --- a/docs/user/SteamROM.md +++ b/docs/user/SteamROM.md @@ -59,7 +59,7 @@ EmuDeck will automatically create an *Emulators - Emulators* parser for ***Steam 4. Paste the following code into the contents of the file, save and close the file. ```bash - #!/bin/bash + #!/bin/sh -e emuName="eden" #parameterize me . "$HOME/.config/EmuDeck/backend/functions/all.sh" diff --git a/docs/user/ThirdParty.md b/docs/user/ThirdParty.md index 5bd72ebe72..1dd359cec3 100644 --- a/docs/user/ThirdParty.md +++ b/docs/user/ThirdParty.md @@ -13,6 +13,7 @@ The main origin repository is always at https://git.eden-emu.dev/eden-emu/eden. - https://github.com/eden-emulator/mirror - https://git.crueter.xyz/mirror/eden +- https://codeberg.org/eden-emu/eden - https://collective.taymaerz.de/eden/eden Other mirrors obviously exist on the internet, but we can't guarantee their reliability and/or availability. diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index e230f709fa..aafa518ed2 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -6,8 +6,7 @@ # TODO(crueter): A lot of this should be moved to the root. # otherwise we have to do weird shenanigans with library linking and stuff - -include(CPMUtil) +# Or just add a CPMUtil thing to propagate packages # Explicitly declare this option here to propagate to the oaknut CPM call option(DYNARMIC_TESTS "Build tests" ${BUILD_TESTING}) @@ -49,8 +48,8 @@ if (NOT TARGET stb::headers) add_library(stb::headers ALIAS stb) endif() -# ItaniumDemangle -if (NOT TARGET LLVM::Demangle) +# ItaniumDemangle (Windows only) +if (WIN32 AND NOT TARGET LLVM::Demangle) add_library(demangle demangle/ItaniumDemangle.cpp) target_include_directories(demangle PUBLIC ./demangle) if (NOT MSVC) @@ -82,6 +81,11 @@ if (ARCHITECTURE_riscv64) AddJsonPackage(biscuit) endif() +# Lagoon +if (ARCHITECTURE_loongarch64) + AddJsonPackage(lagoon) +endif() + # Vulkan stuff AddDependentPackages(vulkan-headers vulkan-utility-libraries) @@ -136,8 +140,7 @@ if(ENABLE_CUBEB) endif() if (NOT ANDROID) - if (YUZU_USE_EXTERNAL_SDL2) - message(STATUS "Using SDL2 from externals.") + if (NOT YUZU_USE_BUNDLED_SDL3) if (NOT WIN32) # Yuzu itself needs: Atomic Audio Events Joystick Haptic Sensor Threads Timers # Since 2.0.18 Atomic+Threads required for HIDAPI/libusb (see https://github.com/libsdl-org/SDL/issues/5095) @@ -158,21 +161,26 @@ if (NOT ANDROID) set(SDL_FILE ON) endif() - if ("${YUZU_SYSTEM_PROFILE}" STREQUAL "steamdeck") - set(SDL_PIPEWIRE OFF) # build errors out with this on - AddJsonPackage("sdl2_steamdeck") - else() - AddJsonPackage("sdl2_generic") - endif() - elseif (YUZU_USE_BUNDLED_SDL2) - message(STATUS "Using bundled SDL2") + AddJsonPackage(sdl3) + else() + message(STATUS "Using bundled SDL3") if (PLATFORM_FREEBSD) set(BUILD_SHARED_LIBS ON) endif() - AddJsonPackage(sdl2) + AddJsonPackage(sdl3-ci) endif() - find_package(SDL2 2.26.4 REQUIRED) + # Normalize SDL3 link target across package variants. + # Some SDL3 packages export only SDL3::SDL3-shared or SDL3::SDL3-static. + if (NOT TARGET SDL3::SDL3) + if (TARGET SDL3::SDL3-shared) + add_library(SDL3::SDL3 ALIAS SDL3::SDL3-shared) + elseif (TARGET SDL3::SDL3-static) + add_library(SDL3::SDL3 ALIAS SDL3::SDL3-static) + else() + message(FATAL_ERROR "SDL3 package found, but no usable SDL3 target was exported") + endif() + endif() endif() set(BUILD_SHARED_LIBS OFF) diff --git a/externals/cpmfile.json b/externals/cpmfile.json deleted file mode 100644 index 0f8d34230c..0000000000 --- a/externals/cpmfile.json +++ /dev/null @@ -1,254 +0,0 @@ -{ - "vulkan-memory-allocator": { - "package": "VulkanMemoryAllocator", - "repo": "GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator", - "tag": "v%VERSION%", - "hash": "deb5902ef8db0e329fbd5f3f4385eb0e26bdd9f14f3a2334823fb3fe18f36bc5d235d620d6e5f6fe3551ec3ea7038638899db8778c09f6d5c278f5ff95c3344b", - "find_args": "CONFIG", - "git_version": "3.3.0" - }, - "sirit": { - "repo": "eden-emulator/sirit", - "git_version": "1.0.4", - "tag": "v%VERSION%", - "artifact": "sirit-source-%VERSION%.tar.zst", - "hash_suffix": "sha512sum", - "find_args": "CONFIG", - "options": [ - "SIRIT_USE_SYSTEM_SPIRV_HEADERS ON" - ] - }, - "sirit-ci": { - "ci": true, - "package": "sirit", - "name": "sirit", - "repo": "eden-emulator/sirit", - "version": "1.0.4" - }, - "httplib": { - "repo": "yhirose/cpp-httplib", - "tag": "v%VERSION%", - "hash": "5efa8140aadffe105dcf39935b732476e95755f6c7473ada3d0b64df2bc02c557633ae3948a25b45e1cf67e89a3ff6329fb30362e4ac033b9a1d1e453aa2eded", - "git_version": "0.37.0", - "version": "0.18.7", - "find_args": "MODULE GLOBAL", - "patches": [ - "0001-mingw.patch", - "0002-fix-zstd.patch" - ], - "options": [ - "HTTPLIB_REQUIRE_OPENSSL ON", - "HTTPLIB_DISABLE_MACOSX_AUTOMATIC_ROOT_CERTIFICATES ON" - ] - }, - "cpp-jwt": { - "version": "1.4", - "repo": "arun11299/cpp-jwt", - "sha": "7f24eb4c32", - "hash": "d11cbd5ddb3197b4c5ca15679bcd76a49963e7b530b7dd132db91e042925efa20dfb2c24ccfbe7ef82a7012af80deff0f72ee25851312ae80381a462df8534b8", - "find_args": "CONFIG", - "options": [ - "CPP_JWT_USE_VENDORED_NLOHMANN_JSON OFF" - ], - "patches": [ - "0001-fix-missing-decl.patch" - ] - }, - "xbyak": { - "package": "xbyak", - "repo": "herumi/xbyak", - "tag": "v%VERSION%", - "hash": "b6475276b2faaeb315734ea8f4f8bd87ededcee768961b39679bee547e7f3e98884d8b7851e176d861dab30a80a76e6ea302f8c111483607dde969b4797ea95a", - "git_version": "7.35.2" - }, - "oaknut": { - "repo": "eden-emulator/oaknut", - "version": "2.0.1", - "git_version": "2.0.3", - "tag": "v%VERSION%", - "hash": "9697e80a7d5d9bcb3ce51051a9a24962fb90ca79d215f1f03ae6b58da8ba13a63b5dda1b4dde3d26ac6445029696b8ef2883f4e5a777b342bba01283ed293856" - }, - "libadrenotools": { - "repo": "eden-emulator/libadrenotools", - "sha": "8ba23b42d7", - "hash": "f6526620cb752876edc5ed4c0925d57b873a8218ee09ad10859ee476e9333259784f61c1dcc55a2bcba597352d18aff22cd2e4c1925ec2ae94074e09d7da2265", - "patches": [ - "0001-linkerns-cpm.patch" - ] - }, - "oboe": { - "repo": "google/oboe", - "tag": "%VERSION%", - "hash": "ce4011afe7345370d4ead3b891cd69a5ef224b129535783586c0ca75051d303ed446e6c7f10bde8da31fff58d6e307f1732a3ffd03b249f9ef1fd48fd4132715", - "git_version": "1.10.0", - "bundled": true - }, - "unordered-dense": { - "package": "unordered_dense", - "repo": "martinus/unordered_dense", - "sha": "7b55cab841", - "hash": "d2106f6640f6bfb81755e4b8bfb64982e46ec4a507cacdb38f940123212ccf35a20b43c70c6f01d7bfb8c246d1a16f7845d8052971949cea9def1475e3fa02c8", - "find_args": "CONFIG", - "bundled": true, - "patches": [ - "0001-avoid-memset-when-clearing-an-empty-table.patch" - ] - }, - "enet": { - "repo": "lsalzman/enet", - "tag": "v%VERSION%", - "hash": "a0d2fa8c957704dd49e00a726284ac5ca034b50b00d2b20a94fa1bbfbb80841467834bfdc84aa0ed0d6aab894608fd6c86c3b94eee46343f0e6d9c22e391dbf9", - "version": "1.3", - "git_version": "1.3.18", - "find_args": "MODULE" - }, - "spirv-headers": { - "package": "SPIRV-Headers", - "repo": "KhronosGroup/SPIRV-Headers", - "sha": "04f10f650d", - "hash": "cae8cd179c9013068876908fecc1d158168310ad6ac250398a41f0f5206ceff6469e2aaeab9c820bce9d1b08950c725c89c46e94b89a692be9805432cf749396", - "options": [ - "SPIRV_WERROR OFF" - ] - }, - "cubeb": { - "repo": "mozilla/cubeb", - "sha": "fa02160712", - "hash": "8a4bcb2f83ba590f52c66626e895304a73eb61928dbc57777e1822e55378e3568366f17f9da4b80036cc2ef4ea9723c32abf6e7d9bbe00fb03654f0991596ab0", - "find_args": "CONFIG", - "options": [ - "USE_SANITIZERS OFF", - "BUILD_TESTS OFF", - "BUILD_TOOLS OFF", - "BUNDLE_SPEEX ON" - ] - }, - "sdl2": { - "ci": true, - "package": "SDL2", - "name": "SDL2", - "repo": "crueter-ci/SDL2", - "version": "2.32.10-3c28e8ecc0", - "min_version": "2.26.4" - }, - "catch2": { - "package": "Catch2", - "repo": "catchorg/Catch2", - "tag": "v%VERSION%", - "hash": "7eea385d79d88a5690cde131fe7ccda97d5c54ea09d6f515000d7bf07c828809d61c1ac99912c1ee507cf933f61c1c47ecdcc45df7850ffa82714034b0fccf35", - "version": "3.0.1", - "git_version": "3.13.0", - "patches": [ - "0001-solaris-isnan-fix.patch" - ] - }, - "discord-rpc": { - "package": "DiscordRPC", - "repo": "eden-emulator/discord-rpc", - "sha": "0d8b2d6a37", - "hash": "8213c43dcb0f7d479f5861091d111ed12fbdec1e62e6d729d65a4bc181d82f48a35d5fd3cd5c291f2393ac7c9681eabc6b76609755f55376284c8a8d67e148f3", - "find_args": "MODULE" - }, - "simpleini": { - "package": "SimpleIni", - "repo": "brofield/simpleini", - "tag": "v%VERSION%", - "hash": "b937c18a7b6277d77ca7ebfb216af4984810f77af4c32d101b7685369a4bd5eb61406223f82698e167e6311a728d07415ab59639fdf19eff71ad6dc2abfda989", - "find_args": "MODULE", - "git_version": "4.25" - }, - "sdl2_generic": { - "package": "SDL2", - "repo": "libsdl-org/SDL", - "tag": "release-%VERSION%", - "hash": "d5622d6bb7266f7942a7b8ad43e8a22524893bf0c2ea1af91204838d9b78d32768843f6faa248757427b8404b8c6443776d4afa6b672cd8571a4e0c03a829383", - "bundled": true, - "git_version": "2.32.10", - "skip_updates": true - }, - "sdl2_steamdeck": { - "package": "SDL2", - "repo": "libsdl-org/SDL", - "sha": "cc016b0046", - "hash": "b8d9873446cdb922387471df9968e078714683046674ef0d0edddf8e25da65a539a3bae83d635496b970237f90b07b36a69f8d7855d450de59311d6d6e8c3dbc", - "bundled": true, - "skip_updates": "true" - }, - "moltenvk": { - "repo": "V380-Ori/Ryujinx.MoltenVK", - "tag": "v%VERSION%-ryujinx", - "git_version": "1.4.1", - "artifact": "MoltenVK-macOS.tar", - "hash": "5695b36ca5775819a71791557fcb40a4a5ee4495be6b8442e0b666d0c436bec02aae68cc6210183f7a5c986bdbec0e117aecfad5396e496e9c2fd5c89133a347", - "bundled": true - }, - "gamemode": { - "repo": "FeralInteractive/gamemode", - "sha": "ce6fe122f3", - "hash": "e87ec14ed3e826d578ebf095c41580069dda603792ba91efa84f45f4571a28f4d91889675055fd6f042d7dc25b0b9443daf70963ae463e38b11bcba95f4c65a9", - "version": "1.7", - "find_args": "MODULE" - }, - "biscuit": { - "repo": "lioncash/biscuit", - "tag": "v%VERSION%", - "hash": "1229f345b014f7ca544dedb4edb3311e41ba736f9aa9a67f88b5f26f3c983288c6bb6cdedcfb0b8a02c63088a37e6a0d7ba97d9c2a4d721b213916327cffe28a", - "version": "0.9.1", - "git_version": "0.19.0" - }, - "libusb": { - "repo": "libusb/libusb", - "tag": "v%VERSION%", - "hash": "98c5f7940ff06b25c9aa65aa98e23de4c79a4c1067595f4c73cc145af23a1c286639e1ba11185cd91bab702081f307b973f08a4c9746576dc8d01b3620a3aeb5", - "find_args": "MODULE", - "git_version": "1.0.29", - "patches": [ - "0001-netbsd-gettime.patch" - ] - }, - "ffmpeg": { - "repo": "FFmpeg/FFmpeg", - "sha": "c7b5f1537d", - "hash": "ed177621176b3961bdcaa339187d3a7688c1c8b060b79c4bb0257cbc67ad7021ae5d5adca5303b45625abbbe3d9aafdd87ce777b8690ac295290d744c875489a", - "bundled": true - }, - "ffmpeg-ci": { - "ci": true, - "package": "FFmpeg", - "name": "ffmpeg", - "repo": "crueter-ci/FFmpeg", - "version": "8.0.1-c7b5f1537d", - "min_version": "4.1" - }, - "tzdb": { - "package": "nx_tzdb", - "repo": "eden-emu/tzdb_to_nx", - "git_host": "git.eden-emu.dev", - "artifact": "%VERSION%.tar.gz", - "tag": "%VERSION%", - "hash": "cce65a12bf90f4ead43b24a0b95dfad77ac3d9bfbaaf66c55e6701346e7a1e44ca5d2f23f47ee35ee02271eb1082bf1762af207aad9fb236f1c8476812d008ed", - "version": "121125", - "git_version": "230326" - }, - "vulkan-headers": { - "repo": "KhronosGroup/Vulkan-Headers", - "package": "VulkanHeaders", - "version": "1.4.317", - "hash": "d2846ea228415772645eea4b52a9efd33e6a563043dd3de059e798be6391a8f0ca089f455ae420ff22574939ed0f48ed7c6ff3d5a9987d5231dbf3b3f89b484b", - "git_version": "1.4.345", - "tag": "v%VERSION%" - }, - "vulkan-utility-libraries": { - "repo": "KhronosGroup/Vulkan-Utility-Libraries", - "package": "VulkanUtilityLibraries", - "hash": "114f6b237a6dcba923ccc576befb5dea3f1c9b3a30de7dc741f234a831d1c2d52d8a224afb37dd57dffca67ac0df461eaaab6a5ab5e503b393f91c166680c3e1", - "git_version": "1.4.345", - "tag": "v%VERSION%" - }, - "frozen": { - "package": "frozen", - "repo": "serge-sans-paille/frozen", - "sha": "61dce5ae18", - "hash": "b8dfe741c82bc178dfc9749d4ab5a130cee718d9ee7b71d9b547cf5f7f23027ed0152ad250012a8546399fcc1e12187efc68d89d6731256c4d2df7d04eef8d5c" - } -} diff --git a/externals/ffmpeg/CMakeLists.txt b/externals/ffmpeg/CMakeLists.txt index 2d85876ad2..7908e0a619 100644 --- a/externals/ffmpeg/CMakeLists.txt +++ b/externals/ffmpeg/CMakeLists.txt @@ -59,7 +59,7 @@ endif() if (PLATFORM_PS4 OR PLATFORM_MANAGARM) # Doesn't support VA-API, don't go thru the embarrassment of trying to enable it list(APPEND FFmpeg_HWACCEL_FLAGS --disable-vaapi) -elseif (UNIX AND NOT DEFINED FFmpeg_IS_CROSS_COMPILING) +elseif (UNIX AND NOT DEFINED FFmpeg_IS_CROSS_COMPILING AND NOT ANDROID) find_package(PkgConfig REQUIRED) pkg_check_modules(LIBVA libva) pkg_check_modules(CUDA cuda) @@ -169,7 +169,7 @@ if (PLATFORM_PS4) ) elseif (PLATFORM_MANAGARM) # Required for proper stuff - list(APPEND FFmpeg_CROSS_COMPILE_FLAGS + list(APPEND FFmpeg_CROSS_COMPILE_FLAGS --disable-pthreads --extra-libs="${FFmpeg_CROSS_COMPILE_LIBS}" ) diff --git a/externals/nx_tzdb/NxTzdbCreateHeader.cmake b/externals/nx_tzdb/NxTzdbCreateHeader.cmake index 95606d8629..229c8aa43d 100644 --- a/externals/nx_tzdb/NxTzdbCreateHeader.cmake +++ b/externals/nx_tzdb/NxTzdbCreateHeader.cmake @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +# SPDX-License-Identifier: GPL-3.0-or-later + # SPDX-FileCopyrightText: 2023 yuzu Emulator Project # SPDX-License-Identifier: GPL-2.0-or-later @@ -16,15 +19,24 @@ if (NOT FILE_LIST) endif() set(DIRECTORY_NAME ${HEADER_NAME}) - set(FILE_DATA "") + +string(APPEND FILE_DATA "[[nodiscard]] static inline std::vector CollectFiles_${DIRECTORY_NAME}() {\n") +string(APPEND FILE_DATA [[ + std::vector vfs_files; + auto const fn = [&](std::string_view name, std::span data) { + vfs_files.push_back(std::make_shared( + std::vector(data.begin(), data.end()), + std::string{name} + )); + }; +]]) foreach(ZONE_FILE ${FILE_LIST}) if (ZONE_FILE STREQUAL "\n") continue() endif() - - string(APPEND FILE_DATA "{\"${ZONE_FILE}\",\n{") - + string(APPEND FILE_DATA " {\n") + string(APPEND FILE_DATA " constexpr uint8_t tzdb_data[] = {\n") file(READ ${ZONE_PATH}/${ZONE_FILE} ZONE_DATA HEX) string(LENGTH "${ZONE_DATA}" ZONE_DATA_LEN) foreach(I RANGE 0 ${ZONE_DATA_LEN} 2) @@ -42,9 +54,12 @@ foreach(ZONE_FILE ${FILE_LIST}) string(APPEND FILE_DATA " ") endif() endforeach() - - string(APPEND FILE_DATA "}},\n") + string(APPEND FILE_DATA " };\n") + string(APPEND FILE_DATA " fn(\"${ZONE_FILE}\", tzdb_data);\n") + string(APPEND FILE_DATA " }\n") endforeach() +string(APPEND FILE_DATA " return vfs_files;\n") +string(APPEND FILE_DATA "}\n") file(READ ${NX_TZDB_SOURCE_DIR}/tzdb_template.h.in NX_TZDB_TEMPLATE_H_IN) file(CONFIGURE OUTPUT ${NX_TZDB_INCLUDE_DIR}/nx_tzdb/${HEADER_NAME}.h CONTENT "${NX_TZDB_TEMPLATE_H_IN}") diff --git a/externals/nx_tzdb/tzdb_template.h.in b/externals/nx_tzdb/tzdb_template.h.in index 289d002ea8..c349a143d5 100644 --- a/externals/nx_tzdb/tzdb_template.h.in +++ b/externals/nx_tzdb/tzdb_template.h.in @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -9,10 +12,10 @@ namespace NxTzdb { +// @DIRECTORY_NAME@ + // clang-format off -const static std::map> @DIRECTORY_NAME@ = -{ -@FILE_DATA@}; +@FILE_DATA@ // clang-format on } // namespace NxTzdb diff --git a/hooks/pre-commit b/hooks/pre-commit index 484eef1762..b543ff36ab 100755 --- a/hooks/pre-commit +++ b/hooks/pre-commit @@ -1,5 +1,7 @@ -#!/bin/sh +#!/bin/sh -e +# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-FileCopyrightText: 2015 Citra Emulator Project # SPDX-License-Identifier: GPL-2.0-or-later @@ -10,20 +12,19 @@ paths_to_check="src/ CMakeLists.txt" # If there are whitespace errors, print the offending file names and fail. if ! git diff --cached --check -- $paths_to_check ; then - cat<:-mcx16>) + if (PLATFORM_LINUX OR PLATFORM_FREEBSD) + add_compile_options($<$:-mtls-dialect=gnu2>) + endif() endif() if (APPLE AND CXX_CLANG) diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts index ccc5b3cabe..a4076978a1 100644 --- a/src/android/app/build.gradle.kts +++ b/src/android/app/build.gradle.kts @@ -256,7 +256,7 @@ android { externalNativeBuild { cmake { - version = "3.22.1" + version = "3.31.6" path = file("${edenDir}/CMakeLists.txt") } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt index eab0fac705..716bdfeb7e 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt @@ -15,7 +15,6 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter -import android.content.pm.PackageManager import android.content.res.Configuration import android.graphics.Rect import android.graphics.drawable.Icon @@ -101,7 +100,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener, InputManager private var romSwapGeneration = 0 private var hasEmulationSession = processHasEmulationSession private val romSwapStopTimeoutRunnable = Runnable { onRomSwapStopTimeout() } - private val pictureInPictureFailureActions: MutableSet = mutableSetOf() private fun onRomSwapStopTimeout() { if (!isWaitingForRomSwapStop) { @@ -127,6 +125,8 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener, InputManager super.onCreate(savedInstanceState) + NativeConfig.reloadGlobalConfig() + InputHandler.updateControllerData() val players = NativeConfig.getInputSettings(true) var hasConfiguredControllers = false @@ -268,18 +268,12 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener, InputManager } override fun onUserLeaveHint() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S || - !isPictureInPictureSupported() || - !BooleanSetting.PICTURE_IN_PICTURE.getBoolean() || - isInPictureInPictureMode - ) { - return - } - - val pictureInPictureParamsBuilder = PictureInPictureParams.Builder() - .getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder() - runPictureInPictureAction("enter picture-in-picture mode") { - enterPictureInPictureMode(pictureInPictureParamsBuilder.build()) + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) { + if (BooleanSetting.PICTURE_IN_PICTURE.getBoolean() && !isInPictureInPictureMode) { + val pictureInPictureParamsBuilder = PictureInPictureParams.Builder() + .getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder() + enterPictureInPictureMode(pictureInPictureParamsBuilder.build()) + } } } @@ -659,29 +653,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener, InputManager return this.apply { setActions(pictureInPictureActions) } } - private fun isPictureInPictureSupported() = - Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && - packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE) - - private fun runPictureInPictureAction(actionName: String, action: () -> Unit) { - try { - action() - } catch (e: IllegalStateException) { - if (pictureInPictureFailureActions.add(actionName)) { - Log.warning("[PiP] Failed to $actionName: ${e.message}") - } - } catch (e: UnsupportedOperationException) { - if (pictureInPictureFailureActions.add(actionName)) { - Log.warning("[PiP] Failed to $actionName: ${e.message}") - } - } - } - fun buildPictureInPictureParams() { - if (!isPictureInPictureSupported()) { - return - } - val pictureInPictureParamsBuilder = PictureInPictureParams.Builder() .getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { @@ -691,9 +663,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener, InputManager BooleanSetting.PICTURE_IN_PICTURE.getBoolean() && isEmulationActive ) } - runPictureInPictureAction("set picture-in-picture params") { - setPictureInPictureParams(pictureInPictureParamsBuilder.build()) - } + setPictureInPictureParams(pictureInPictureParamsBuilder.build()) } fun displayMultiplayerDialog() { diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt index eccf0549e7..ad983bd749 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt @@ -16,6 +16,7 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting { RENDERER_USE_SPEED_LIMIT("use_speed_limit"), USE_CUSTOM_CPU_TICKS("use_custom_cpu_ticks"), SKIP_CPU_INNER_INVALIDATION("skip_cpu_inner_invalidation"), + ANTIFLICKER("antiflicker"), FIX_BLOOM_EFFECTS("fix_bloom_effects"), EMULATE_BGR565("emulate_bgr565"), RESCALE_HACK("rescale_hack"), @@ -30,6 +31,7 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting { RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"), ENABLE_BUFFER_HISTORY("enable_buffer_history"), USE_OPTIMIZED_VERTEX_BUFFERS("use_optimized_vertex_buffers"), + ENABLE_GPU_BUFFER_READBACK("enable_gpu_buffer_readback"), SYNC_MEMORY_OPERATIONS("sync_memory_operations"), BUFFER_REORDER_DISABLE("disable_buffer_reorder"), RENDERER_DEBUG("debug"), @@ -82,9 +84,10 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting { ENABLE_OVERLAY("enable_overlay"), // GPU Logging - GPU_LOGGING_ENABLED("gpu_logging_enabled"), GPU_LOG_VULKAN_CALLS("gpu_log_vulkan_calls"), GPU_LOG_SHADER_DUMPS("gpu_log_shader_dumps"), + DUMP_GUEST_SHADERS("dump_guest_shaders"), + DUMP_MACROS("dump_macros"), GPU_LOG_MEMORY_TRACKING("gpu_log_memory_tracking"), GPU_LOG_DRIVER_DEBUG("gpu_log_driver_debug"), diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt index 55ddd5950c..2215afb663 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: 2023 yuzu Emulator Project @@ -11,6 +11,7 @@ import org.yuzu.yuzu_emu.utils.NativeConfig enum class StringSetting(override val key: String) : AbstractStringSetting { DRIVER_PATH("driver_path"), DEVICE_NAME("device_name"), + PROGRAM_ARGS("program_args"), WEB_TOKEN("eden_token"), WEB_USERNAME("eden_username") diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt index 230390749e..67cf392fef 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt @@ -125,6 +125,13 @@ abstract class SettingsItem( // List of all general val settingsItems = HashMap().apply { put(StringInputSetting(StringSetting.DEVICE_NAME, titleId = R.string.device_name)) + put( + StringInputSetting( + StringSetting.PROGRAM_ARGS, + titleId = R.string.program_args, + descriptionId = R.string.program_args_description + ) + ) put( SwitchSetting( BooleanSetting.RENDERER_USE_SPEED_LIMIT, @@ -750,6 +757,13 @@ abstract class SettingsItem( descriptionId = R.string.skip_cpu_inner_invalidation_description ) ) + put( + SwitchSetting( + BooleanSetting.ANTIFLICKER, + titleId = R.string.antiflicker, + descriptionId = R.string.antiflicker_description + ) + ) put( SwitchSetting( BooleanSetting.FIX_BLOOM_EFFECTS, @@ -792,6 +806,13 @@ abstract class SettingsItem( descriptionId = R.string.enable_buffer_history_description ) ) + put( + SwitchSetting( + BooleanSetting.ENABLE_GPU_BUFFER_READBACK, + titleId = R.string.enable_gpu_buffer_readback, + descriptionId = R.string.enable_gpu_buffer_readback_description + ) + ) put( SwitchSetting( BooleanSetting.USE_OPTIMIZED_VERTEX_BUFFERS, @@ -917,13 +938,6 @@ abstract class SettingsItem( ) // GPU Logging settings - put( - SwitchSetting( - BooleanSetting.GPU_LOGGING_ENABLED, - titleId = R.string.gpu_logging_enabled, - descriptionId = R.string.gpu_logging_enabled_description - ) - ) put( SingleChoiceSetting( ByteSetting.GPU_LOG_LEVEL, @@ -940,6 +954,13 @@ abstract class SettingsItem( descriptionId = R.string.gpu_log_vulkan_calls_description ) ) + put( + SwitchSetting( + BooleanSetting.DUMP_GUEST_SHADERS, + titleId = R.string.dump_guest_shaders, + descriptionId = R.string.dump_guest_shaders_description + ) + ) put( SwitchSetting( BooleanSetting.GPU_LOG_SHADER_DUMPS, @@ -947,6 +968,13 @@ abstract class SettingsItem( descriptionId = R.string.gpu_log_shader_dumps_description ) ) + put( + SwitchSetting( + BooleanSetting.DUMP_MACROS, + titleId = R.string.dump_macros, + descriptionId = R.string.dump_macros_description + ) + ) put( SwitchSetting( BooleanSetting.GPU_LOG_MEMORY_TRACKING, diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt index 4da218bfcc..7b7762520d 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt @@ -76,18 +76,25 @@ class SettingsFragmentPresenter( } } - private fun isFsrScalingFilterSelected(): Boolean { - val fsrFilterValue = resolveFsrScalingFilterValue() ?: return false + private fun isSharpnessScalingFilterSelected(): Boolean { val needsGlobal = getNeedsGlobalForKey(IntSetting.RENDERER_SCALING_FILTER.key) val selectedFilter = IntSetting.RENDERER_SCALING_FILTER.getInt(needsGlobal) - return selectedFilter == fsrFilterValue + return selectedFilter in resolveSharpnessScalingFilterValues() } - private fun resolveFsrScalingFilterValue(): Int? { + private fun resolveSharpnessScalingFilterValues(): Set { val names = context.resources.getStringArray(R.array.rendererScalingFilterNames) val values = context.resources.getIntArray(R.array.rendererScalingFilterValues) - val fsrIndex = names.indexOf(context.getString(R.string.scaling_filter_fsr)) - return if (fsrIndex in values.indices) values[fsrIndex] else null + val sharpnessFilterNames = setOf( + context.getString(R.string.scaling_filter_fsr), + context.getString(R.string.scaling_filter_sgsr), + context.getString(R.string.scaling_filter_sgsr_edge), + ) + return names.asSequence() + .mapIndexedNotNull { index, name -> + if (name in sharpnessFilterNames && index in values.indices) values[index] else null + } + .toSet() } // Allows you to show/hide abstract settings based on the paired setting key @@ -267,7 +274,7 @@ class SettingsFragmentPresenter( add(IntSetting.RENDERER_RESOLUTION.key) add(IntSetting.RENDERER_VSYNC.key) add(IntSetting.RENDERER_SCALING_FILTER.key) - if (isFsrScalingFilterSelected()) { + if (isSharpnessScalingFilterSelected()) { add(IntSetting.FSR_SHARPENING_SLIDER.key) } add(IntSetting.RENDERER_ANTI_ALIASING.key) @@ -285,12 +292,14 @@ class SettingsFragmentPresenter( add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key) add(BooleanSetting.RENDERER_REACTIVE_FLUSHING.key) add(BooleanSetting.ENABLE_BUFFER_HISTORY.key) + add(BooleanSetting.ENABLE_GPU_BUFFER_READBACK.key) add(BooleanSetting.USE_OPTIMIZED_VERTEX_BUFFERS.key) add(HeaderSetting(R.string.hacks)) add(IntSetting.FAST_GPU_TIME.key) add(BooleanSetting.SKIP_CPU_INNER_INVALIDATION.key) + add(BooleanSetting.ANTIFLICKER.key) add(BooleanSetting.FIX_BLOOM_EFFECTS.key) add(BooleanSetting.EMULATE_BGR565.key) add(BooleanSetting.RESCALE_HACK.key) @@ -1278,15 +1287,19 @@ class SettingsFragmentPresenter( add(HeaderSetting(R.string.general)) add(ShortSetting.DEBUG_KNOBS.key) + add(StringSetting.PROGRAM_ARGS.key) - add(HeaderSetting(R.string.gpu_logging_header)) - add(BooleanSetting.GPU_LOGGING_ENABLED.key) - add(ByteSetting.GPU_LOG_LEVEL.key) - add(BooleanSetting.GPU_LOG_VULKAN_CALLS.key) - add(BooleanSetting.GPU_LOG_SHADER_DUMPS.key) - add(BooleanSetting.GPU_LOG_MEMORY_TRACKING.key) - add(BooleanSetting.GPU_LOG_DRIVER_DEBUG.key) - add(IntSetting.GPU_LOG_RING_BUFFER_SIZE.key) + if (!NativeConfig.isPerGameConfigLoaded()) { + add(HeaderSetting(R.string.gpu_logging_header)) + add(ByteSetting.GPU_LOG_LEVEL.key) + add(BooleanSetting.GPU_LOG_VULKAN_CALLS.key) + add(BooleanSetting.DUMP_GUEST_SHADERS.key) + add(BooleanSetting.GPU_LOG_SHADER_DUMPS.key) + add(BooleanSetting.DUMP_MACROS.key) + add(BooleanSetting.GPU_LOG_MEMORY_TRACKING.key) + add(BooleanSetting.GPU_LOG_DRIVER_DEBUG.key) + add(IntSetting.GPU_LOG_RING_BUFFER_SIZE.key) + } } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt index 120bafdd14..0069e169b3 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt @@ -1090,7 +1090,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { private fun addQuickSettings() { binding.quickSettingsSheet.apply { val container = binding.quickSettingsSheet.findViewById(R.id.quick_settings_container) - val isFsrSelected = isFsrScalingFilterSelected() + val isSharpnessFilterSelected = isSharpnessScalingFilterSelected() container.removeAllViews() @@ -1176,7 +1176,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { addQuickSettings() } - if (isFsrSelected) { + if (isSharpnessFilterSelected) { quickSettings.addSliderSetting( R.string.fsr_sharpness, container, @@ -1197,17 +1197,24 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { } } - private fun isFsrScalingFilterSelected(): Boolean { - val fsrFilterValue = resolveFsrScalingFilterValue() ?: return false + private fun isSharpnessScalingFilterSelected(): Boolean { val selectedFilter = IntSetting.RENDERER_SCALING_FILTER.getInt(needsGlobal = false) - return selectedFilter == fsrFilterValue + return selectedFilter in resolveSharpnessScalingFilterValues() } - private fun resolveFsrScalingFilterValue(): Int? { + private fun resolveSharpnessScalingFilterValues(): Set { val names = resources.getStringArray(R.array.rendererScalingFilterNames) val values = resources.getIntArray(R.array.rendererScalingFilterValues) - val fsrIndex = names.indexOf(getString(R.string.scaling_filter_fsr)) - return if (fsrIndex in values.indices) values[fsrIndex] else null + val sharpnessFilterNames = setOf( + getString(R.string.scaling_filter_fsr), + getString(R.string.scaling_filter_sgsr), + getString(R.string.scaling_filter_sgsr_edge), + ) + return names.asSequence() + .mapIndexedNotNull { index, name -> + if (name in sharpnessFilterNames && index in values.indices) values[index] else null + } + .toSet() } private fun openQuickSettingsMenu() { diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt index c3dea79bae..aa7d8bb6be 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt @@ -440,7 +440,7 @@ class GamePropertiesFragment : Fragment() { val shaderCacheDir = File( DirectoryInitialization.userDirectory + - "/shader/" + args.game.settingsName.lowercase() + "/cache/shader/" + args.game.settingsName.lowercase() ) if (shaderCacheDir.exists()) { add( diff --git a/src/android/app/src/main/jni/CMakeLists.txt b/src/android/app/src/main/jni/CMakeLists.txt index c68e206d24..b6fe2d7722 100644 --- a/src/android/app/src/main/jni/CMakeLists.txt +++ b/src/android/app/src/main/jni/CMakeLists.txt @@ -33,3 +33,5 @@ if (ENABLE_UPDATE_CHECKER) endif() set(CPACK_PACKAGE_EXECUTABLES ${CPACK_PACKAGE_EXECUTABLES} yuzu-android) + +target_link_options(yuzu-android PRIVATE "-Wl,-Bsymbolic") diff --git a/src/android/app/src/main/jni/android_config.cpp b/src/android/app/src/main/jni/android_config.cpp index f697084348..e016322c23 100644 --- a/src/android/app/src/main/jni/android_config.cpp +++ b/src/android/app/src/main/jni/android_config.cpp @@ -19,6 +19,9 @@ AndroidConfig::AndroidConfig(const std::string& config_name, ConfigType config_t } void AndroidConfig::ReloadAllValues() { + // Ensure the INI file is current before reloading values. + SetUpIni(); + Reload(); ReadAndroidValues(); SaveAndroidValues(); diff --git a/src/android/app/src/main/res/drawable/ic_launcher_foreground.png b/src/android/app/src/main/res/drawable/ic_launcher_foreground.png index e7800a9bb9..53f1cace9b 100644 Binary files a/src/android/app/src/main/res/drawable/ic_launcher_foreground.png and b/src/android/app/src/main/res/drawable/ic_launcher_foreground.png differ diff --git a/src/android/app/src/main/res/drawable/ic_yuzu.png b/src/android/app/src/main/res/drawable/ic_yuzu.png index 27ac821833..fce02afa1f 100644 Binary files a/src/android/app/src/main/res/drawable/ic_yuzu.png and b/src/android/app/src/main/res/drawable/ic_yuzu.png differ diff --git a/src/android/app/src/main/res/drawable/ic_yuzu_splash.png b/src/android/app/src/main/res/drawable/ic_yuzu_splash.png index 7f20f404ff..0e43cb9374 100644 Binary files a/src/android/app/src/main/res/drawable/ic_yuzu_splash.png and b/src/android/app/src/main/res/drawable/ic_yuzu_splash.png differ diff --git a/src/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/src/android/app/src/main/res/mipmap-hdpi/ic_launcher.png index d7b2e938df..23bc2897c3 100644 Binary files a/src/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/src/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/src/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/src/android/app/src/main/res/mipmap-mdpi/ic_launcher.png index 7f60994e50..f630e793e3 100644 Binary files a/src/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/src/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/src/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/src/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png index 37e436f599..1daa3c624f 100644 Binary files a/src/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/src/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/src/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/src/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index e3e679367d..7fc64e1393 100644 Binary files a/src/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/src/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/src/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png index e358e1ebb7..53ed9b9914 100644 Binary files a/src/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/src/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src/android/app/src/main/res/values-ar/strings.xml b/src/android/app/src/main/res/values-ar/strings.xml index 08d56dea49..c66c20d66a 100644 --- a/src/android/app/src/main/res/values-ar/strings.xml +++ b/src/android/app/src/main/res/values-ar/strings.xml @@ -428,6 +428,9 @@ دقة وحدة المعالجة المركزية %1$s%2$s + وسائط Homebrew + سطر الأوامر الوسائط المُمررة إلى Homebrew عند التشغيل (مثل -noglsl). + اسم الجهاز وضع الإرساء @@ -462,8 +465,8 @@ الدقة (الإرساء/محمول) VSync وضع مرشح ملائم للنافذة - حدة FSR - يحدد مدى وضوح الصورة عند استخدام التباين الديناميكي لـ FSR + حدة FSR/SGSR + يحدد مدى وضوح الصورة عند استخدام مرشحات FSR أو SGSR طريقة مضاد التعرج @@ -503,6 +506,8 @@ يُجبر هذا الخيار معظم الألعاب على العمل بأعلى دقة عرض أصلية. استخدم 256 للحصول على أقصى أداء و512 للحصول على أعلى جودة رسومات. تخطي إبطال صلاحية وحدة المعالجة المركزية الداخلية يتخطى بعض عمليات إبطال ذاكرة التخزين المؤقتة من جانب وحدة المعالجة المركزية أثناء تحديثات الذاكرة، مما يقلل من استخدام وحدة المعالجة المركزية ويحسن أداءها. قد يتسبب ذلك في حدوث أعطال أو تعطل في بعض الألعاب. + مضاد الوميض + يُجبر هذا الوضع وظائف وحدة معالجة الرسومات على الانتظار حتى يتم إرسال العمل إليها. استخدمه مع وضع وحدة معالجة الرسومات السريع لتجنب الوميض مع تأثير أقل على الأداء. إصلاح تأثيرات التوهج يقلل من ضبابية التوهج في LA/EOW (Adreno A6XX - A7XX/ Turnip)، ويزيل التوهج في Burnout. تحذير: قد يسبب تشوهات رسومية في ألعاب أخرى. محاكاة BGR565 @@ -564,8 +569,6 @@ تسجيل وحدة معالجة الرسومات - تمكين تسجيل وحدة معالجة الرسومات - تسجيل عمليات وحدة معالجة الرسومات في ملف eden_gpu.log لتصحيح أخطاء برامج تشغيل Adreno مستوى السجل مستوى التفاصيل لسجلات وحدة معالجة الرسومات (كلما زاد المستوى، زادت التفاصيل وزادت التكاليف الإضافية) تسجيل استدعاءات واجهة برمجة تطبيقات Vulkan diff --git a/src/android/app/src/main/res/values-ckb/strings.xml b/src/android/app/src/main/res/values-ckb/strings.xml index c310b88d25..c6a5075cf5 100644 --- a/src/android/app/src/main/res/values-ckb/strings.xml +++ b/src/android/app/src/main/res/values-ckb/strings.xml @@ -329,8 +329,6 @@ ڕوونی (دۆخی دەستی/دۆخی دۆک) دۆخی VSync فلتەری گونجاندنی پەنجەرە - تیژی FSR - دیاریکردنی تیژی وێنە لە کاتی بەکارهێنانی FSR شێوازی دژە-خاوڕۆیی diff --git a/src/android/app/src/main/res/values-cs/strings.xml b/src/android/app/src/main/res/values-cs/strings.xml index 3354830611..9c278038dc 100644 --- a/src/android/app/src/main/res/values-cs/strings.xml +++ b/src/android/app/src/main/res/values-cs/strings.xml @@ -439,8 +439,6 @@ Rozlišení (Handheld/V doku) Režim VSync Škálovací filtr - Ostrost FSR - Určuje jak ostře bude obraz vypadat při použití dynamického kontrastu FSR. Metoda anti-aliasingu diff --git a/src/android/app/src/main/res/values-de/strings.xml b/src/android/app/src/main/res/values-de/strings.xml index e7246dae2c..5fba5c4c97 100644 --- a/src/android/app/src/main/res/values-de/strings.xml +++ b/src/android/app/src/main/res/values-de/strings.xml @@ -1,7 +1,7 @@ - Diese Software führt Spiele für die Nintendo Switch-Konsole aus. Es sind keine Spiele oder Keys enthalten. Suche bevor du beginnst deine prod.keys ]]>-Datei auf deinem Gerät.

Mehr Erfahren]]>
+ Diese Software führt Spiele für die Nintendo Switch-Konsole aus. Es sind keine Spiele oder Schlüssel enthalten. Suche, bevor du beginnst, deine prod.keys ]]>-Datei auf deinem Gerät.

Mehr Erfahren]]>
Hinweise und Fehler Zeigt Benachrichtigungen an, wenn etwas schief läuft. Berechtigung für Benachrichtigungen nicht zugelassen! @@ -16,22 +16,27 @@ Wert darf höchstens %1$d betragen Ungültiger Wert + Verwendung spielspezifischer Konfigurationen + - Eingabe-Overlay anzeigen + Eingabeüberlagerung anzeigen Touch-Bedienelemente während der Emulation anzeigen Am Raster ausrichten - Beim Bearbeiten die Overlay-Bedienelemente am Raster ausrichten + Überlagerungs-Steuerelemente beim Bearbeiten an einem Raster ausrichten Rastergröße - Rastergröße in Pixeln + Größe der Rasterzellen in Pixeln Verhalten - Automatisches Ausblenden des Overlays - Blende die Touch-Bedienelemente nach Ablauf der angegebenen Inaktivitätszeit automatisch aus - Automatisches Ausblenden des Overlays aktivieren - Overlay bei Controller-Eingabe ausblenden - Blende die Touch-Bedienelemente automatisch aus wenn ein physischer Controller benutzt wird. Das Overlay wird wieder eingeblendet, wenn die Verbindung zum Controller getrennt wird. - Eingabe-Overlay + Automatisches Ausblenden der Überlagerung + Überlagerung der Touch-Bedienelementen automatisch nach der festgelegten Zeit der Inaktivität ausblenden. + Automatisches Ausblenden der Überlagerung aktivieren + Überlagerung bei Controller-Eingabe ausblenden + Touch-Bedienelemente automatisch ausblenden, wenn ein physischer Controller benutzt wird. Die Überlagerung wird wieder eingeblendet, wenn die Verbindung zum Controller getrennt wird. + Controller-Tasten für Bestätigen/Zurück vertauschen + Belegung der Android-Tasten „Bestätigen“ und „Zurück“ so tauschen, dass sie bei der Nutzung der App-Benutzeroberfläche sowohl dem Switch- als auch dem Xbox-Schema entsprechen. + + Eingabeüberlagerung Bedienelemente auf dem Bildschirm konfigurieren - Overlay-Layout bearbeiten + Überlagerungs-Layout bearbeiten Position und Skalierung der Bedienelemente auf dem Bildschirm anpassen @@ -45,12 +50,12 @@ Leistungsstatistik Overlay anzeigen Anpassung Sichtbarkeit - Leistungs Overlay - Leistungsstatistik Overlay aktivieren - Konfiguriere angezeigte Informationen im Overlay - FPS anzeigen + Leistungsüberlagerung + Leistungsstatistik-Überlagerung aktivieren + Angezeigte Informationen in der Überlagerung konfigurieren + BpS anzeigen Aktuelle Bilder pro Sekunde - Frametime anzeigen + Frame-Zeit anzeigen App-RAM-Nutzung anzeigen Zeigt den RAM-Verbrauch des Emulators an System-RAM-Nutzung anzeigen @@ -59,25 +64,28 @@ Batterietemperatur-Einheiten Batterieinfo anzeigen Aktuellen Stromverbrauch und verbleibende Kapazität der Batterie anzeigen - Shader-Bau anzeigen - Aktuelle Anzahl der erstellten Shader anzeigen - Overlay-Position - Position des Overlays auf dem Bildschirm + Schattierer-Erstellung anzeigen + Aktuelle Anzahl der erstellten Schattierer anzeigen + Lege die Anzahl der Kerne fest, die für die Erstellung von Vulkan-Rohrleitungen verwendet werden sollen. Ein höherer Wert verbessert die Kompilierungsleistung der Rohrleitung, führt jedoch auch zu einem Anstieg der Temperaturen. + Überlagerungs-Position + Wähle aus, wo die Überlagerung auf dem Bildschirm angezeigt wird Oben links Oben rechts Unten links Unten rechts Mitte oben Mitte unten - Overlay-Hintergrund + Überlagerungs-Hintergrund Hintergrund für bessere Lesbarkeit - Geräteinfo-Overlay anzeigen - Geräte-Overlay aktivieren - Geräte-Overlay - Konfigurieren Sie, welche Informationen im Geräte-Overlay angezeigt werden + Geräteinfo-Überlagerung anzeigen + Geräteüberlagerung aktivieren + Geräteüberlagerung + Konfiguriere, welche Informationen in der Geräteüberlagerung angezeigt werden + Build-ID anzeigen + Treiberversion anzeigen Gerätemodell anzeigen GPU-Modell anzeigen SoC-Modell anzeigen @@ -85,19 +93,19 @@ Puffer-Neuanordnung deaktivieren - Wenn aktiviert, wird die Neuanordnung von gemappten Speicher-Uploads deaktiviert, was die Zuordnung von Uploads zu bestimmten Zeichenvorgängen ermöglicht. Kann in einigen Fällen die Leistung verringern. + Wenn aktiviert, wird die Neuanordnung von zugeordneten Speicher-Uploads deaktiviert, wodurch Uploads bestimmten Abrufen zugeordnet werden können. Dies kann in einigen Fällen zu Leistungseinbußen führen. Kern-Geschwindigkeit synchronisieren Synchronisiert die Taktrate des Kerns mit der maximalen Geschwindigkeit, um die Leistung zu verbessern, ohne die tatsächliche Spielgeschwindigkeit zu verändern. Host-MMU-Emulation aktivieren Diese Optimierung beschleunigt Speicherzugriffe durch das Gastprogramm. Wenn aktiviert, erfolgen Speicherlese- und -schreibvorgänge des Gastes direkt im Speicher und nutzen die MMU des Hosts. Das Deaktivieren erzwingt die Verwendung der Software-MMU-Emulation für alle Speicherzugriffe. - Debug-Regler + Fehlerbehebungs-Regler Nur für Entwicklungszwecke 0 bis 65535 NVDEC-Emulation - Methode zur Videodekodierung + Wähle aus, wie die Videodekodierung (NVDEC) während Zwischensequenzen und Intros gehandhabt wird. Keine @@ -126,7 +134,7 @@ Falsches Passwort Verbindung fehlgeschlagen Raum ist voll - Sie sind von diesem Raum gebannt + Du bist von diesem Raum gebannt Zugriff verweigert Benutzer existiert nicht Bereits im Raum @@ -139,7 +147,7 @@ Raum beigetreten Raum-Moderator %1$s ist beigetreten - %1$s hat verlassen + %1$s ist gegangen %1$s wurde entfernt %1$s wurde gebannt Adresse entbannt @@ -157,11 +165,11 @@ Nachricht senden Moderation Bannliste - Keine gebannten Benutzer - Entbannen + Keine gebannten Nutzer + Nutzer entbannen Entbannen %1$s wirklich entbannen? - Benutzer bannen + Nutzer bannen Öffentliche Räume Keine öffentlichen Räume gefunden Passwort erforderlich @@ -172,15 +180,15 @@ Volle Räume ausblenden Leere Räume ausblenden Zum Aktualisieren tippen - Räume suchen… + Lobbys suchen… Bevorzugtes Spiel Lobby-Typ Muss 3-20 Zeichen lang sein Erforderlich - Web-Token erforderlich, gehen Sie zu Erweiterte Einstellungen -> System -> Netzwerk + Web-Token erforderlich, gehe zu Erweiterte Einstellungen -> System -> Netzwerk Ungültiges IP-Format Muss zwischen 4–20 Zeichen lang sein und nur alphanumerische Zeichen, Punkte, Bindestriche, Unterstriche und Leerzeichen enthalten - Ungültiger Benutzername, stellen Sie sicher, dass er in System → Netzwerk korrekt eingestellt ist + Ungültiger Benutzername; stelle sicher, dass er unter System -> Netzwerk korrekt konfiguriert ist. Muss 48 Zeichen lang sein und nur Kleinbuchstaben a-z enthalten Muss zwischen 1-65535 liegen Abbrechen @@ -188,20 +196,20 @@ Aktualisieren Raumliste Öffentlich - Unlisted + Nicht gelistet Willkommen! - Erfahre wie man <b>Eden</b> einrichtet und beginne mit der Emulation. + Erfahre, wie du Eden einrichtest, und tauche in die Welt der Emulation ein. Erste Schritte Schlüssel - Wähle deine <b>prod.keys</b> Datei mit dem Button unten aus. + Wähle deine prod.keys -Datei über die untenstehende Schaltfläche aus. Firmware - Wähle deine firmware.zip-Datei mit dem Button unten aus + Wähle deine firmware.zip-Datei über die untenstehende Schaltfläche aus. Spiele - Wähle mit dem Knopf unten den <b>Spiele</b>-Ordner aus. + Wähle deinen Spiele-Ordner mit der untenstehenden Schaltfläche aus. Fertig - Du bist start klar.\nViel Spaß mit deinen Spielen! + Alles bereit.\nViel Spaß beim Spielen! Weiter Zurück Spiele hinzufügen @@ -214,23 +222,23 @@ Raster Kompaktes Gitter Karussell - Scrennshot für %1$s + Bildschirmfoto für %1$s Ordner Nicht mehr anzeigen Neues Spieleverzeichnis erfolgreich hinzugefügt. - Auf Updates überprüfen - Beim Start auf Updates überprüfen und optional das neue Update herunterladen und installieren - Update verfügbar - Eine Version ist verfügbar: %1$s\n\nWillst du sie herunterladen\? - Update wird heruntergeladen - Herunterladen des Updates fehlgeschlagen - Update erfolgreich installiert - Installieren des Updates fehlgeschlagen: %1$s + Auf Aktualisierungen prüfen + Beim Start auf Aktualisierungen prüfen und optional die neue Aktualisierung herunterladen und installieren + Aktualisierung verfügbar + Eine neue Version ist verfügbar: %1$s\n\nWillst du sie herunterladen\? + Aktualisierung wird heruntergeladen + Herunterladen der Aktualisierung fehlgeschlagen + Aktualisierung erfolgreich installiert + Installieren der Aktualisierung fehlgeschlagen: %1$s Suche Einstellungen Es wurden keine Dateien gefunden oder es wurde noch kein Spielverzeichnis ausgewählt. - Spiele-Ordner verwalten - Erlaubt Eden die Spieleliste zu füllen + Spielordner verwalten + Ermöglicht es Eden, die Spieleliste zu füllen. Auswahl des Spieleverzeichnisses überspringen? Spiele werden in der Spieleliste nicht angezeigt, wenn kein Ordner ausgewählt ist. https://yuzu-mirror.github.io/help/quickstart/#dumping-games @@ -241,33 +249,33 @@ Hinzufügen der Schlüssel überspringen? Für die Emulation von Spielen sind gültige Schlüssel erforderlich. Wenn du fortfährst, funktionieren nur Homebrew-Anwendungen. https://yuzu-mirror.github.io/help/quickstart/#guide-introduction - Firmware nicht hinzufügen? + Das Hinzufügen der Firmware überspringen\? Emulatordaten einrichten Für die Funktion des Emulators werden Schlüssel benötigt, und Firmware ist empfohlen und für die Verwendung des QLaunch-Applets erforderlich. - Berechtigung erteilen - Optionale Berechtigungen erteilen um bestimmte Features des Emulators zu nutzen + Berechtigungen erteilen + Erteile optionale Berechtigungen zur Nutzung bestimmter Funktionen des Emulators. Viele Spiele benötigen Zugriff auf die Firmware, um richtig zu funktionieren. https://yuzu-mirror.github.io/help/quickstart/#guide-introduction Benachrichtigungen - Erteile mit dem Knopf unten die Berechtigung, Benachrichtigungen zu senden. + Erteile die Berechtigung für Benachrichtigungen über die untenstehende Schaltfläche. Zugriff verweigert Du hast diese Berechtigung zu oft verweigert und musst sie nun manuell in den Systemeinstellungen erteilen. Über - Build-Version, Credits und mehr + Build-Version, Mitwirkende und mehr Systeminformationen Detaillierte Systeminformationen anzeigen Hersteller Modell Produkt Android-Version - Sicherheitspatch + Sicherheits-Patch Build-ID Allgemeine Informationen Hardware Unterstützte ABIs CPU-Informationen GPU-Informationen - Vulkan Treiberversion + Vulkan-Treiberversion Fehler beim Abrufen der Emulatorinformationen Speicherinformation Gesamtspeicher @@ -278,7 +286,7 @@ Überspringen Abbrechen Amiibo-Schlüssel installieren - Benötigt um Amiibos im Spiel zu verwenden + Benötigt, um Amiibos im Spiel zu verwenden GPU-Treiber-Hersteller GPU-Treiber Verwaltung Alternative Treiber für eventuell bessere Leistung oder Genauigkeit installieren @@ -289,36 +297,39 @@ Eden-Ordner öffnen Eden\'s interne Dateien verwalten Verhalten und Aussehen der App verändern - Kein Dateimanager gefunden + Kein Datei-Manager gefunden Eden-Verzeichnis konnte nicht geöffnet werden - Bitte suche den Benutzerordner manuell über die Seitenleiste des Dateimanagers. + Bitte suche den Benutzerordner manuell über die Seitenleiste des Datei-Managers. Speicherdaten verwalten Speicherdaten gefunden. Bitte wähle unten eine Option aus. Speicherdaten importieren Das überschreibt alle existierenden Speicherdaten für dieses Spiel mit der ausgewählten Datei. Wirklich fortfahren? - Importiere Speicherdaten... - Exportiere Speicherdaten... + Speicherstände werden importiert... + Speicherstände werden exportiert... Erfolgreich importiert Ungültige Speicherverzeichnisstruktur Der erste Unterordnername muss die Titel-ID des Spiels sein. Firmware installieren - Die Firmware muss in einem ZIP-Archiv vorliegen und wird zum Booten einiger Spiele benötigt + Die Firmware muss in einem ZIP-Archiv vorliegen und wird zum Starten einiger Spiele benötigt Firmware wird installiert - Bei der Firmware installation ist etwas fehlgeschlagen. - Stellen Sie sicher, dass sich die Firmware-NCA-Dateien im Stammverzeichnis der ZIP-Datei befinden, und versuchen Sie es erneut. - Die Deinstallation der Firmware ist fehlgeschlagen - Debug-Logs teilen - Debug-Logs an Eden zur Untersuchung absenden - Keine Log-Datei gefunden + Firmware-Installation fehlgeschlagen + Stelle sicher, dass sich die Firmware-NCA-Dateien im Stammverzeichnis der ZIP-Datei befinden, und versuche es erneut. + Deinstallation der Firmware fehlgeschlagen + Fehlerbehebungs-Protokolle teilen + Fehlerbehebungs-Protokolle an Eden zur Fehlerbehebung absenden + Keine Protokolldatei gefunden + GPU-Protokolle teilen + Teile Edens GPU-Protokolldatei, um Grafikprobleme zu beheben + Keine GPU-Protokolldatei gefunden Spiel installieren - Spiel-Updates oder DLCs installieren - Installiere... + Spielaktualisierungen oder DLCs installieren + Inhalt wird installiert... Fehler beim Installieren der Datei(en) auf NAND - Bitte stellen Sie sicher, dass der/die Inhalt(e) gültig sind und dass die Datei prod.keys installiert ist. + Bitte stelle sicher, dass der/die Inhalt(e) gültig sind und dass die Datei prod.keys installiert ist. Um mögliche Konflikte zu vermeiden, ist die Installation von Basisspielen nicht gestattet. %1$d Installationsfehler - Game content(s) installed successfully + Spielinhalt(e) erfolgreich installiert %1$d erfolgreich installiert %1$d erfolgreich überschrieben https://yuzu-mirror.github.io/help/quickstart/#dumping-installed-updates @@ -326,20 +337,20 @@ Wirklich fortfahren?
Das Laden von benutzerdefinierten Treibern wird für dieses Gerät momentan nicht unterstützt.\nSchau später einfach nochmal nach, ob die Unterstützung hinzugefügt wurde! Eden-Daten verwalten Importieren/Exportieren Sie Firmware, Schlüssel, Benutzerdaten und mehr! - Spiele-Ordner + Spielordner Tiefer Scan - Spiele-Ordner hinzufügen + Spielordner hinzufügen Ordner bereits vorhanden Ordner-Eigenschaften %d Spielstand konnte nicht importiert werden - %d Spielstände konnten nicht importiert werden + %d-Spielstände konnten nicht importiert werden %d Spielstand erfolgreich importiert - %d Spielstände erfolgreich importiert + %d -Spielstände erfolgreich importiert - Keine Spielstände gefunden + Keine Speicherdaten gefunden Inhalt überprüfen Überprüft installierte Inhalte auf Fehler @@ -356,14 +367,15 @@ Wirklich fortfahren? Schlüsselinstallation fehlgeschlagen Schlüssel erfolgreich installiert Ein oder mehrere Schlüssel konnten nicht kopiert werden. - Stellen Sie sicher, dass Ihre Schlüsseldatei die Erweiterung .keys hat, und versuchen Sie es erneut. - Schlüssel konnten nicht initialisiert werden. Stellen Sie sicher, dass Ihre Dumping-Tools auf dem neuesten Stand sind, und dumpen Sie die Schlüssel erneut. + Stelle sicher, dass deine Schlüsseldatei die Erweiterung .keys hat, und versuche es erneut. + Schlüssel konnten nicht initialisiert werden. Stelle sicher, dass deine Auslese-Werkzeuge auf dem neuesten Stand sind, und lies dann die Schlüssel erneut aus. Qlaunch Anwendungen vom Systemstartbildschirm aus starten Applet-Launcher System-Applets mit Firmware starten + Firmware nicht installiert Applet nicht verfügbar Album Bilder im Screenshot-Ordner anzeigen @@ -439,8 +451,6 @@ Wird der Handheld-Modus verwendet, verringert es die Auflösung und erhöht die Auflösung (Handheld/Gedockt) VSync-Modus Skalierungsfilter - FSR-Schärfe - Bestimmt die Schärfe bei FSR-Nutzung. Kantenglättung diff --git a/src/android/app/src/main/res/values-es/strings.xml b/src/android/app/src/main/res/values-es/strings.xml index 1abf996752..8b505df846 100644 --- a/src/android/app/src/main/res/values-es/strings.xml +++ b/src/android/app/src/main/res/values-es/strings.xml @@ -376,7 +376,7 @@ Qlaunch Iniciar aplicaciones desde la pantalla de inicio del sistema Lanzador de Applet - Ejecutar applets de sistema usando el firmware instalado + Ejecutar applets del sistema usando el firmware instalado El firmware no está instalado Applet no disponible prod.keys y el firmware están instalados e inténtelo de nuevo.]]> @@ -422,6 +422,9 @@ Precisión de la CPU %1$s%2$s + Argumentos Homebrew + Argumentos en la línea de comandos pasados al homebrew al ser lanzado (p.e. -noglsl). + Nombre del dispositivo Modo sobremesa @@ -456,8 +459,8 @@ Resolución (Portátil/Sobremesa) Modo de sincronización vertical Filtro de adaptación de ventana - Nitidez FSR - Ajusta la intensidad del filtro de enfoque al usar el contraste dinámico de FSR. + Nitidez FSR/SGSR + Determina el grado de nitidez de la imagen al usar filtros FSR o SGSR Método de suavizado de bordes @@ -489,17 +492,22 @@ Activar el historial del búfer Permite el acceso al estado del búfer anterior. Esta opción puede mejorar la calidad de renderizado y la consistencia en el rendimiento de algunos juegos. Búferes de vértices optimizados + Permite la optimización del enlace del búfer de vértices para un mejor rendimiento. Requiere controladores Mesa 26.0+ Turnip/ controladores QCOM. Fallará con controladores Turnip más antiguos (versión 25.3 o inferior). + Hacks Tiempo rápido de la GPU Fuerza a la mayoría de los juegos a ejecutarse a su resolución nativa más alta. Usa 256 para un máximo rendimiento y 512 para una fidelidad gráfica óptima. Omitir invalidación interna de la CPU Omite ciertas invalidaciones de caché de la CPU durante las actualizaciones de memoria, lo que reduce el uso de la CPU y mejora su rendimiento. Esto puede causar fallos o bloqueos en algunos juegos. + Antiparpadeo + Fuerza a las funciones de devolución de llamada de la GPU a esperar a que se envíen las tareas a la GPU.\nÚsalo con el modo de GPU rápida para evitar el parpadeo con un menor impacto en el rendimiento. Arreglar los efectos de resplandor Reduce el efecto de resplandor en LA/EOW (Adreno A6XX - A7XX/ Turnip), elimina el resplandor en Burnout. Advertencia: puede causar artefactos gráficos en otros juegos. Emular BGR565 Soluciona problemas con colores invertidos en juegos, artefactos o sombras extrañas. Activar la pasada de reescalado heredada + Permite el manejo de versiones anteriores para el paso de configuración de reescalado para juegos mediante el uso de una ruta de reescalado rápida. Usar sombreadores asíncronos Compila los sombreadores de forma asíncrona. Esto puede reducir los tirones, pero también puede introducir errores gráficos. Ajustes de desentrelazado de la GPU @@ -518,8 +526,10 @@ Extensiones Estado dinámico extendido + Controla la cantidad de características que se pueden usar en ExtendedDynamicState (EDS). Un valor más alto permitirá reducir la cantidad de compilaciones de canalización basadas en el estado dinámico compatible con el controlador. Desactivado Estado dinámico de entrada de vértices + La activación de esta función permite un manejo más flexible de la entrada de vértices, lo que podría reducir el tiempo de compilación de la canalización en vértices/búfer. Muestreo de sombreado Permite que el sombreador de fragmentos se ejecute por muestra en un fragmento multimuestreado, en lugar de una sola vez por fragmento. Mejora la calidad de los gráficos a coste de algo de rendimiento. @@ -553,8 +563,6 @@ Registros de la GPU - Activar los registros de la GPU - Registra las operaciones de la GPU en eden_gpu.log para la depuración de los controladores de Adreno Nivel de registros Nivel de detalle de los registros de la GPU (más alto = más detalles, más sobrecarga) Registros de llamadas del API de Vulkan @@ -798,7 +806,7 @@ Tipo de contenido Actualizaciones y contenido descargable Mods y trucos - Aviso importante de complementos + Aviso importante sobre los complementos Para instalar mods y trucos, debe seleccionar una carpeta que contenga los directorios cheats/, romfs/, o exefs/ . ¡No podemos confirmar si éstos serán compatibles con su juego, así que tenga cuidado! Directorio no válido @@ -809,7 +817,7 @@ Aviso importante de contenido El contenido seleccionado no es de este juego.\n¿Instalar aun que\? Confirmar desinstalación - ¿Está seguro de que quiere desinstalar este complemento\? + ¿Estás seguro de que quieres desinstalar este complemento\? Verificar integridad Verificando... ¡La verificación de integridad ha sido un éxito! @@ -1067,7 +1075,7 @@ Fondos oscuros - Cuando utilice el modo oscuro, aplique fondos negros. + Cuando se usa el modo oscuro, aplicar fondos de pantalla negros. Carpeta diff --git a/src/android/app/src/main/res/values-fr/strings.xml b/src/android/app/src/main/res/values-fr/strings.xml index d7f77337cf..328141d70c 100644 --- a/src/android/app/src/main/res/values-fr/strings.xml +++ b/src/android/app/src/main/res/values-fr/strings.xml @@ -454,11 +454,12 @@ Résolution (Mode Portable/Mode TV) Mode VSync Filtre de fenêtre adaptatif - Netteté FSR - Détermine à quel point l\'image sera affinée lors de l\'utilisation du contraste dynamique FSR. Méthode d\'anticrénelage + Avancé + + Mode GPU Précision DMA Contrôle la précision du DMA. Une précision sûre peut résoudre les problèmes dans certains jeux, mais peut aussi affecter les performances dans certains cas. Si vous n\'êtes pas sûr, laissez ce paramètre sur Par défaut. Filtrage anisotropique @@ -474,15 +475,29 @@ Réduire les saccades en stockant et en chargeant localement les shaders générés Forcer les fréquences maximales (Adreno uniquement) Forcer le GPU à fonctionner à ses fréquences maximales possibles (les contraintes thermiques seront toujours appliquées). + Émulation GPU asynchrone + Ce contournement peut améliorer les performances en faisant tourner l\'émulation GPU de manière asynchrone au détriment de la fidélité graphique et de la stabilité (plantages plus fréquents) dus à des erreurs de cadence. Utiliser le vidage réactif Améliore la précision du rendu dans certains jeux au détriment des performances. + Activer l\'historique du tampon + Contournements + + Temps GPU rapide Ignorer l\'invalidation interne du CPU Ignore certaines invalidations de cache côté CPU lors des mises à jour mémoire, réduisant l\'utilisation du CPU et améliorant ses performances. Peut causer des bugs ou plantages sur certains jeux. + Emuler BGR565 Utiliser les shaders asynchrones Compile les shaders de manière asynchrone. Cela peut réduire les saccades mais peut aussi provoquer des problèmes graphiques. + Désactivé + Par défaut + + + Extensions + État dynamique étendu Désactivé État dynamique d\'entrée de sommet + Échantillonnage de shading Affichage Orientation @@ -508,6 +523,20 @@ Vider les journaux de débogage ligne par ligne Vide les journaux de débogage à chaque ligne écrite, facilitant le débogage en cas de plantage ou de gel. + + Journalisation GPU + Niveau de journalisation + Journaliser les appels API Vulkan + Extraire les shaders + Sauvegarder le shader SPIR-V complié dans les fichiers + Monitorer la mémoire GPU + Monitorer les allocations et désallocations de la mémoire GPU + Informations de débogage du pilote + Nombre d\'appels Vulkans récents à monitorer (par défaut : 512) + 64 à 4096 entrées + + Général + Moteur de sortie Volume @@ -590,6 +619,7 @@ Par défaut + Par défaut Chargement... Extinction en cours... Voulez-vous réinitialiser ce paramètre à sa valeur par défaut ? @@ -628,6 +658,7 @@ Par défaut Pilote non valide sélectionné Pilote déjà installé + %1$s (Installé) Pilote du GPU du système Installation du pilote... @@ -647,6 +678,7 @@ Installation en cours… Dernière Pilote recommandé : + Modèle GPU GPU non pris en charge Votre GPU ne prend pas en charge l\'injection de pilotes. Il n\'est pas recommandé de définir des pilotes personnalisés. @@ -656,6 +688,9 @@ Mode TV, région, langue Vidéo Niveau de précision, résolution, cache de shaders + Paramètres rapides + Activer les paramètres rapides + Autoriser l\'accès aux paramètres rapides par le balayage de l\'écran et le bouton du menu Audio Moteur de sortie, volume Contrôles @@ -663,6 +698,25 @@ Joueur %d Débogage Débogage CPU/GPU, API graphique, fastmem + Chemins personnalisés + Sauvegarder le répertoire des données + + + Sauvegarder le répertoire des données + Définir un chemin personnalisé pour les sauvegardes + Réinitialiser par défaut + Migrer les données de sauvegarde + Données de sauvegarde supprimées avec succès + Échec de la migration des données de sauvegarde + La destination contient déjà des données. Voulez-vous les écraser \? + Accorder la permission + Dossier NAND + Définir un chemin personnalisé pour le stockage NAND + Répertoire de carte SD + Définir un chemin personnalisé pour le stockage de la carte SD virtuelle + Chemin défini avec succès + Sauter + Info ID du programme, développeur, version @@ -676,6 +730,7 @@ Copier les détails Extensions Activer les mods, mises à jour et DLC + Temps de jeu : Réinitialiser le Temps de Jeu Réinitialiser le temps de jeu du jeu actuel à 0 seconde Cela effacera les données de temps de jeu du jeu actuel. Êtes-vous sûr \? @@ -683,6 +738,9 @@ Modifier le Temps de Jeu Heures Minutes + h + m + s Les heures doivent être comprises entre 0 et 9999 Les minutes doivent être comprises entre 0 et 59 Les secondes doivent être comprises entre 0 et 59 @@ -714,6 +772,7 @@ Confirmer la désinstallation Êtes-vous sûr de vouloir désinstaller cette extension ? Vérifier l\'intégrité + Vérification... La vérification de l\'intégrité a réussi ! La vérification de l\'intégrité a échoué ! Le contenu d\'un fichier peut être corrompu @@ -785,6 +844,7 @@ Opacité Réinitialiser l\'overlay Modifier l\'overlay + Aimanter à la grille Mettre en pause l\'émulation Reprendre l\'émulation Options de l\'overlay @@ -838,6 +898,32 @@ Boost (1700MHz) Rapide (2000MHz) + + Désactivé + Moyen (256) + Élevé (512) + + + Très petit (16 Mo) + Petit (32 Mo) + Normal (128 Mo) + Large (256 Mo) + Très large (512 Mo) + + + Très faible (4 Mo) + Faible (8 Mo) + Normal (16 Mo) + Moyen (32 Mo) + Élevé (64 Mo) + + + Très faible (32) + Faible (64) + Normal (128) + Moyen (256) + Élevé (512) + Celsius Fahrenheit @@ -853,6 +939,11 @@ Aucune + + Rapide + Moyen + Précis + Défaut Dangereux @@ -886,6 +977,26 @@ Paranoïaque Débogage + + Paramètres de Freedreno + Paramètres du pilote GPU + Préréglages rapides + Paramètres actuels + Paramètres avancés + Valeur de la variable + Ajouter la variable + Effacer tout + Configuration Freedreno sauvegardée + Toutes les variables Freedreno effacées + Variable %1$s ajouté + Préréglage \'%1$s\' appliqué + Le nom de la variable ne peut pas être vide + Échec de l\'assignation de la variable + À propos de la configuration Freedreno + Paramètres de Freedreno + Configurer les paramètres du pilote GPU pour ce jeu + Configuration Freedreno sauvegardée + Pavé directionnel Stick gauche @@ -902,26 +1013,37 @@ Material You Paramètres de l’App Thème et Couleur + Mode plein écran Changer le mode de thème Automatique Lumineux Sombre + Aucun + Arrière-plan noir Lorsque vous utilisez le thème sombre, appliquer un arrière-plan noir. + + Dossier + Afficher le bouton pour ajouter les dossiers de jeu + QLaunch + Afficher le bouton pour lancer QLaunch + Language de l\'application Changer la langue de l\'interface Suivre le système Couleur du thème + Eden Violet (Par défaut) Bleu Cyan Rouge + Vert Jaune Orange Rose @@ -940,6 +1062,8 @@ Applets + Modifier les frontends et paramètres des applets + Frontend personnalisé Applet réel @@ -948,7 +1072,39 @@ Mode avion Passe le mode avion au système d\'exploitation Switch + Activer l\'applet d\'overlay + + Gestionnaire de profil + Gérer les profils utilisateurs + Ajouter un utilisateur + Nouvel utilisateur + Modifier l\'utilisateur + Éditer + Supprimer + Nom d\'utilisateur + ID de l\'utilisateur (UUID) + Il s\'agit de l\'identifiant unique de ce profil utilisateur. Il ne peut pas être changé après sa création. + Générer + Avatar de l\'utilisateur + Sélectionner l\'image + Avatars du firmware + Avatars du firmware non disponibles. Veuillez installer le firmware pour utiliser cette fonctionnalité. + Restaurer à la valeur par défaut + Utilisateur actuel + Nombre d\'Utilisateurs Maximum Atteint + Vous ne pouvez pas créer plus du 8 profils utilisateurs. Veuillez supprimer un des profils existants pour en créer un nouveau. + Supprimer le profil \? + Échec de la création du profil utilisateur + Échec de la mise à jour du profil utilisateur + Échec du chargement de l\'image : %1$s + Échec de la sauvegarde de l\'image : %1$s + Erreur + Licences Mise à l\'échelle de haute qualité par AMD. - + Contenu externe + Ajouter un dossier + %1$d%% + + diff --git a/src/android/app/src/main/res/values-he/strings.xml b/src/android/app/src/main/res/values-he/strings.xml index 4cfd89217e..aae29cedec 100644 --- a/src/android/app/src/main/res/values-he/strings.xml +++ b/src/android/app/src/main/res/values-he/strings.xml @@ -359,8 +359,6 @@ רזולוציה (מעוגן/נייד) מצב VSync פילטר מתאם חלון - חדות FSR - קובע את מידת החדות בעת שימוש ב-FSR. שיטת Anti-aliasing diff --git a/src/android/app/src/main/res/values-hu/strings.xml b/src/android/app/src/main/res/values-hu/strings.xml index a42bac6779..5052773843 100644 --- a/src/android/app/src/main/res/values-hu/strings.xml +++ b/src/android/app/src/main/res/values-hu/strings.xml @@ -348,8 +348,6 @@ Felbontás (Kézi/Dockolt) VSync mód Ablakhoz alkalmazkodó szűrő - FSR élesség - Meghatározza, milyen éles lesz a kép az FSR dinamikus kontraszt használata közben. Élsimítási módszer diff --git a/src/android/app/src/main/res/values-id/strings.xml b/src/android/app/src/main/res/values-id/strings.xml index 2c575dcc8f..12ef4231dd 100644 --- a/src/android/app/src/main/res/values-id/strings.xml +++ b/src/android/app/src/main/res/values-id/strings.xml @@ -380,8 +380,6 @@ Resolusi (Handheld/Docked) Mode Sinkronisasi Vertikal Filter penyesuaian jendela - Ketajaman FSR - Menentukan seberapa tajam gambar akan terlihat saat menggunakan kontras dinamis FSR Metode anti-aliasing diff --git a/src/android/app/src/main/res/values-it/strings.xml b/src/android/app/src/main/res/values-it/strings.xml index e3b3009385..ef64042320 100644 --- a/src/android/app/src/main/res/values-it/strings.xml +++ b/src/android/app/src/main/res/values-it/strings.xml @@ -387,8 +387,6 @@ Risoluzione (Portatile/Docked) Modalità VSync Filtro adattivo della finestra - Nitidezza FSR - Determina quanto sarà nitida l\'immagine utilizzando il contrasto dinamico di FSR Metodo di anti-aliasing diff --git a/src/android/app/src/main/res/values-ko/strings.xml b/src/android/app/src/main/res/values-ko/strings.xml index 2f32665356..7cf519d514 100644 --- a/src/android/app/src/main/res/values-ko/strings.xml +++ b/src/android/app/src/main/res/values-ko/strings.xml @@ -6,8 +6,8 @@ 알림 권한이 부여되지 않았습니다! 프로세스 RAM: %1$d MB - 셰이더 - 빌드 중 + 구축 중 + 개 셰이더 (충전 중) 시스템: diff --git a/src/android/app/src/main/res/values-nb/strings.xml b/src/android/app/src/main/res/values-nb/strings.xml index 8fbc008bb1..d66dbf734b 100644 --- a/src/android/app/src/main/res/values-nb/strings.xml +++ b/src/android/app/src/main/res/values-nb/strings.xml @@ -329,8 +329,6 @@ Oppløsning (håndholdt/dokket) VSync-modus Filter for vindustilpasning - FSR-skarphet - Bestemmer bildekvalitet med FSR Anti-aliasing-metode diff --git a/src/android/app/src/main/res/values-pl/strings.xml b/src/android/app/src/main/res/values-pl/strings.xml index 40a2035200..1637c3d2b9 100644 --- a/src/android/app/src/main/res/values-pl/strings.xml +++ b/src/android/app/src/main/res/values-pl/strings.xml @@ -439,8 +439,6 @@ Rozdzielczość (Handheld/Zadokowany) Synchronizacja pionowa VSync Filtr adaptacji rozdzielczości - Ostrość FSR - Kontroluje ostrość obrazu w FSR. Metoda wygładzania krawędzi diff --git a/src/android/app/src/main/res/values-pt-rBR/strings.xml b/src/android/app/src/main/res/values-pt-rBR/strings.xml index e631d3566f..33605dfcba 100644 --- a/src/android/app/src/main/res/values-pt-rBR/strings.xml +++ b/src/android/app/src/main/res/values-pt-rBR/strings.xml @@ -430,8 +430,6 @@ Resolução (Portátil/Modo TV) Modo de VSync Filtro de Adaptação da Janela - Nitidez do FSR - Determina a nitidez da imagem ao utilizar o contraste dinâmico do FSR Método de Anti-aliasing diff --git a/src/android/app/src/main/res/values-pt-rPT/strings.xml b/src/android/app/src/main/res/values-pt-rPT/strings.xml index 51283f93a0..eaf1445133 100644 --- a/src/android/app/src/main/res/values-pt-rPT/strings.xml +++ b/src/android/app/src/main/res/values-pt-rPT/strings.xml @@ -352,8 +352,6 @@ Resolução (Portátil/Ancorado) Modo VSync Filtro de Adaptação da Janela - Nitidez do FSR - Determina a nitidez da imagem ao usar contraste dinâmico do FSR Método de Anti-Serrilhado diff --git a/src/android/app/src/main/res/values-ru/strings.xml b/src/android/app/src/main/res/values-ru/strings.xml index 4e5f2149e8..badb24bc09 100644 --- a/src/android/app/src/main/res/values-ru/strings.xml +++ b/src/android/app/src/main/res/values-ru/strings.xml @@ -458,8 +458,8 @@ Разрешение (портативное/в док-станции) Режим верт. синхронизации Фильтр адаптации окна - Резкость FSR - Определяет, насколько чётким будет изображение при использовании динамического контраста FSR. + Резкость FSR/SGSR + Определяет, насколько чётким будет изображение при использовании динамического контраста FSR или SGSR фильтров Метод сглаживания @@ -499,6 +499,8 @@ Принудительно запускает большинство игр в их максимальном нативном разрешении. Используйте значение 256 для максимальной производительности и 512 для максимального качества графики. Пропустить внутреннюю инвалидацию ЦП Пропускает некоторые инвалидации кэша на стороне ЦП при обновлениях памяти, уменьшая нагрузку на процессор и повышая производительность. Может вызывать сбои в некоторых играх. + Анти-мерцание + Принудительно заставляет обратные вызовы ГПУ-фильтра ожидать выполнения отправленных задач на ГПУ. Используйте с Быстрым режимом ГПУ, что бы избежать мерцаний с меньшим влиянием на производительность. Исправить эффекты размытия Частично убирает размытие в LA/EOW (Adreno A6XX - A7XX/ Turnip), полностью отключает его в Burnout. Внимание: может вызывать графические артефакты в других играх. Эмулировать BGR565 @@ -560,8 +562,6 @@ Ведение журнала ГПУ - Включить ведение журнала ГПУ - Записывать операции ГПУ в файл eden_gpu.log для отладки драйверов Adreno Уровень журналирования Уровень детализации логов ГПУ (больше значение = больше деталей, выше нагрузка) Записывать вызовы Vulkan API diff --git a/src/android/app/src/main/res/values-sr/strings.xml b/src/android/app/src/main/res/values-sr/strings.xml index 74b04d4c8f..238b70e3b3 100644 --- a/src/android/app/src/main/res/values-sr/strings.xml +++ b/src/android/app/src/main/res/values-sr/strings.xml @@ -351,8 +351,6 @@ Резолуција (ручно / прикључено) Всинц мод Филтер прилагођавања прозора - ФСР оштрина - Одређује колико ће се слика наоштрен трајати док користи \"ФСР\" динамички контраст Метода против алиасирања diff --git a/src/android/app/src/main/res/values-uk/strings.xml b/src/android/app/src/main/res/values-uk/strings.xml index cbf4211bac..284cd5ba2e 100644 --- a/src/android/app/src/main/res/values-uk/strings.xml +++ b/src/android/app/src/main/res/values-uk/strings.xml @@ -3,7 +3,7 @@ Цей застосунок запускає ігри для ігрової консолі Nintendo Switch. Він не містить ігор чи ключів.

Перш ніж почати, укажіть розташування файлу prod.keys ]]> у пам’яті вашого пристрою.

Дізнатися більше]]>
Сповіщення та помилки - Виводить сповіщення у разі виникнення проблем. + Показує сповіщення у разі виникнення проблем. Дозвіл на сповіщення не надано! Сповіщення емулятора Switch Eden Eden працює @@ -424,6 +424,9 @@ Точність CPU %1$s%2$s + Параметри запуску Homebrew + Параметри командного рядка, що передаються Homebrew при запуску (як-от noglsl). + Назва пристрою Режим док-станції @@ -458,8 +461,8 @@ Роздільна здатність (Портативний/Док) Режим верт. синхронізації Фільтр масштабування вікна - Різкість FSR - Визначає різкість зображення при використанні FSR. + Різкість FSR/SGSR + Визначає різкість зображення при використанні фільтрів FSR або SGSR. Згладжування @@ -499,6 +502,8 @@ Примушує більшість ігор працювати на їхній максимальній нативній роздільності. Використовуйте 256 для максимальної продуктивності та 512 для найкращої якості. Пропустити внутрішнє інвалідування CPU Пропускає деякі інвалідації кешу на стороні CPU під час оновлення пам\'яті, зменшуючи навантаження на процесор і покращуючи продуктивність. Може спричинити збої в деяких іграх. + Антимерехтіння + Змушує механізм синхронізації чекати, доки ГП завершить подані завдання. Використовуйте з режимом ГП «Швидко», щоб уникнути мерехтіння з меншими втратами продуктивності. Виправити ефекти світіння Зменшує розмиття світіння в LA/EOW (Adreno A6XX–A7XX / Turnip), прибирає світіння в Burnout. Увага: може спричинити графічні артефакти в інших іграх. Емулювати BGR565 @@ -560,8 +565,6 @@ Журналювання ГП - Увімкнути журналювання ГП - Журналювати операції ГП до eden_gpu.log для зневадження драйверів Adreno Рівень журналювання Рівень подробиць у журналі ГП (вищий = більше подробиць, більший вплив на швидкодію) Записувати виклики API Vulkan diff --git a/src/android/app/src/main/res/values-vi/strings.xml b/src/android/app/src/main/res/values-vi/strings.xml index 5aec3b1219..c9bdb77f3e 100644 --- a/src/android/app/src/main/res/values-vi/strings.xml +++ b/src/android/app/src/main/res/values-vi/strings.xml @@ -327,8 +327,6 @@ Độ phân giải (Handheld/Docked) Chế độ VSync Bộ lọc điều chỉnh cửa sổ - Độ sắc nét FSR - Độ sắc nét khi dùng FSR Phương pháp khử răng cưa diff --git a/src/android/app/src/main/res/values-zh-rCN/strings.xml b/src/android/app/src/main/res/values-zh-rCN/strings.xml index bedc53dc43..8a669986e6 100644 --- a/src/android/app/src/main/res/values-zh-rCN/strings.xml +++ b/src/android/app/src/main/res/values-zh-rCN/strings.xml @@ -29,8 +29,8 @@ 触控叠加层自动隐藏 在指定时间内未进行任何操作后,自动隐藏触控叠加层。 启用触控叠加层自动隐藏 - 使用控制器时隐藏触控叠加层 - 在使用实体控制器时自动隐藏触控叠加层,而当控制器断开时触控叠加层则会重新显现。 + 当使用控制器控制输入时隐藏触控叠加层 + 在使用实体控制器时自动隐藏触控叠加层,而当控制器断开连接时,触控叠加层则会重新显现。 切换“确认/返回”控制器按钮功能 在与本应用的界面交互时,交换 Android 的“确认”与“返回”按钮的处理方式,以匹配 Switch 和 Xbox 的风格。 @@ -66,6 +66,8 @@ 显示当前功耗和电池剩余容量 显示着色器编译信息 显示当前正在编译的着色器数量 + 管线工作线程 + 管理用于构建 Vulkan 管线的核心数量,数值越高则管线编译性能越好,但温度也会随之升高。 叠加层位置 选择叠加层在屏幕上显示的位置 左上 @@ -95,7 +97,7 @@ 勾选时,禁用映射内存上传的重排序功能,允许将上传与特定绘制关联。在某些情况下可能会降低性能。 同步核心速度 - 将核心速度与最大速度百分比同步,在不改变游戏实际速度的情况下提高性能。 + 将核心时钟周期速度与最大速度百分比同步,从而在不改变游戏实际速度的情况下提升性能。 启用主机 MMU 模拟 此优化可加速来宾程序的内存访问。启用后,来宾内存读取/写入将直接在内存中执行并利用主机的 MMU。禁用此功能将强制所有内存访问使用软件 MMU 模拟。 调试开关 @@ -104,7 +106,7 @@ NVDEC模拟 - 选择视频解码处理方式 + 播放过场与开场动画期间的视频解码处理方式(NVDEC)。 禁用 @@ -179,14 +181,14 @@ 隐藏满员房间 隐藏空房间 点击刷新重试 - 搜索房间… + 搜索游戏大厅… 首选游戏 - 大厅类型 + 游戏大厅类型 长度需为3-20个字符 必填 需要Web令牌,请前往高级设置 -> 系统 -> 网络 IP格式无效 - 必须为4-20个字符(仅字母数字、点号、连字符、下划线和空格) + 必须为4至20个字符,且仅包含字母、数字、点号、连字符、下划线和空格 用户名无效,请在系统→网络中检查设置 必须为48个字符,且仅包含小写字母a-z 端口需为1-65535 @@ -246,7 +248,7 @@ 安装 prod.keys 文件 需要密钥文件来解密游戏 跳过添加密钥文件? - 对于商业游戏,需要有效的密钥文件才能运行。如果没有密钥文件,将只能运行自制软件。 + 模拟零售版游戏需要有效的密钥。如果选择继续,仅有自制软件可以正常运行。 https://yuzu-mirror.github.io/help/quickstart/#guide-introduction 跳过添加固件? 设置模拟器数据 @@ -289,7 +291,7 @@ GPU驱动获取器 GPU 驱动管理器 安装替代的驱动程序以获得更好的性能和精度 - 高级选项 + 高级设置 更改模拟器设置 最近游玩 最近添加 @@ -416,6 +418,9 @@ CPU 精度 %1$s%2$s + 自制软件参数 + 启动时传递给自制软件的命令行参数(例如 -noglsl)。 + 设备名称 主机模式 @@ -430,89 +435,99 @@ CPU 超频 - 强制模拟的 CPU 以更高的时钟频率运行,从而解除某些 FPS 限制器。使用 Boost (1700MHz) 可让游戏以 Switch 的最高原生时钟运行,或使用 Fast (2000MHz) 以 2 倍时钟运行。 - 自定义CPU时钟 - 设置自定义的CPU时钟值。更高的值可能提高性能,但也可能导致游戏卡顿。建议范围为77-21000。 - 时钟 + 强制模拟的 CPU 以更高的时钟频率运行,从而消除某些帧率限制。使用“升频”(1700MHz)以在 Switch 的最高原生时钟频率运行,或使用“快速”(2000MHz)以 2 倍时钟频率运行。 + 自定义 CPU 时钟周期 + 设定自定义的 CPU 时钟周期值。更高的值可以提升性能,但也可能导致游戏冻结卡死。建议的赋值范围为77-21000。 + 时钟周期 内存布局 - (实验性) 更改模拟内存布局。此设置不会提高性能,但可能有助于通过模组使用高分辨率的游戏。不要在 RAM 为 8GB 或更少的手机上使用。 + (实验性) 更改模拟内存布局。此项设置并不会提升性能,但可能有助于游戏通过 mods 来利用高分辨率。请不要在内存不大于 8GB 的手机上使用。仅适用于 Dynamic(JIT)后端。 生成 网络令牌 - 用于创建公共房间的网络令牌。它是一个48个字符的字符串,仅包含小写字母a-z。 + 用于创建公共游戏大厅的 web token。这是一个仅包含小写字母 a-z 的 48 位字符串。 网络用户名 - 多人游戏房间中显示的用户名。必须为4-20个字符(仅字母数字、连字符、点号、下划线和空格)。 + 在多人游戏大厅中显示的用户名。用户名必须为4至20个字符,且仅可包含字母、数字、连字符、点号、下划线和空格。 网络 分辨率 (掌机模式/主机模式) 垂直同步模式 窗口滤镜 - FSR 锐化度 - 指定使用 FSR 时图像的锐化程度 + FSR/SGSR 锐度 + 确定在使用 FSR 或 SGSR 过滤器时的图像锐度 抗锯齿方式 高级 GPU 模式 - 控制 GPU 模拟的精确度。大部分游戏在性能或平衡模式下可以正常渲染,但部分游戏需要设置为精确。粒子效果通常只有在精确模式下才能正确显示。 + 控制 GPU 模拟模式。大多数游戏在“快速”或“平衡”模式下都能正常渲染,但有些游戏仍需使用“精确”模式。粒子效果通常只有在“精确”模式下才能正确渲染。 DMA 精度 - 控制 DMA 精度。安全精度可以修复某些游戏中的问题,但在某些情况下也可能影响性能。如果不确定,请保留为“默认”。 + 控制 DMA 的精准度。安全精度可以修复存在于某些游戏中的问题,但在某些情况下也会对性能造成影响。如不确定,请保持“默认”。 各向异性过滤 提高斜角的纹理质量 显存使用模式 - 控制显存分配策略 + 控制显存分配与释放策略 ASTC解码方式 - 选择ASTC压缩纹理的解码方式:CPU(慢速、安全)、GPU(快速、推荐)或CPU异步(无卡顿,可能导致问题) + 选择渲染时使用的 ASTC 压缩纹理解码方式:CPU(缓慢,安全),GPU(快速,推荐),或 CPU 异步(无卡顿,但可能导致问题) 同步内存操作 确保计算和内存操作之间的数据一致性。 此选项应能修复某些游戏中的问题,但在某些情况下可能会降低性能。 使用Unreal Engine 4的游戏似乎受影响最大。 磁盘着色器缓存 将生成的着色器缓存于磁盘中并进行读取,以减少卡顿。 强制最大时钟 (仅限 Adreno) - 强制 GPU 以最大时钟运行 (仍被温控限制)。 + 强制 GPU 以最大时钟运行 (温控依然生效)。 GPU 异步模拟 + 此技巧可通过异步运行 GPU 模拟来提升性能,但在执行与时序相关的操作时,可能带来图形显示问题以及增加崩溃概率。 异步呈现 + 此技巧通过将图形呈现移至独立的 CPU 线程来提升性能,但可能会带来图形显示问题。 启用反应性刷新 - 通过牺牲性能来提高某些游戏的渲染精度。 + 通过牺牲性能来提升某些游戏的渲染精度。 启用缓冲区历史 启用对先前缓冲区状态的访问。此选项可在某些游戏中提升渲染质量并保持性能的一致性。 优化顶点缓冲区 + 启用经过优化的顶点缓冲区绑定以提升性能。需要 Mesa 26.0 及以上版本的 Turnip 或 QCOM 驱动程序。若使用较旧版本的 Turnip 驱动 (25.3 及以下版本) 则会导致崩溃。 + Hacks - GPU 超频频率 + 快速 GPU 时间 强制大多数游戏以其最高原生分辨率运行。设置为 256 可获得最佳性能,设置为 512 可获得最佳画面保真度。 跳过CPU内部无效化 - 在内存更新期间跳过某些CPU端缓存无效化,减少CPU使用率并提高其性能。可能会导致某些游戏出现故障或崩溃。 + 在更新内存时跳过某些 CPU 端的缓存失效操作,从而降低 CPU 占用率并提升性能。可能会在某些游戏中引发故障点或崩溃。 + 防闪烁 + 强制 GPU 围栏回调等待已提交的 GPU 任务。配合“快速 GPU 模式”一起使用,以牺牲少量性能为代价来避免画面闪烁现象。 修复 Bloom 效果 - 减少《塞尔达传说:智慧的再现》(Adreno A6XX - A7XX/ Turnip)中的 bloom 模糊,并移除《Burnout》中的 bloom 效果。警告:可能会导致在其他游戏中出现图形异常。 + 减少《智慧的再现》和《众神的三角力量2》(Adreno A6XX - A7XX/ Turnip)中的 bloom 模糊,并移除《Burnout》中的 bloom 效果。警告:可能会导致在其他游戏中出现图形异常。 模拟 BGR565 - 修复了游戏中的颜色反转以及出现的异常画面瑕疵或奇怪阴影问题 + 修复游戏中的颜色反转或是异常的画面瑕疵或阴影问题 + 启用旧版缩放处理 + 启用通过使用快速缩放路径,来为游戏提供缩放配置处理的传统处理方式 使用异步着色器 - 异步编译着色器。这可能会减少卡顿,但也可能会导致图形错误。 - GPU 还原设置 - 配置基于 GPU 的纹理还原参数,或将其完全禁用。调整这些设置以平衡性能与纹理加载质量。 - 启用 GPU 还原 + 以异步方式编译着色器。采用此方式或可减少卡顿,但也可能引入故障点。 + GPU Unswizzle 设置 + 配置基于 GPU 的纹理 unswizzling 参数,或完全禁用该功能。通过调整这些设置,以尝试在性能与纹理加载质量之间取得平衡。 + 启用 GPU Unswizzle 禁用 - GPU 还原最大纹理尺寸 - 设置基于 GPU 的纹理还原的最大尺寸(单位:MiB)。\n虽然 GPU 在处理中型和大型纹理时速度更快,但对于非常小的纹理,CPU 的效率可能更高。\n调整此设置,以便在 GPU 加速和 CPU 开销之间找到最佳平衡点。 - GPU 还原流大小 - 设置每帧还原大型纹理的数据限制。较高的数值可以加快纹理加载速度,但代价是增加帧延迟(影响响应速度/平滑度);较低的数值可以减少 GPU 开销,但可能会导致明显的纹理突然出现(Pop-in)现象。 - GPU 还原块大小 - 定义了 3D 纹理在单个批次(Batch)中处理的深度切片(Depth Slices)数量。增加此数值可以提升强力 GPU 的吞吐效率,但在性能较弱的硬件上可能会引起卡顿或驱动程序超时(Driver Timeouts)。 + GPU Unswizzle 最大纹理尺寸 + 设置基于 GPU 的纹理 unswizzling 的最大尺寸(MB)。虽然 GPU 处理中等和大型纹理的速度更快,但对于非常小的纹理,CPU 可能更为高效。通过调节此项设置,以尝试在 GPU 加速与 CPU 开销之间找到平衡。 + GPU Unswizzle 流大小 + 设置用于 unswizzling 大型纹理时的每帧数据限制。较高的数值可以加速纹理的加载过程,但会带来更高的帧延迟。而较低的数值则可以降低 GPU 的开销,但也可能会导致可见的纹理闪现。 + GPU Unswizzle 块大小 + 定义了 3D 纹理每批次处理的深度切片数量。增加此数值可在高性能 GPU 上提升吞吐效率,但在性能较弱的硬件上可能会导致卡顿或驱动超时。 默认 扩展 扩展动态状态 + 控制扩展动态状态 (EDS) 可使用的功能数目。较高的数值将允许根据驱动程序支持的动态状态来减少管线编译的次数。 已禁用 顶点输入动态状态 + 启用此功能可实现更灵活的顶点输入处理,可能减少顶点/缓冲区的管线编译时间。 采样着色 - 允许片段着色器在多采样片段中每个样本执行一次,而不是每个片段执行一次。以提高性能为代价改善图形质量。 + 允许片段着色器在多采样片段中对每个采样点执行一次操作,而非对每个片段执行一次。在提升图形显示质量的同时,会牺牲一部分性能。 显示 @@ -527,8 +542,8 @@ CPU - 使用自动存根 - 自动补全缺失的服务和功能。可提高兼容性但可能导致崩溃和稳定性问题。 + 使用自动占位处理 + 自动为缺失的服务和功能生成占位代码。这可能会提高兼容性,但也可能导致崩溃和稳定性问题 GPU API @@ -536,7 +551,7 @@ 将图形 API 设置为较慢的调试模式。 BCn 纹理补丁 在 Adreno GPU 上覆盖自动 BCn 纹理格式检测。通常根据 Android 版本自动检测(在 API 28 及以上启用)。 - Fastmem 内存访问 + Fastmem 日志记录 按行刷新调试日志 @@ -544,8 +559,6 @@ GPU 日志 - 启用 GPU 日志 - 将 GPU 操作记录至 eden_gpu.log 以供调试 Adreno 驱动 日志等级 GPU 日志的详细级别(数值越高 = 细节越多,开销越大) 记录 Vulkan API 调用 @@ -603,7 +616,7 @@ 未使用 输入映射过滤器 选择一个设备过滤输入映射 - 控制器自动映射 + 自动映射控制器键位 选择一个设备以尝试自动映射 尝试为 %1$s 自动映射 控制器类型 @@ -649,9 +662,9 @@ 正在关闭… 您要将此设定重置为默认值吗? 恢复默认 - 重置所有高级选项 + 重置所有高级设置 重置所有设置项? - 所有高级选项都将被重置,此动作无法还原。 + 所有高级设置都将重置为默认。此操作无法撤销。 重置设置项 关闭 了解更多 @@ -719,10 +732,10 @@ 声音 输出引擎及音量 控制 - 使用控制器来映射输入 + 映射控制器键位输入 玩家 %d 调试 - CPU/GPU 调试、图形 API 及 fastmem 内存访问 + CPU/GPU 调试、图形 API 以及 fastmem 自定义路径 存档目录 @@ -778,7 +791,7 @@ 由于着色器缓存需要重新生成,您将遇到更多卡顿 着色器缓存清除成功 已清理着色器 - Eden 已自动清除所有着色器缓存以维护 Vulkan 管线验证。这在切换 GPU 驱动程序时是必需的,以防止崩溃和图形损坏。在着色器重建期间您可能会遇到一些卡顿。 + Eden 已自动清空所有着色器缓存,以维持 Vulkan 管线验证流程。在切换 GPU 驱动程序时,此操作是必要的,可防止崩溃和画面异常。在着色器重构期间,您可能会感受到些许卡顿。 附加项: %1$s 保存数据 管理此游戏的保存数据 @@ -866,15 +879,15 @@ 相对摇杆中心 十字方向键滑动 触觉反馈 - 显示控制器 - 隐藏控制器 + 显示触控叠加层 + 隐藏触控叠加层 全部切换 调整触控叠加层 缩放 不透明度 重置触控叠加层 编辑触控叠加层 - 截图到网格 + 对齐到网格 暂停模拟 继续模拟 触控叠加层选项 @@ -970,7 +983,7 @@ - 性能 + 快速 平衡 精确 @@ -979,8 +992,8 @@ 不安全 安全 - 保守模式 - 激进模式 + 保守式 + 主动式 即时 (关闭) @@ -1002,9 +1015,9 @@ 拉伸窗口 - 高精度 - 低精度 - 偏执模式 + 精准 + 不安全 + 极致 调试 diff --git a/src/android/app/src/main/res/values-zh-rTW/strings.xml b/src/android/app/src/main/res/values-zh-rTW/strings.xml index dd56e9968d..4e1746e0d3 100644 --- a/src/android/app/src/main/res/values-zh-rTW/strings.xml +++ b/src/android/app/src/main/res/values-zh-rTW/strings.xml @@ -437,8 +437,6 @@ 解析度 (手提/底座) 垂直同步 視窗適應過濾器 - FSR 銳化度 - 使用 FSR 時圖片的銳化程度 抗鋸齒 diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml index 99838b98cc..f3a2a069e7 100644 --- a/src/android/app/src/main/res/values/arrays.xml +++ b/src/android/app/src/main/res/values/arrays.xml @@ -240,6 +240,8 @@ @string/scaling_filter_bspline @string/scaling_filter_mitchell @string/scaling_filter_spline1 + @string/scaling_filter_sgsr + @string/scaling_filter_sgsr_edge @@ -256,6 +258,8 @@ 10 11 12 + 13 + 14 diff --git a/src/android/app/src/main/res/values/colors.xml b/src/android/app/src/main/res/values/colors.xml index 2198022855..472567b323 100644 --- a/src/android/app/src/main/res/values/colors.xml +++ b/src/android/app/src/main/res/values/colors.xml @@ -1 +1 @@ -#ffd700 +#1F143C diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 7372fa5e34..c67c2f279f 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -434,6 +434,9 @@ CPU accuracy %1$s%2$s + Homebrew Args + Command-line arguments passed to homebrew at launch (e.g. -noglsl). + Device name Docked Mode @@ -468,8 +471,8 @@ Resolution (Handheld/Docked) VSync mode Window adapting filter - FSR sharpness - Determines how sharpened the image will look while using FSR\'s dynamic contrast + FSR/SGSR sharpness + Determines how sharpened the image will look while using FSR or SGSR filters Anti-aliasing method @@ -500,6 +503,8 @@ Improves rendering accuracy in some games at the cost of performance. Enable buffer history Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. + Enable GPU Buffer Readback + Preserves GPU-modified buffer data by reading it back before uploads. Some games require this to render certain effects properly. May cause issues if the hardware cannot handle the additional workload. Optimized Vertex Buffers Enables optimized vertex buffer binding for improved performance. Requires Mesa 26.0+ Turnip drivers/ QCOM drivers. Will crash on older Turnip drivers (25.3 and below). @@ -509,6 +514,8 @@ Forces most games to run at their highest native resolution. Use 256 for maximal performance and 512 for maximal graphics fidelity. Skip CPU Inner Invalidation Skips certain CPU-side cache invalidations during memory updates, reducing CPU usage and improving it\'s performance. This may cause glitches or crashes on some games. + Anti-Flicker + Forces GPU fence callbacks to wait for submitted GPU work. Use with Fast GPU mode, to avoid flicker with lower performance impact. Fix Bloom Effects Reduces bloom blur in LA/EOW (Adreno A6XX - A7XX/ Turnip), removes bloom in Burnout. Warning: may cause graphical artifacts in other games. Emulate BGR565 @@ -570,14 +577,16 @@ GPU Logging - Enable GPU Logging - Log GPU operations to eden_gpu.log for debugging Adreno drivers Log Level Detail level for GPU logs (higher = more detail, more overhead) Log Vulkan API Calls Track all Vulkan API calls in ring buffer - Dump Shaders - Save compiled shader SPIR-V to files + Dump SPIR-V Shaders + Save recompiled SPIR-V binaries (.spv) to dump folder. Inspect with spirv-dis/spirv-cross/spirv-val. + Dump Guest (Maxwell) Shaders + Save Maxwell guest shader bytecode files (*.ash) to dump folder. Inspect with nvdisasm. + Dump Maxwell Macros + Save Maxwell macro program files (*.macro) to dump folder. Inspect with envydis. Track GPU Memory Monitor GPU memory allocations and deallocations Driver Debug Info @@ -1078,6 +1087,8 @@ B-Spline Mitchell MMPX + Snapdragon GSR + Snapdragon GSR EdgeDir None diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt index e31fd2c5b9..4d0b130afc 100644 --- a/src/audio_core/CMakeLists.txt +++ b/src/audio_core/CMakeLists.txt @@ -247,11 +247,11 @@ if(ANDROID) target_compile_definitions(audio_core PUBLIC HAVE_OBOE) else() target_sources(audio_core PRIVATE - sink/sdl2_sink.cpp - sink/sdl2_sink.h) + sink/sdl3_sink.cpp + sink/sdl3_sink.h) - target_link_libraries(audio_core PRIVATE SDL2::SDL2) - target_compile_definitions(audio_core PRIVATE HAVE_SDL2) + target_link_libraries(audio_core PRIVATE SDL3::SDL3) + target_compile_definitions(audio_core PRIVATE HAVE_SDL3) endif() create_target_directory_groups(audio_core) diff --git a/src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp index 45a69eca03..af37fb4685 100644 --- a/src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp +++ b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp @@ -29,8 +29,96 @@ void AudioRenderer::Start() { CreateSinkStreams(); mailbox.Initialize(AppMailboxId::AudioRenderer); + // Main AudioRenderer thread, responsible for processing the command lists. + main_thread = std::jthread([this](std::stop_token stop_token) { + Common::SetCurrentThreadName("DSP_AudioRenderer_Main"); + Common::SetCurrentThreadPriority(Common::ThreadPriority::High); - main_thread = std::jthread([this](std::stop_token stop_token) { Main(stop_token); }); + // TODO: Create buffer map/unmap thread + mailbox + // TODO: Create gMix devices, initialize them here + + if (mailbox.Receive(Direction::DSP) != Message::InitializeOK) { + LOG_ERROR(Service_Audio, "ADSP Audio Renderer -- Failed to receive initialize message from host!"); + return; + } + + mailbox.Send(Direction::Host, Message::InitializeOK); + + // 0.12 seconds (2,304,000 / 19,200,000) + constexpr u64 max_process_time{2'304'000ULL}; + while (!stop_token.stop_requested()) { + auto msg{mailbox.Receive(Direction::DSP)}; + switch (msg) { + case Message::Shutdown: + mailbox.Send(Direction::Host, Message::Shutdown); + return; + + case Message::Render: { + if (system.IsShuttingDown()) { + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + mailbox.Send(Direction::Host, Message::RenderResponse); + continue; + } + std::array buffers_reset{}; + std::array render_times_taken{}; + const auto start_time{system.CoreTiming().GetGlobalTimeUs().count()}; + + for (u32 index = 0; index < MaxRendererSessions; index++) { + auto& command_buffer{command_buffers[index]}; + auto& command_list_processor{command_list_processors[index]}; + + // Check this buffer is valid, as it may not be used. + if (command_buffer.buffer != 0) { + // If there are no remaining commands (from the previous list), + // this is a new command list, initialize it. + if (command_buffer.remaining_command_count == 0) { + command_list_processor.Initialize(system, *command_buffer.process, + command_buffer.buffer, + command_buffer.size, streams[index]); + } + + if (command_buffer.reset_buffer && !buffers_reset[index]) { + streams[index]->ClearQueue(); + buffers_reset[index] = true; + } + + u64 max_time{max_process_time}; + if (index == 1 && command_buffer.applet_resource_user_id == + command_buffers[0].applet_resource_user_id) { + max_time = max_process_time - render_times_taken[0]; + if (render_times_taken[0] > max_process_time) { + max_time = 0; + } + } + + max_time = (std::min)(command_buffer.time_limit, max_time); + command_list_processor.SetProcessTimeMax(max_time); + + if (index == 0) { + streams[index]->WaitFreeSpace(stop_token); + } + + // Process the command list + { + render_times_taken[index] = + command_list_processor.Process(index) - start_time; + } + + const auto end_time{system.CoreTiming().GetGlobalTimeUs().count()}; + + command_buffer.remaining_command_count = + command_list_processor.GetRemainingCommandCount(); + command_buffer.render_time_taken_us = end_time - start_time; + } + } + mailbox.Send(Direction::Host, Message::RenderResponse); + } break; + default: + LOG_WARNING(Service_Audio, "ADSP AudioRenderer received an invalid message, msg={:02X}!", msg); + break; + } + } + }); mailbox.Send(Direction::DSP, Message::InitializeOK); if (mailbox.Receive(Direction::Host) != Message::InitializeOK) { @@ -129,95 +217,4 @@ void AudioRenderer::CreateSinkStreams() { } } -void AudioRenderer::Main(std::stop_token stop_token) { - Common::SetCurrentThreadName("DSP_AudioRenderer_Main"); - Common::SetCurrentThreadPriority(Common::ThreadPriority::High); - - // TODO: Create buffer map/unmap thread + mailbox - // TODO: Create gMix devices, initialize them here - - if (mailbox.Receive(Direction::DSP) != Message::InitializeOK) { - LOG_ERROR(Service_Audio, "ADSP Audio Renderer -- Failed to receive initialize message from host!"); - return; - } - - mailbox.Send(Direction::Host, Message::InitializeOK); - - // 0.12 seconds (2,304,000 / 19,200,000) - constexpr u64 max_process_time{2'304'000ULL}; - - while (!stop_token.stop_requested()) { - auto msg{mailbox.Receive(Direction::DSP)}; - switch (msg) { - case Message::Shutdown: - mailbox.Send(Direction::Host, Message::Shutdown); - return; - - case Message::Render: { - if (system.IsShuttingDown()) { - std::this_thread::sleep_for(std::chrono::milliseconds(200)); - mailbox.Send(Direction::Host, Message::RenderResponse); - continue; - } - std::array buffers_reset{}; - std::array render_times_taken{}; - const auto start_time{system.CoreTiming().GetGlobalTimeUs().count()}; - - for (u32 index = 0; index < MaxRendererSessions; index++) { - auto& command_buffer{command_buffers[index]}; - auto& command_list_processor{command_list_processors[index]}; - - // Check this buffer is valid, as it may not be used. - if (command_buffer.buffer != 0) { - // If there are no remaining commands (from the previous list), - // this is a new command list, initialize it. - if (command_buffer.remaining_command_count == 0) { - command_list_processor.Initialize(system, *command_buffer.process, - command_buffer.buffer, - command_buffer.size, streams[index]); - } - - if (command_buffer.reset_buffer && !buffers_reset[index]) { - streams[index]->ClearQueue(); - buffers_reset[index] = true; - } - - u64 max_time{max_process_time}; - if (index == 1 && command_buffer.applet_resource_user_id == - command_buffers[0].applet_resource_user_id) { - max_time = max_process_time - render_times_taken[0]; - if (render_times_taken[0] > max_process_time) { - max_time = 0; - } - } - - max_time = (std::min)(command_buffer.time_limit, max_time); - command_list_processor.SetProcessTimeMax(max_time); - - if (index == 0) { - streams[index]->WaitFreeSpace(stop_token); - } - - // Process the command list - { - render_times_taken[index] = - command_list_processor.Process(index) - start_time; - } - - const auto end_time{system.CoreTiming().GetGlobalTimeUs().count()}; - - command_buffer.remaining_command_count = - command_list_processor.GetRemainingCommandCount(); - command_buffer.render_time_taken_us = end_time - start_time; - } - } - mailbox.Send(Direction::Host, Message::RenderResponse); - } break; - default: - LOG_WARNING(Service_Audio, "ADSP AudioRenderer received an invalid message, msg={:02X}!", msg); - break; - } - } -} - } // namespace AudioCore::ADSP::AudioRenderer diff --git a/src/audio_core/adsp/apps/audio_renderer/audio_renderer.h b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.h index db6b893d43..a7a21e76af 100644 --- a/src/audio_core/adsp/apps/audio_renderer/audio_renderer.h +++ b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project @@ -82,11 +82,6 @@ public: u64 GetRenderingStartTick(s32 session_id) const noexcept; private: - /** - * Main AudioRenderer thread, responsible for processing the command lists. - */ - void Main(std::stop_token stop_token); - /** * Creates the streams which will receive the processed samples. */ diff --git a/src/audio_core/adsp/apps/opus/opus_decoder.cpp b/src/audio_core/adsp/apps/opus/opus_decoder.cpp index 8a009c04c7..1ce81d287a 100644 --- a/src/audio_core/adsp/apps/opus/opus_decoder.cpp +++ b/src/audio_core/adsp/apps/opus/opus_decoder.cpp @@ -37,7 +37,9 @@ bool IsValidMultiStreamStreamCounts(s32 total_stream_count, s32 stereo_stream_co } // namespace OpusDecoder::OpusDecoder(Core::System& system_) : system{system_} { - init_thread = std::jthread([this](std::stop_token stop_token) { Init(stop_token); }); + init_thread = std::jthread([this](std::stop_token stop_token) { + Init(stop_token); + }); } OpusDecoder::~OpusDecoder() { @@ -64,206 +66,203 @@ u32 OpusDecoder::Receive(Direction dir, std::stop_token stop_token) { return mailbox.Receive(dir, stop_token); } -void OpusDecoder::Init(std::stop_token stop_token) { +void OpusDecoder::Init(std::stop_token rc_stop_token) { Common::SetCurrentThreadName("DSP_OpusDecoder_Init"); - if (Receive(Direction::DSP, stop_token) != Message::Start) { - LOG_ERROR(Service_Audio, - "DSP OpusDecoder failed to receive Start message. Opus initialization failed."); + if (Receive(Direction::DSP, rc_stop_token) != Message::Start) { + LOG_ERROR(Service_Audio, "DSP OpusDecoder failed to receive Start message. Opus initialization failed."); return; } - main_thread = std::jthread([this](std::stop_token st) { Main(st); }); + // Main OpusDecoder thread, responsible for processing the incoming Opus packets. + main_thread = std::jthread([this](std::stop_token stop_token) { + Common::SetCurrentThreadName("DSP_OpusDecoder_Main"); + while (!stop_token.stop_requested()) { + auto msg = Receive(Direction::DSP, stop_token); + switch (msg) { + case Shutdown: + Send(Direction::Host, Message::ShutdownOK); + return; + + case GetWorkBufferSize: { + auto channel_count = static_cast(shared_memory->host_send_data[0]); + + ASSERT(IsValidChannelCount(channel_count)); + + shared_memory->dsp_return_data[0] = OpusDecodeObject::GetWorkBufferSize(channel_count); + Send(Direction::Host, Message::GetWorkBufferSizeOK); + } break; + + case InitializeDecodeObject: { + auto buffer = shared_memory->host_send_data[0]; + auto buffer_size = shared_memory->host_send_data[1]; + auto sample_rate = static_cast(shared_memory->host_send_data[2]); + auto channel_count = static_cast(shared_memory->host_send_data[3]); + + ASSERT(sample_rate >= 0); + ASSERT(IsValidChannelCount(channel_count)); + ASSERT(buffer_size >= OpusDecodeObject::GetWorkBufferSize(channel_count)); + + auto& decoder_object = OpusDecodeObject::Initialize(buffer, buffer); + shared_memory->dsp_return_data[0] = + decoder_object.InitializeDecoder(sample_rate, channel_count); + + Send(Direction::Host, Message::InitializeDecodeObjectOK); + } break; + + case ShutdownDecodeObject: { + auto buffer = shared_memory->host_send_data[0]; + [[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1]; + + auto& decoder_object = OpusDecodeObject::Initialize(buffer, buffer); + shared_memory->dsp_return_data[0] = decoder_object.Shutdown(); + + Send(Direction::Host, Message::ShutdownDecodeObjectOK); + } break; + + case DecodeInterleaved: { + auto start_time = system.CoreTiming().GetGlobalTimeUs(); + + auto buffer = shared_memory->host_send_data[0]; + auto input_data = shared_memory->host_send_data[1]; + auto input_data_size = shared_memory->host_send_data[2]; + auto output_data = shared_memory->host_send_data[3]; + auto output_data_size = shared_memory->host_send_data[4]; + auto final_range = static_cast(shared_memory->host_send_data[5]); + auto reset_requested = shared_memory->host_send_data[6]; + + u32 decoded_samples{0}; + + auto& decoder_object = OpusDecodeObject::Initialize(buffer, buffer); + s32 error_code{OPUS_OK}; + if (reset_requested) { + error_code = decoder_object.ResetDecoder(); + } + + if (error_code == OPUS_OK) { + error_code = decoder_object.Decode(decoded_samples, output_data, output_data_size, + input_data, input_data_size); + } + + if (error_code == OPUS_OK) { + if (final_range && decoder_object.GetFinalRange() != final_range) { + error_code = OPUS_INVALID_PACKET; + } + } + + auto end_time = system.CoreTiming().GetGlobalTimeUs(); + shared_memory->dsp_return_data[0] = error_code; + shared_memory->dsp_return_data[1] = decoded_samples; + shared_memory->dsp_return_data[2] = (end_time - start_time).count(); + + Send(Direction::Host, Message::DecodeInterleavedOK); + } break; + + case MapMemory: { + [[maybe_unused]] auto buffer = shared_memory->host_send_data[0]; + [[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1]; + Send(Direction::Host, Message::MapMemoryOK); + } break; + + case UnmapMemory: { + [[maybe_unused]] auto buffer = shared_memory->host_send_data[0]; + [[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1]; + Send(Direction::Host, Message::UnmapMemoryOK); + } break; + + case GetWorkBufferSizeForMultiStream: { + auto total_stream_count = static_cast(shared_memory->host_send_data[0]); + auto stereo_stream_count = static_cast(shared_memory->host_send_data[1]); + + ASSERT(IsValidMultiStreamStreamCounts(total_stream_count, stereo_stream_count)); + + shared_memory->dsp_return_data[0] = OpusMultiStreamDecodeObject::GetWorkBufferSize( + total_stream_count, stereo_stream_count); + Send(Direction::Host, Message::GetWorkBufferSizeForMultiStreamOK); + } break; + + case InitializeMultiStreamDecodeObject: { + auto buffer = shared_memory->host_send_data[0]; + auto buffer_size = shared_memory->host_send_data[1]; + auto sample_rate = static_cast(shared_memory->host_send_data[2]); + auto channel_count = static_cast(shared_memory->host_send_data[3]); + auto total_stream_count = static_cast(shared_memory->host_send_data[4]); + auto stereo_stream_count = static_cast(shared_memory->host_send_data[5]); + // Nintendo seem to have a bug here, they try to use &host_send_data[6] for the channel + // mappings, but [6] is never set, and there is not enough room in the argument data for + // more than 40 channels, when 255 are possible. + // It also means the mapping values are undefined, though likely always 0, + // and the mappings given by the game are ignored. The mappings are copied to this + // dedicated buffer host side, so let's do as intended. + auto mappings = shared_memory->channel_mapping.data(); + + ASSERT(IsValidMultiStreamStreamCounts(total_stream_count, stereo_stream_count)); + ASSERT(sample_rate >= 0); + ASSERT(buffer_size >= OpusMultiStreamDecodeObject::GetWorkBufferSize( + total_stream_count, stereo_stream_count)); + + auto& decoder_object = OpusMultiStreamDecodeObject::Initialize(buffer, buffer); + shared_memory->dsp_return_data[0] = decoder_object.InitializeDecoder( + sample_rate, total_stream_count, channel_count, stereo_stream_count, mappings); + + Send(Direction::Host, Message::InitializeMultiStreamDecodeObjectOK); + } break; + + case ShutdownMultiStreamDecodeObject: { + auto buffer = shared_memory->host_send_data[0]; + [[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1]; + + auto& decoder_object = OpusMultiStreamDecodeObject::Initialize(buffer, buffer); + shared_memory->dsp_return_data[0] = decoder_object.Shutdown(); + + Send(Direction::Host, Message::ShutdownMultiStreamDecodeObjectOK); + } break; + + case DecodeInterleavedForMultiStream: { + auto start_time = system.CoreTiming().GetGlobalTimeUs(); + + auto buffer = shared_memory->host_send_data[0]; + auto input_data = shared_memory->host_send_data[1]; + auto input_data_size = shared_memory->host_send_data[2]; + auto output_data = shared_memory->host_send_data[3]; + auto output_data_size = shared_memory->host_send_data[4]; + auto final_range = static_cast(shared_memory->host_send_data[5]); + auto reset_requested = shared_memory->host_send_data[6]; + + u32 decoded_samples{0}; + + auto& decoder_object = OpusMultiStreamDecodeObject::Initialize(buffer, buffer); + s32 error_code{OPUS_OK}; + if (reset_requested) { + error_code = decoder_object.ResetDecoder(); + } + + if (error_code == OPUS_OK) { + error_code = decoder_object.Decode(decoded_samples, output_data, output_data_size, + input_data, input_data_size); + } + + if (error_code == OPUS_OK) { + if (final_range && decoder_object.GetFinalRange() != final_range) { + error_code = OPUS_INVALID_PACKET; + } + } + + auto end_time = system.CoreTiming().GetGlobalTimeUs(); + shared_memory->dsp_return_data[0] = error_code; + shared_memory->dsp_return_data[1] = decoded_samples; + shared_memory->dsp_return_data[2] = (end_time - start_time).count(); + + Send(Direction::Host, Message::DecodeInterleavedForMultiStreamOK); + } break; + + default: + LOG_ERROR(Service_Audio, "Invalid OpusDecoder command {}", msg); + continue; + } + } + }); running = true; Send(Direction::Host, Message::StartOK); } -void OpusDecoder::Main(std::stop_token stop_token) { - Common::SetCurrentThreadName("DSP_OpusDecoder_Main"); - - while (!stop_token.stop_requested()) { - auto msg = Receive(Direction::DSP, stop_token); - switch (msg) { - case Shutdown: - Send(Direction::Host, Message::ShutdownOK); - return; - - case GetWorkBufferSize: { - auto channel_count = static_cast(shared_memory->host_send_data[0]); - - ASSERT(IsValidChannelCount(channel_count)); - - shared_memory->dsp_return_data[0] = OpusDecodeObject::GetWorkBufferSize(channel_count); - Send(Direction::Host, Message::GetWorkBufferSizeOK); - } break; - - case InitializeDecodeObject: { - auto buffer = shared_memory->host_send_data[0]; - auto buffer_size = shared_memory->host_send_data[1]; - auto sample_rate = static_cast(shared_memory->host_send_data[2]); - auto channel_count = static_cast(shared_memory->host_send_data[3]); - - ASSERT(sample_rate >= 0); - ASSERT(IsValidChannelCount(channel_count)); - ASSERT(buffer_size >= OpusDecodeObject::GetWorkBufferSize(channel_count)); - - auto& decoder_object = OpusDecodeObject::Initialize(buffer, buffer); - shared_memory->dsp_return_data[0] = - decoder_object.InitializeDecoder(sample_rate, channel_count); - - Send(Direction::Host, Message::InitializeDecodeObjectOK); - } break; - - case ShutdownDecodeObject: { - auto buffer = shared_memory->host_send_data[0]; - [[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1]; - - auto& decoder_object = OpusDecodeObject::Initialize(buffer, buffer); - shared_memory->dsp_return_data[0] = decoder_object.Shutdown(); - - Send(Direction::Host, Message::ShutdownDecodeObjectOK); - } break; - - case DecodeInterleaved: { - auto start_time = system.CoreTiming().GetGlobalTimeUs(); - - auto buffer = shared_memory->host_send_data[0]; - auto input_data = shared_memory->host_send_data[1]; - auto input_data_size = shared_memory->host_send_data[2]; - auto output_data = shared_memory->host_send_data[3]; - auto output_data_size = shared_memory->host_send_data[4]; - auto final_range = static_cast(shared_memory->host_send_data[5]); - auto reset_requested = shared_memory->host_send_data[6]; - - u32 decoded_samples{0}; - - auto& decoder_object = OpusDecodeObject::Initialize(buffer, buffer); - s32 error_code{OPUS_OK}; - if (reset_requested) { - error_code = decoder_object.ResetDecoder(); - } - - if (error_code == OPUS_OK) { - error_code = decoder_object.Decode(decoded_samples, output_data, output_data_size, - input_data, input_data_size); - } - - if (error_code == OPUS_OK) { - if (final_range && decoder_object.GetFinalRange() != final_range) { - error_code = OPUS_INVALID_PACKET; - } - } - - auto end_time = system.CoreTiming().GetGlobalTimeUs(); - shared_memory->dsp_return_data[0] = error_code; - shared_memory->dsp_return_data[1] = decoded_samples; - shared_memory->dsp_return_data[2] = (end_time - start_time).count(); - - Send(Direction::Host, Message::DecodeInterleavedOK); - } break; - - case MapMemory: { - [[maybe_unused]] auto buffer = shared_memory->host_send_data[0]; - [[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1]; - Send(Direction::Host, Message::MapMemoryOK); - } break; - - case UnmapMemory: { - [[maybe_unused]] auto buffer = shared_memory->host_send_data[0]; - [[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1]; - Send(Direction::Host, Message::UnmapMemoryOK); - } break; - - case GetWorkBufferSizeForMultiStream: { - auto total_stream_count = static_cast(shared_memory->host_send_data[0]); - auto stereo_stream_count = static_cast(shared_memory->host_send_data[1]); - - ASSERT(IsValidMultiStreamStreamCounts(total_stream_count, stereo_stream_count)); - - shared_memory->dsp_return_data[0] = OpusMultiStreamDecodeObject::GetWorkBufferSize( - total_stream_count, stereo_stream_count); - Send(Direction::Host, Message::GetWorkBufferSizeForMultiStreamOK); - } break; - - case InitializeMultiStreamDecodeObject: { - auto buffer = shared_memory->host_send_data[0]; - auto buffer_size = shared_memory->host_send_data[1]; - auto sample_rate = static_cast(shared_memory->host_send_data[2]); - auto channel_count = static_cast(shared_memory->host_send_data[3]); - auto total_stream_count = static_cast(shared_memory->host_send_data[4]); - auto stereo_stream_count = static_cast(shared_memory->host_send_data[5]); - // Nintendo seem to have a bug here, they try to use &host_send_data[6] for the channel - // mappings, but [6] is never set, and there is not enough room in the argument data for - // more than 40 channels, when 255 are possible. - // It also means the mapping values are undefined, though likely always 0, - // and the mappings given by the game are ignored. The mappings are copied to this - // dedicated buffer host side, so let's do as intended. - auto mappings = shared_memory->channel_mapping.data(); - - ASSERT(IsValidMultiStreamStreamCounts(total_stream_count, stereo_stream_count)); - ASSERT(sample_rate >= 0); - ASSERT(buffer_size >= OpusMultiStreamDecodeObject::GetWorkBufferSize( - total_stream_count, stereo_stream_count)); - - auto& decoder_object = OpusMultiStreamDecodeObject::Initialize(buffer, buffer); - shared_memory->dsp_return_data[0] = decoder_object.InitializeDecoder( - sample_rate, total_stream_count, channel_count, stereo_stream_count, mappings); - - Send(Direction::Host, Message::InitializeMultiStreamDecodeObjectOK); - } break; - - case ShutdownMultiStreamDecodeObject: { - auto buffer = shared_memory->host_send_data[0]; - [[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1]; - - auto& decoder_object = OpusMultiStreamDecodeObject::Initialize(buffer, buffer); - shared_memory->dsp_return_data[0] = decoder_object.Shutdown(); - - Send(Direction::Host, Message::ShutdownMultiStreamDecodeObjectOK); - } break; - - case DecodeInterleavedForMultiStream: { - auto start_time = system.CoreTiming().GetGlobalTimeUs(); - - auto buffer = shared_memory->host_send_data[0]; - auto input_data = shared_memory->host_send_data[1]; - auto input_data_size = shared_memory->host_send_data[2]; - auto output_data = shared_memory->host_send_data[3]; - auto output_data_size = shared_memory->host_send_data[4]; - auto final_range = static_cast(shared_memory->host_send_data[5]); - auto reset_requested = shared_memory->host_send_data[6]; - - u32 decoded_samples{0}; - - auto& decoder_object = OpusMultiStreamDecodeObject::Initialize(buffer, buffer); - s32 error_code{OPUS_OK}; - if (reset_requested) { - error_code = decoder_object.ResetDecoder(); - } - - if (error_code == OPUS_OK) { - error_code = decoder_object.Decode(decoded_samples, output_data, output_data_size, - input_data, input_data_size); - } - - if (error_code == OPUS_OK) { - if (final_range && decoder_object.GetFinalRange() != final_range) { - error_code = OPUS_INVALID_PACKET; - } - } - - auto end_time = system.CoreTiming().GetGlobalTimeUs(); - shared_memory->dsp_return_data[0] = error_code; - shared_memory->dsp_return_data[1] = decoded_samples; - shared_memory->dsp_return_data[2] = (end_time - start_time).count(); - - Send(Direction::Host, Message::DecodeInterleavedForMultiStreamOK); - } break; - - default: - LOG_ERROR(Service_Audio, "Invalid OpusDecoder command {}", msg); - continue; - } - } -} - } // namespace AudioCore::ADSP::OpusDecoder diff --git a/src/audio_core/adsp/apps/opus/opus_decoder.h b/src/audio_core/adsp/apps/opus/opus_decoder.h index fcb89bb40e..d554586703 100644 --- a/src/audio_core/adsp/apps/opus/opus_decoder.h +++ b/src/audio_core/adsp/apps/opus/opus_decoder.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -69,10 +72,6 @@ private: * Initializing thread, launched at audio_core boot to avoid blocking the main emu boot thread. */ void Init(std::stop_token stop_token); - /** - * Main OpusDecoder thread, responsible for processing the incoming Opus packets. - */ - void Main(std::stop_token stop_token); /// Core system Core::System& system; diff --git a/src/audio_core/audio_core.cpp b/src/audio_core/audio_core.cpp index fcaab2b320..234c831ac0 100644 --- a/src/audio_core/audio_core.cpp +++ b/src/audio_core/audio_core.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -8,10 +11,11 @@ namespace AudioCore { -AudioCore::AudioCore(Core::System& system) : audio_manager{std::make_unique()} { +AudioCore::AudioCore(Core::System& system) { + audio_manager.emplace(); CreateSinks(); // Must be created after the sinks - adsp = std::make_unique(system, *output_sink); + adsp.emplace(system, *output_sink); } AudioCore ::~AudioCore() { diff --git a/src/audio_core/audio_core.h b/src/audio_core/audio_core.h index e4e27fc661..ababd780b1 100644 --- a/src/audio_core/audio_core.h +++ b/src/audio_core/audio_core.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -15,10 +18,7 @@ class System; namespace AudioCore { -class AudioManager; -/** - * Main audio class, stored inside the core, and holding the audio manager, all sinks, and the ADSP. - */ +/// @brief Main audio class, stored inside the core, and holding the audio manager, all sinks, and the ADSP. class AudioCore { public: explicit AudioCore(Core::System& system); @@ -50,27 +50,22 @@ public: */ Sink::Sink& GetInputSink(); - /** - * Get the ADSP. - * - * @return Ref to the ADSP. - */ + /// @brief Get the ADSP. + /// @return Ref to the ADSP. ADSP::ADSP& ADSP(); private: - /** - * Create the sinks on startup. - */ + /// @brief Create the sinks on startup. void CreateSinks(); /// Main audio manager for audio in/out - std::unique_ptr audio_manager; + std::optional audio_manager; /// Sink used for audio renderer and audio out std::unique_ptr output_sink; /// Sink used for audio input std::unique_ptr input_sink; /// The ADSP in the sysmodule - std::unique_ptr adsp; + std::optional adsp; }; } // namespace AudioCore diff --git a/src/audio_core/audio_in_manager.cpp b/src/audio_core/audio_in_manager.cpp index 63b064922a..6b528e9db0 100644 --- a/src/audio_core/audio_in_manager.cpp +++ b/src/audio_core/audio_in_manager.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -41,8 +44,7 @@ void Manager::ReleaseSessionId(const size_t session_id) { Result Manager::LinkToManager() { std::scoped_lock l{mutex}; if (!linked_to_manager) { - AudioManager& manager{system.AudioCore().GetAudioManager()}; - manager.SetInManager(std::bind(&Manager::BufferReleaseAndRegister, this)); + system.AudioCore().GetAudioManager().SetInManager(std::bind(&Manager::BufferReleaseAndRegister, this)); linked_to_manager = true; } diff --git a/src/audio_core/audio_manager.cpp b/src/audio_core/audio_manager.cpp index 10b56f2140..93142a53fb 100644 --- a/src/audio_core/audio_manager.cpp +++ b/src/audio_core/audio_manager.cpp @@ -1,81 +1,77 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "audio_core/audio_manager.h" +#include "common/thread.h" #include "core/core.h" #include "core/hle/service/audio/errors.h" namespace AudioCore { AudioManager::AudioManager() { - thread = std::jthread([this]() { ThreadFunc(); }); + thread = std::jthread([this](std::stop_token stop_token) { + Common::SetCurrentThreadName("AudioManager"); + std::unique_lock l{events.GetAudioEventLock()}; + events.ClearEvents(); + while (!stop_token.stop_requested()) { + const auto timed_out{events.Wait(l, std::chrono::seconds(2))}; + if (events.CheckAudioEventSet(Event::Type::Max)) { + break; + } + for (size_t i = 0; i < buffer_events.size(); i++) { + const auto event_type = Event::Type(i); + if (events.CheckAudioEventSet(event_type) || timed_out) { + if (buffer_events[i]) { + buffer_events[i](); + } + } + events.SetAudioEvent(event_type, false); + } + } + }); } void AudioManager::Shutdown() { - running = false; events.SetAudioEvent(Event::Type::Max, true); - thread.join(); + if (thread.joinable()) { + thread.request_stop(); + thread.join(); + } } Result AudioManager::SetOutManager(BufferEventFunc buffer_func) { - if (!running) { - return Service::Audio::ResultOperationFailed; + if (thread.joinable()) { + std::scoped_lock l{lock}; + const auto index{events.GetManagerIndex(Event::Type::AudioOutManager)}; + if (buffer_events[index] == nullptr) { + buffer_events[index] = std::move(buffer_func); + needs_update = true; + events.SetAudioEvent(Event::Type::AudioOutManager, true); + } + return ResultSuccess; } - - std::scoped_lock l{lock}; - - const auto index{events.GetManagerIndex(Event::Type::AudioOutManager)}; - if (buffer_events[index] == nullptr) { - buffer_events[index] = std::move(buffer_func); - needs_update = true; - events.SetAudioEvent(Event::Type::AudioOutManager, true); - } - return ResultSuccess; + return Service::Audio::ResultOperationFailed; } Result AudioManager::SetInManager(BufferEventFunc buffer_func) { - if (!running) { - return Service::Audio::ResultOperationFailed; + if (thread.joinable()) { + std::scoped_lock l{lock}; + const auto index{events.GetManagerIndex(Event::Type::AudioInManager)}; + if (buffer_events[index] == nullptr) { + buffer_events[index] = std::move(buffer_func); + needs_update = true; + events.SetAudioEvent(Event::Type::AudioInManager, true); + } + return ResultSuccess; } - - std::scoped_lock l{lock}; - - const auto index{events.GetManagerIndex(Event::Type::AudioInManager)}; - if (buffer_events[index] == nullptr) { - buffer_events[index] = std::move(buffer_func); - needs_update = true; - events.SetAudioEvent(Event::Type::AudioInManager, true); - } - return ResultSuccess; + return Service::Audio::ResultOperationFailed; } void AudioManager::SetEvent(const Event::Type type, const bool signalled) { events.SetAudioEvent(type, signalled); } -void AudioManager::ThreadFunc() { - std::unique_lock l{events.GetAudioEventLock()}; - events.ClearEvents(); - running = true; - - while (running) { - const auto timed_out{events.Wait(l, std::chrono::seconds(2))}; - - if (events.CheckAudioEventSet(Event::Type::Max)) { - break; - } - - for (size_t i = 0; i < buffer_events.size(); i++) { - const auto event_type = static_cast(i); - - if (events.CheckAudioEventSet(event_type) || timed_out) { - if (buffer_events[i]) { - buffer_events[i](); - } - } - events.SetAudioEvent(event_type, false); - } - } -} - } // namespace AudioCore diff --git a/src/audio_core/audio_manager.h b/src/audio_core/audio_manager.h index 02270242ac..0194aa16db 100644 --- a/src/audio_core/audio_manager.h +++ b/src/audio_core/audio_manager.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -66,13 +69,6 @@ public: void SetEvent(Event::Type type, bool signalled); private: - /** - * Main thread, waiting on a manager signal and calling the registered function. - */ - void ThreadFunc(); - - /// Is the main thread running? - std::atomic running{}; /// Unused bool needs_update{}; /// Events to be set and signalled diff --git a/src/audio_core/audio_out_manager.cpp b/src/audio_core/audio_out_manager.cpp index 316ea7c817..569df8d1e0 100644 --- a/src/audio_core/audio_out_manager.cpp +++ b/src/audio_core/audio_out_manager.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -40,8 +43,7 @@ void Manager::ReleaseSessionId(const size_t session_id) { Result Manager::LinkToManager() { std::scoped_lock l{mutex}; if (!linked_to_manager) { - AudioManager& manager{system.AudioCore().GetAudioManager()}; - manager.SetOutManager(std::bind(&Manager::BufferReleaseAndRegister, this)); + system.AudioCore().GetAudioManager().SetOutManager(std::bind(&Manager::BufferReleaseAndRegister, this)); linked_to_manager = true; } diff --git a/src/audio_core/device/device_session.cpp b/src/audio_core/device/device_session.cpp index 2a1ae1bb3f..2e9da7876f 100644 --- a/src/audio_core/device/device_session.cpp +++ b/src/audio_core/device/device_session.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -39,7 +42,7 @@ Result DeviceSession::Initialize(std::string_view name_, SampleFormat sample_for channel_count = channel_count_; session_id = session_id_; handle = handle_; - handle->Open(); + handle->Open(system.Kernel()); applet_resource_user_id = applet_resource_user_id_; if (type == Sink::StreamType::In) { @@ -60,7 +63,7 @@ void DeviceSession::Finalize() { } if (handle) { - handle->Close(); + handle->Close(system.Kernel()); handle = nullptr; } } diff --git a/src/audio_core/in/audio_in_system.cpp b/src/audio_core/in/audio_in_system.cpp index ec1f565736..f71a2df6be 100644 --- a/src/audio_core/in/audio_in_system.cpp +++ b/src/audio_core/in/audio_in_system.cpp @@ -122,7 +122,7 @@ Result System::Stop() { session->SetVolume(0.0f); session->ClearBuffers(); if (buffers.ReleaseBuffers(system.CoreTiming(), *session, true)) { - buffer_event->Signal(); + buffer_event->Signal(system.Kernel()); } state = State::Stopped; } @@ -164,7 +164,7 @@ void System::ReleaseBuffers() { if (signal) { // Signal if any buffer was released, or if none are registered, we need more. - buffer_event->Signal(); + buffer_event->Signal(system.Kernel()); } } @@ -181,7 +181,7 @@ bool System::FlushAudioInBuffers() { buffers.FlushBuffers(buffers_released); if (buffers_released > 0) { - buffer_event->Signal(); + buffer_event->Signal(system.Kernel()); } return true; } diff --git a/src/audio_core/out/audio_out_system.cpp b/src/audio_core/out/audio_out_system.cpp index dcb0d0694e..46f30f19c6 100644 --- a/src/audio_core/out/audio_out_system.cpp +++ b/src/audio_core/out/audio_out_system.cpp @@ -121,7 +121,7 @@ Result System::Stop() { session->SetVolume(0.0f); session->ClearBuffers(); if (buffers.ReleaseBuffers(system.CoreTiming(), *session, true)) { - buffer_event->Signal(); + buffer_event->Signal(system.Kernel()); } state = State::Stopped; } @@ -162,7 +162,7 @@ void System::ReleaseBuffers() { bool signal{buffers.ReleaseBuffers(system.CoreTiming(), *session, false)}; if (signal) { // Signal if any buffer was released, or if none are registered, we need more. - buffer_event->Signal(); + buffer_event->Signal(system.Kernel()); } } @@ -179,7 +179,7 @@ bool System::FlushAudioOutBuffers() { buffers.FlushBuffers(buffers_released); if (buffers_released > 0) { - buffer_event->Signal(); + buffer_event->Signal(system.Kernel()); } return true; } diff --git a/src/audio_core/renderer/system.cpp b/src/audio_core/renderer/system.cpp index 2053ec6dd4..b7675ff5de 100644 --- a/src/audio_core/renderer/system.cpp +++ b/src/audio_core/renderer/system.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project @@ -540,7 +540,7 @@ Result System::Update(std::span input, std::span performance, std: return result; } - adsp_rendered_event->Clear(); + adsp_rendered_event->Clear(core.Kernel()); num_times_updated++; const auto end_time{core.CoreTiming().GetGlobalTimeNs().count()}; @@ -624,7 +624,7 @@ void System::SendCommandToDsp() { reset_command_buffers = false; command_buffer_size = command_size; if (remaining_command_count == 0) { - adsp_rendered_event->Signal(); + adsp_rendered_event->Signal(core.Kernel()); } } else { audio_renderer.ClearRemainCommandCount(session_id); diff --git a/src/audio_core/sink/sdl2_sink.cpp b/src/audio_core/sink/sdl3_sink.cpp similarity index 62% rename from src/audio_core/sink/sdl2_sink.cpp rename to src/audio_core/sink/sdl3_sink.cpp index b016bf2222..990b62deb5 100644 --- a/src/audio_core/sink/sdl2_sink.cpp +++ b/src/audio_core/sink/sdl3_sink.cpp @@ -7,16 +7,40 @@ #include #include -#include +#include #include "audio_core/common/common.h" -#include "audio_core/sink/sdl2_sink.h" +#include "audio_core/sink/sdl3_sink.h" #include "audio_core/sink/sink_stream.h" #include "common/logging.h" #include "common/scope_exit.h" #include "core/core.h" namespace AudioCore::Sink { + +namespace { +SDL_AudioDeviceID FindAudioDeviceByName(const std::string& device_name, bool capture) { + int device_count = 0; + SDL_AudioDeviceID* devices = capture ? SDL_GetAudioRecordingDevices(&device_count) + : SDL_GetAudioPlaybackDevices(&device_count); + if (devices == nullptr) { + return capture ? SDL_AUDIO_DEVICE_DEFAULT_RECORDING : SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK; + } + + SDL_AudioDeviceID selected = capture ? SDL_AUDIO_DEVICE_DEFAULT_RECORDING + : SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK; + for (int i = 0; i < device_count; ++i) { + const char* current_name = SDL_GetAudioDeviceName(devices[i]); + if (current_name != nullptr && device_name == current_name) { + selected = devices[i]; + break; + } + } + SDL_free(devices); + return selected; +} +} // Anonymous namespace + /** * SDL sink stream, responsible for sinking samples to hardware. */ @@ -39,13 +63,10 @@ public: system_channels = system_channels_; device_channels = device_channels_; - SDL_AudioSpec spec; + SDL_AudioSpec spec{}; spec.freq = TargetSampleRate; spec.channels = static_cast(device_channels); - spec.format = AUDIO_S16SYS; - spec.samples = TargetSampleCount * 2; - spec.callback = &SDLSinkStream::DataCallback; - spec.userdata = this; + spec.format = SDL_AUDIO_S16; std::string device_name{output_device}; bool capture{false}; @@ -54,22 +75,28 @@ public: capture = true; } - SDL_AudioSpec obtained; - if (device_name.empty()) { - device = SDL_OpenAudioDevice(nullptr, capture, &spec, &obtained, false); - } else { - device = SDL_OpenAudioDevice(device_name.c_str(), capture, &spec, &obtained, false); - } + const SDL_AudioDeviceID audio_device = + device_name.empty() ? (capture ? SDL_AUDIO_DEVICE_DEFAULT_RECORDING + : SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK) + : FindAudioDeviceByName(device_name, capture); - if (device == 0) { + stream = SDL_OpenAudioDeviceStream(audio_device, &spec, &SDLSinkStream::DataCallback, + this); + + if (stream == nullptr) { LOG_CRITICAL(Audio_Sink, "Error opening SDL audio device: {}", SDL_GetError()); return; } + SDL_AudioSpec stream_in{}; + SDL_AudioSpec stream_out{}; + static_cast(SDL_GetAudioStreamFormat(stream, &stream_in, &stream_out)); + LOG_INFO(Service_Audio, "Opening SDL stream {} with: rate {} channels {} (system channels {}) " - " samples {}", - device, obtained.freq, obtained.channels, system_channels, obtained.samples); + " format {}", + static_cast(stream), stream_out.freq, stream_out.channels, + system_channels, static_cast(stream_out.format)); } /** @@ -84,13 +111,14 @@ public: * Finalize the sink stream. */ void Finalize() override { - if (device == 0) { + if (stream == nullptr) { return; } Stop(); - SDL_ClearQueuedAudio(device); - SDL_CloseAudioDevice(device); + SDL_ClearAudioStream(stream); + SDL_DestroyAudioStream(stream); + stream = nullptr; } /** @@ -100,23 +128,23 @@ public: * Default false. */ void Start(bool resume = false) override { - if (device == 0 || !paused) { + if (stream == nullptr || !paused) { return; } paused = false; - SDL_PauseAudioDevice(device, 0); + static_cast(SDL_ResumeAudioStreamDevice(stream)); } /** * Stop the sink stream. */ void Stop() override { - if (device == 0 || paused) { + if (stream == nullptr || paused) { return; } SignalPause(); - SDL_PauseAudioDevice(device, 1); + static_cast(SDL_PauseAudioStreamDevice(stream)); } private: @@ -128,7 +156,8 @@ private: * @param stream - Buffer of samples to be filled or read. * @param len - Length of the stream in bytes. */ - static void DataCallback(void* userdata, Uint8* stream, int len) { + static void DataCallback(void* userdata, SDL_AudioStream* stream, int additional_amount, + int total_amount) { auto* impl = static_cast(userdata); if (!impl) { @@ -137,25 +166,46 @@ private: const std::size_t num_channels = impl->GetDeviceChannels(); const std::size_t frame_size = num_channels; - const std::size_t num_frames{len / num_channels / sizeof(s16)}; if (impl->type == StreamType::In) { - std::span input_buffer{reinterpret_cast(stream), - num_frames * frame_size}; + const int bytes_available = SDL_GetAudioStreamAvailable(stream); + if (bytes_available <= 0) { + return; + } + + std::vector input(bytes_available / static_cast(sizeof(s16))); + const int bytes_read = SDL_GetAudioStreamData(stream, input.data(), bytes_available); + if (bytes_read <= 0) { + return; + } + + const std::size_t num_frames = + static_cast(bytes_read) / sizeof(s16) / frame_size; + std::span input_buffer{input.data(), + static_cast(bytes_read) / sizeof(s16)}; impl->ProcessAudioIn(input_buffer, num_frames); } else { - std::span output_buffer{reinterpret_cast(stream), num_frames * frame_size}; + if (additional_amount <= 0 && total_amount <= 0) { + return; + } + + const int bytes_requested = additional_amount > 0 ? additional_amount : total_amount; + std::vector output(bytes_requested / static_cast(sizeof(s16))); + const std::size_t num_frames = + static_cast(bytes_requested) / sizeof(s16) / frame_size; + std::span output_buffer{output.data(), output.size()}; impl->ProcessAudioOutAndRender(output_buffer, num_frames); + static_cast(SDL_PutAudioStreamData(stream, output.data(), bytes_requested)); } } - /// SDL device id of the opened input/output device - SDL_AudioDeviceID device{}; + /// SDL stream attached to an opened input/output device + SDL_AudioStream* stream{}; }; SDLSink::SDLSink(std::string_view target_device_name) { if (!SDL_WasInit(SDL_INIT_AUDIO)) { - if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) { + if (!SDL_InitSubSystem(SDL_INIT_AUDIO)) { LOG_CRITICAL(Audio_Sink, "SDL_InitSubSystem audio failed: {}", SDL_GetError()); return; } @@ -218,18 +268,26 @@ std::vector ListSDLSinkDevices(bool capture) { std::vector device_list; if (!SDL_WasInit(SDL_INIT_AUDIO)) { - if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) { + if (!SDL_InitSubSystem(SDL_INIT_AUDIO)) { LOG_CRITICAL(Audio_Sink, "SDL_InitSubSystem audio failed: {}", SDL_GetError()); return {}; } } - const int device_count = SDL_GetNumAudioDevices(capture); + int device_count = 0; + SDL_AudioDeviceID* devices = + capture ? SDL_GetAudioRecordingDevices(&device_count) + : SDL_GetAudioPlaybackDevices(&device_count); + if (devices == nullptr) { + return device_list; + } + for (int i = 0; i < device_count; ++i) { - if (const char* name = SDL_GetAudioDeviceName(i, capture)) { + if (const char* name = SDL_GetAudioDeviceName(devices[i])) { device_list.emplace_back(name); } } + SDL_free(devices); return device_list; } @@ -242,7 +300,7 @@ u32 GetSDLLatency() { // REVERTED back to 3833 - Below function IsSDLSuitable() removed, reverting to GetSDLLatency() above. - DIABLO 3 FIX /* bool IsSDLSuitable() { -#if !defined(HAVE_SDL2) +#if !defined(HAVE_SDL3) return false; #else // Check SDL can init diff --git a/src/audio_core/sink/sdl2_sink.h b/src/audio_core/sink/sdl3_sink.h similarity index 97% rename from src/audio_core/sink/sdl2_sink.h rename to src/audio_core/sink/sdl3_sink.h index 19ea32595f..d0f4586f53 100644 --- a/src/audio_core/sink/sdl2_sink.h +++ b/src/audio_core/sink/sdl3_sink.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project diff --git a/src/audio_core/sink/sink_details.cpp b/src/audio_core/sink/sink_details.cpp index 4420560d22..1b737eb12e 100644 --- a/src/audio_core/sink/sink_details.cpp +++ b/src/audio_core/sink/sink_details.cpp @@ -16,8 +16,8 @@ #ifdef HAVE_CUBEB #include "audio_core/sink/cubeb_sink.h" #endif -#ifdef HAVE_SDL2 -#include "audio_core/sink/sdl2_sink.h" +#ifdef HAVE_SDL3 +#include "audio_core/sink/sdl3_sink.h" #endif #include "audio_core/sink/null_sink.h" #include "common/logging.h" @@ -71,9 +71,9 @@ constexpr SinkDetails sink_details[] = { &GetCubebLatency, }, #endif -#ifdef HAVE_SDL2 +#ifdef HAVE_SDL3 SinkDetails{ - Settings::AudioEngine::Sdl2, + Settings::AudioEngine::Sdl3, [](std::string_view device_id) -> std::unique_ptr { return std::make_unique(device_id); }, @@ -115,10 +115,10 @@ const SinkDetails& GetOutputSinkDetails(Settings::AudioEngine sink_id) { // BEGIN REINTRODUCED FROM 3833 - REPLACED CODE BLOCK ABOVE - DIABLO 3 FIX // Auto-select a backend. Prefer CubeB, but it may report a large minimum latency which // causes audio issues, in that case go with SDL. -#if defined(HAVE_CUBEB) && defined(HAVE_SDL2) +#if defined(HAVE_CUBEB) && defined(HAVE_SDL3) iter = find_backend(Settings::AudioEngine::Cubeb); if (iter->latency() > TargetSampleCount * 3) { - iter = find_backend(Settings::AudioEngine::Sdl2); + iter = find_backend(Settings::AudioEngine::Sdl3); } #else iter = std::begin(sink_details); diff --git a/src/audio_core/sink/sink_stream.cpp b/src/audio_core/sink/sink_stream.cpp index 2c41e7f2c8..cc27a09105 100644 --- a/src/audio_core/sink/sink_stream.cpp +++ b/src/audio_core/sink/sink_stream.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project @@ -177,10 +177,8 @@ void SinkStream::ProcessAudioOutAndRender(std::span output_buffer, std::siz queued_buffers.store(0); release_cv.notify_one(); } - - static constexpr std::array silence{}; for (size_t i = 0; i < num_frames; i++) - std::memcpy(&output_buffer[i * frame_size], silence.data(), frame_size_bytes); + std::memset(&output_buffer[i * frame_size], 0, frame_size_bytes); return; } diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index fb2bdda146..7bb4c3aa4a 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -50,7 +50,6 @@ add_library( elf.h error.cpp error.h - expected.h fiber.cpp fiber.h fixed_point.h @@ -141,12 +140,12 @@ add_library( vector_math.h virtual_buffer.cpp virtual_buffer.h - wall_clock.cpp - wall_clock.h zstd_compression.cpp zstd_compression.h fs/ryujinx_compat.h fs/ryujinx_compat.cpp fs/symlink.h fs/symlink.cpp + cpu_features.cpp + cpu_features.h httplib.h net/net.h net/net.cpp) @@ -180,23 +179,13 @@ endif() if(ARCHITECTURE_x86_64) target_sources( common - PRIVATE x64/cpu_detect.cpp - x64/cpu_detect.h - x64/cpu_wait.cpp - x64/cpu_wait.h - x64/native_clock.cpp - x64/native_clock.h + PRIVATE x64/rdtsc.cpp x64/rdtsc.h - x64/xbyak_abi.h - x64/xbyak_util.h) + x64/xbyak.h) target_link_libraries(common PRIVATE xbyak::xbyak) endif() -if(HAS_NCE) - target_sources(common PRIVATE arm64/native_clock.cpp arm64/native_clock.h) -endif() - if(MSVC) target_compile_definitions( common @@ -241,11 +230,11 @@ if(CXX_CLANG) endif() if (BOOST_NO_HEADERS) - target_link_libraries(common PUBLIC Boost::algorithm Boost::icl Boost::pool) + target_link_libraries(common PUBLIC Boost::algorithm Boost::heap Boost::icl Boost::pool) else() target_link_libraries(common PUBLIC Boost::headers) endif() - +target_link_libraries(common PRIVATE OpenSSL::SSL) target_link_libraries(common PUBLIC Boost::filesystem Boost::context httplib::httplib nlohmann_json::nlohmann_json) if (lz4_ADDED) @@ -253,7 +242,12 @@ if (lz4_ADDED) endif() target_link_libraries(common PUBLIC fmt::fmt stb::headers Threads::Threads unordered_dense::unordered_dense) -target_link_libraries(common PRIVATE lz4::lz4 LLVM::Demangle zstd::zstd) +target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd) + +# Please refer to src/common/demangle.cpp +if (WIN32) + target_link_libraries(common PRIVATE LLVM::Demangle) +endif() if(ANDROID) # For ASharedMemory_create diff --git a/src/common/arm64/native_clock.cpp b/src/common/arm64/native_clock.cpp deleted file mode 100644 index 76ffb74bab..0000000000 --- a/src/common/arm64/native_clock.cpp +++ /dev/null @@ -1,87 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#ifdef ANDROID -#include -#endif -#include "common/arm64/native_clock.h" - -namespace Common::Arm64 { - -namespace { - -NativeClock::FactorType GetFixedPointFactor(u64 num, u64 den) { - return (static_cast(num) << 64) / den; -} - -u64 MultiplyHigh(u64 m, NativeClock::FactorType factor) { - return static_cast((m * factor) >> 64); -} - -} // namespace - -NativeClock::NativeClock() { - const u64 host_cntfrq = GetHostCNTFRQ(); - ns_cntfrq_factor = GetFixedPointFactor(NsRatio::den, host_cntfrq); - us_cntfrq_factor = GetFixedPointFactor(UsRatio::den, host_cntfrq); - ms_cntfrq_factor = GetFixedPointFactor(MsRatio::den, host_cntfrq); - guest_cntfrq_factor = GetFixedPointFactor(CNTFRQ, host_cntfrq); - gputick_cntfrq_factor = GetFixedPointFactor(GPUTickFreq, host_cntfrq); -} - -std::chrono::nanoseconds NativeClock::GetTimeNS() const { - return std::chrono::nanoseconds{MultiplyHigh(GetUptime(), ns_cntfrq_factor)}; -} - -std::chrono::microseconds NativeClock::GetTimeUS() const { - return std::chrono::microseconds{MultiplyHigh(GetUptime(), us_cntfrq_factor)}; -} - -std::chrono::milliseconds NativeClock::GetTimeMS() const { - return std::chrono::milliseconds{MultiplyHigh(GetUptime(), ms_cntfrq_factor)}; -} - -s64 NativeClock::GetCNTPCT() const { - return MultiplyHigh(GetUptime(), guest_cntfrq_factor); -} - -s64 NativeClock::GetGPUTick() const { - return MultiplyHigh(GetUptime(), gputick_cntfrq_factor); -} - -s64 NativeClock::GetUptime() const { - s64 cntvct_el0 = 0; - asm volatile("dsb ish\n\t" - "mrs %[cntvct_el0], cntvct_el0\n\t" - "dsb ish\n\t" - : [cntvct_el0] "=r"(cntvct_el0)); - return cntvct_el0; -} - -bool NativeClock::IsNative() const { - return true; -} - -s64 NativeClock::GetHostCNTFRQ() { - u64 cntfrq_el0 = 0; - std::string_view board{""}; -#ifdef ANDROID - char buffer[PROP_VALUE_MAX]; - int len{__system_property_get("ro.product.board", buffer)}; - board = std::string_view(buffer, static_cast(len)); -#endif - if (board == "s5e9925") { // Exynos 2200 - cntfrq_el0 = 25600000; - } else if (board == "exynos2100") { // Exynos 2100 - cntfrq_el0 = 26000000; - } else if (board == "exynos9810") { // Exynos 9810 - cntfrq_el0 = 26000000; - } else if (board == "s5e8825") { // Exynos 1280 - cntfrq_el0 = 26000000; - } else { - asm("mrs %[cntfrq_el0], cntfrq_el0" : [cntfrq_el0] "=r"(cntfrq_el0)); - } - return cntfrq_el0; -} - -} // namespace Common::Arm64 diff --git a/src/common/arm64/native_clock.h b/src/common/arm64/native_clock.h deleted file mode 100644 index 94bc1882e0..0000000000 --- a/src/common/arm64/native_clock.h +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "common/wall_clock.h" - -namespace Common::Arm64 { - -class NativeClock final : public WallClock { -public: - explicit NativeClock(); - - std::chrono::nanoseconds GetTimeNS() const override; - - std::chrono::microseconds GetTimeUS() const override; - - std::chrono::milliseconds GetTimeMS() const override; - - s64 GetCNTPCT() const override; - - s64 GetGPUTick() const override; - - s64 GetUptime() const override; - - bool IsNative() const override; - - static s64 GetHostCNTFRQ(); - -public: - using FactorType = unsigned __int128; - - FactorType GetGuestCNTFRQFactor() const { - return guest_cntfrq_factor; - } - -private: - FactorType ns_cntfrq_factor; - FactorType us_cntfrq_factor; - FactorType ms_cntfrq_factor; - FactorType guest_cntfrq_factor; - FactorType gputick_cntfrq_factor; -}; - -} // namespace Common::Arm64 diff --git a/src/common/container_hash.h b/src/common/container_hash.h index a5e3577452..b6d8e02e26 100644 --- a/src/common/container_hash.h +++ b/src/common/container_hash.h @@ -1,7 +1,12 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2005-2014 Daniel James // SPDX-FileCopyrightText: 2016 Austin Appleby // SPDX-License-Identifier: BSL-1.0 +#pragma once + #include #include #include diff --git a/src/common/x64/cpu_detect.cpp b/src/common/cpu_features.cpp similarity index 50% rename from src/common/x64/cpu_detect.cpp rename to src/common/cpu_features.cpp index 4cc42ccbd5..2e02c47826 100644 --- a/src/common/x64/cpu_detect.cpp +++ b/src/common/cpu_features.cpp @@ -10,34 +10,32 @@ #include #include #include +#include #include #include #include -#include "common/bit_util.h" -#include "common/common_types.h" -#include "common/logging.h" -#include "common/x64/cpu_detect.h" -#include "common/x64/rdtsc.h" - #ifdef _WIN32 #include -#endif - -#ifdef _MSC_VER -#include - -static inline u64 xgetbv(u32 index) { - return _xgetbv(index); -} -#else - -#if defined(__DragonFly__) || defined(__FreeBSD__) -// clang-format off +#elif defined(__DragonFly__) || defined(__FreeBSD__) #include #include -// clang-format on +#elif defined(__ANDROID__) +#include #endif +#include "common/steady_clock.h" +#include "common/uint128.h" +#include "common/bit_util.h" +#include "common/common_types.h" +#include "common/cpu_features.h" +#include "common/logging.h" + +#ifdef ARCHITECTURE_x86_64 +#include "common/x64/rdtsc.h" +#ifdef _MSC_VER +#include +static inline u64 xgetbv(u32 index) { return _xgetbv(index); } +#else static inline void __cpuidex(int info[4], u32 function_id, u32 subfunction_id) { #if defined(__DragonFly__) || defined(__FreeBSD__) // Despite the name, this is just do_cpuid() with ECX as second input. @@ -50,11 +48,7 @@ static inline void __cpuidex(int info[4], u32 function_id, u32 subfunction_id) { : "a"(function_id), "c"(subfunction_id)); #endif } - -static inline void __cpuid(int info[4], u32 function_id) { - return __cpuidex(info, function_id, 0); -} - +static inline void __cpuid(int info[4], u32 function_id) { return __cpuidex(info, function_id, 0); } #define _XCR_XFEATURE_ENABLED_MASK 0 static inline u64 xgetbv(u32 index) { u32 eax, edx; @@ -62,9 +56,10 @@ static inline u64 xgetbv(u32 index) { return ((u64)edx << 32) | eax; } #endif // _MSC_VER +#endif namespace Common { - +#ifdef ARCHITECTURE_x86_64 CPUCaps::Manufacturer CPUCaps::ParseManufacturer(std::string_view brand_string) { if (brand_string == "GenuineIntel") { return Manufacturer::Intel; @@ -76,142 +71,6 @@ CPUCaps::Manufacturer CPUCaps::ParseManufacturer(std::string_view brand_string) return Manufacturer::Unknown; } -// Detects the various CPU features -static CPUCaps Detect() { - CPUCaps caps = {}; - - // Assumes the CPU supports the CPUID instruction. Those that don't would likely not support - // yuzu at all anyway - - int cpu_id[4]; - - // Detect CPU's CPUID capabilities and grab manufacturer string - __cpuid(cpu_id, 0x00000000); - const u32 max_std_fn = cpu_id[0]; // EAX - - std::memset(caps.brand_string, 0, std::size(caps.brand_string)); - std::memcpy(&caps.brand_string[0], &cpu_id[1], sizeof(u32)); - std::memcpy(&caps.brand_string[4], &cpu_id[3], sizeof(u32)); - std::memcpy(&caps.brand_string[8], &cpu_id[2], sizeof(u32)); - - caps.manufacturer = CPUCaps::ParseManufacturer(caps.brand_string); - - // Set reasonable default cpu string even if brand string not available - std::strncpy(caps.cpu_string, caps.brand_string, std::size(caps.brand_string)); - - __cpuid(cpu_id, 0x80000000); - - const u32 max_ex_fn = cpu_id[0]; - - // Detect family and other miscellaneous features - if (max_std_fn >= 1) { - __cpuid(cpu_id, 0x00000001); - caps.sse = Common::Bit<25>(cpu_id[3]); - caps.sse2 = Common::Bit<26>(cpu_id[3]); - caps.sse3 = Common::Bit<0>(cpu_id[2]); - caps.pclmulqdq = Common::Bit<1>(cpu_id[2]); - caps.ssse3 = Common::Bit<9>(cpu_id[2]); - caps.sse4_1 = Common::Bit<19>(cpu_id[2]); - caps.sse4_2 = Common::Bit<20>(cpu_id[2]); - caps.movbe = Common::Bit<22>(cpu_id[2]); - caps.popcnt = Common::Bit<23>(cpu_id[2]); - caps.aes = Common::Bit<25>(cpu_id[2]); - caps.f16c = Common::Bit<29>(cpu_id[2]); - - // AVX support requires 3 separate checks: - // - Is the AVX bit set in CPUID? - // - Is the XSAVE bit set in CPUID? - // - XGETBV result has the XCR bit set. - if (Common::Bit<28>(cpu_id[2]) && Common::Bit<27>(cpu_id[2])) { - if ((xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6) { - caps.avx = true; - if (Common::Bit<12>(cpu_id[2])) - caps.fma = true; - } - } - - if (max_std_fn >= 7) { - __cpuidex(cpu_id, 0x00000007, 0x00000000); - // Can't enable AVX{2,512} unless the XSAVE/XGETBV checks above passed - if (caps.avx) { - caps.avx2 = Common::Bit<5>(cpu_id[1]); - caps.avx512f = Common::Bit<16>(cpu_id[1]); - caps.avx512dq = Common::Bit<17>(cpu_id[1]); - caps.avx512cd = Common::Bit<28>(cpu_id[1]); - caps.avx512bw = Common::Bit<30>(cpu_id[1]); - caps.avx512vl = Common::Bit<31>(cpu_id[1]); - caps.avx512vbmi = Common::Bit<1>(cpu_id[2]); - caps.avx512bitalg = Common::Bit<12>(cpu_id[2]); - } - - caps.bmi1 = Common::Bit<3>(cpu_id[1]); - caps.bmi2 = Common::Bit<8>(cpu_id[1]); - caps.sha = Common::Bit<29>(cpu_id[1]); - - caps.waitpkg = Common::Bit<5>(cpu_id[2]); - caps.gfni = Common::Bit<8>(cpu_id[2]); - - __cpuidex(cpu_id, 0x00000007, 0x00000001); - caps.avx_vnni = caps.avx && Common::Bit<4>(cpu_id[0]); - } - } - - if (max_ex_fn >= 0x80000004) { - // Extract CPU model string - __cpuid(cpu_id, 0x80000002); - std::memcpy(caps.cpu_string, cpu_id, sizeof(cpu_id)); - __cpuid(cpu_id, 0x80000003); - std::memcpy(caps.cpu_string + 16, cpu_id, sizeof(cpu_id)); - __cpuid(cpu_id, 0x80000004); - std::memcpy(caps.cpu_string + 32, cpu_id, sizeof(cpu_id)); - } - - if (max_ex_fn >= 0x80000001) { - // Check for more features - __cpuid(cpu_id, 0x80000001); - caps.lzcnt = Common::Bit<5>(cpu_id[2]); - caps.fma4 = Common::Bit<16>(cpu_id[2]); - caps.monitorx = Common::Bit<29>(cpu_id[2]); - } - - if (max_ex_fn >= 0x80000007) { - __cpuid(cpu_id, 0x80000007); - caps.invariant_tsc = Common::Bit<8>(cpu_id[3]); - } - - if (max_std_fn >= 0x15) { - __cpuid(cpu_id, 0x15); - caps.tsc_crystal_ratio_denominator = cpu_id[0]; - caps.tsc_crystal_ratio_numerator = cpu_id[1]; - caps.crystal_frequency = cpu_id[2]; - // Some CPU models might not return a crystal frequency. - // The CPU model can be detected to use the values from turbostat - // https://github.com/torvalds/linux/blob/master/tools/power/x86/turbostat/turbostat.c#L5569 - // but it's easier to just estimate the TSC tick rate for these cases. - if (caps.tsc_crystal_ratio_denominator) { - caps.tsc_frequency = static_cast(caps.crystal_frequency) * - caps.tsc_crystal_ratio_numerator / - caps.tsc_crystal_ratio_denominator; - } else { - caps.tsc_frequency = X64::EstimateRDTSCFrequency(); - } - } - - if (max_std_fn >= 0x16) { - __cpuid(cpu_id, 0x16); - caps.base_frequency = cpu_id[0]; - caps.max_frequency = cpu_id[1]; - caps.bus_frequency = cpu_id[2]; - } - - return caps; -} - -const CPUCaps& GetCPUCaps() { - static CPUCaps caps = Detect(); - return caps; -} - std::optional GetProcessorCount() { #if defined(_WIN32) // Get the buffer length. @@ -253,4 +112,315 @@ std::optional GetProcessorCount() { #endif } +/// @brief Detects the various CPU features +const CPUCaps g_cpu_caps = [] { + CPUCaps caps = {}; + + // Assumes the CPU supports the CPUID instruction. Those that don't would likely not support + // yuzu at all anyway + int cpu_id[4]; + + // Detect CPU's CPUID capabilities and grab manufacturer string + __cpuid(cpu_id, 0x00000000); + const u32 max_std_fn = cpu_id[0]; // EAX + + std::memset(caps.brand_string, 0, std::size(caps.brand_string)); + std::memcpy(&caps.brand_string[0], &cpu_id[1], sizeof(u32)); + std::memcpy(&caps.brand_string[4], &cpu_id[3], sizeof(u32)); + std::memcpy(&caps.brand_string[8], &cpu_id[2], sizeof(u32)); + + caps.manufacturer = CPUCaps::ParseManufacturer(caps.brand_string); + + // Set reasonable default cpu string even if brand string not available + std::strncpy(caps.cpu_string, caps.brand_string, std::size(caps.brand_string)); + + __cpuid(cpu_id, 0x80000000); + + const u32 max_ex_fn = cpu_id[0]; + + // Detect family and other miscellaneous features + if (max_std_fn >= 1) { + __cpuid(cpu_id, 0x00000001); + caps.sse3 = Common::Bit<0>(cpu_id[2]); + caps.pclmulqdq = Common::Bit<1>(cpu_id[2]); + caps.ssse3 = Common::Bit<9>(cpu_id[2]); + caps.sse4_1 = Common::Bit<19>(cpu_id[2]); + caps.sse4_2 = Common::Bit<20>(cpu_id[2]); + caps.movbe = Common::Bit<22>(cpu_id[2]); + caps.popcnt = Common::Bit<23>(cpu_id[2]); + caps.aes = Common::Bit<25>(cpu_id[2]); + caps.f16c = Common::Bit<29>(cpu_id[2]); + + // AVX support requires 3 separate checks: + // - Is the AVX bit set in CPUID? + // - Is the XSAVE bit set in CPUID? + // - XGETBV result has the XCR bit set. + if (Common::Bit<28>(cpu_id[2]) && Common::Bit<27>(cpu_id[2])) { + if ((xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6) { + caps.avx = true; + if (Common::Bit<12>(cpu_id[2])) + caps.fma = true; + } + } + + if (max_std_fn >= 7) { + __cpuidex(cpu_id, 0x00000007, 0x00000000); + // Can't enable AVX{2,512} unless the XSAVE/XGETBV checks above passed + if (caps.avx) { + caps.avx2 = Common::Bit<5>(cpu_id[1]); + caps.avx512f = Common::Bit<16>(cpu_id[1]); + caps.avx512dq = Common::Bit<17>(cpu_id[1]); + caps.avx512cd = Common::Bit<28>(cpu_id[1]); + caps.avx512bw = Common::Bit<30>(cpu_id[1]); + caps.avx512vl = Common::Bit<31>(cpu_id[1]); + caps.avx512vbmi = Common::Bit<1>(cpu_id[2]); + caps.avx512bitalg = Common::Bit<12>(cpu_id[2]); + } + + caps.bmi1 = Common::Bit<3>(cpu_id[1]); + caps.bmi2 = Common::Bit<8>(cpu_id[1]); + caps.sha = Common::Bit<29>(cpu_id[1]); + + caps.waitpkg = Common::Bit<5>(cpu_id[2]); + caps.gfni = Common::Bit<8>(cpu_id[2]); + } + } + + if (max_ex_fn >= 0x80000004) { + // Extract CPU model string + __cpuid(cpu_id, 0x80000002); + std::memcpy(caps.cpu_string, cpu_id, sizeof(cpu_id)); + __cpuid(cpu_id, 0x80000003); + std::memcpy(caps.cpu_string + 16, cpu_id, sizeof(cpu_id)); + __cpuid(cpu_id, 0x80000004); + std::memcpy(caps.cpu_string + 32, cpu_id, sizeof(cpu_id)); + } + + if (max_ex_fn >= 0x80000001) { + // Check for more features + __cpuid(cpu_id, 0x80000001); + caps.lzcnt = Common::Bit<5>(cpu_id[2]); + caps.monitorx = Common::Bit<29>(cpu_id[2]); + } + + if (max_ex_fn >= 0x80000007) { + __cpuid(cpu_id, 0x80000007); + caps.invariant_tsc = Common::Bit<8>(cpu_id[3]); + } + + if (max_std_fn >= 0x15) { + __cpuid(cpu_id, 0x15); + caps.tsc_crystal_ratio_denominator = cpu_id[0]; + caps.tsc_crystal_ratio_numerator = cpu_id[1]; + caps.crystal_frequency = cpu_id[2]; + // Some CPU models might not return a crystal frequency. + // The CPU model can be detected to use the values from turbostat + // https://github.com/torvalds/linux/blob/master/tools/power/x86/turbostat/turbostat.c#L5569 + // but it's easier to just estimate the TSC tick rate for these cases. + if (caps.tsc_crystal_ratio_denominator) { + caps.tsc_frequency = u64(caps.crystal_frequency) + * caps.tsc_crystal_ratio_numerator / caps.tsc_crystal_ratio_denominator; + } else { + caps.tsc_frequency = X64::EstimateRDTSCFrequency(); + } + } + + if (max_std_fn >= 0x16) { + __cpuid(cpu_id, 0x16); + caps.base_frequency = cpu_id[0]; + caps.max_frequency = cpu_id[1]; + caps.bus_frequency = cpu_id[2]; + } + return caps; +}(); + +#else + +#endif + +#if defined(ARCHITECTURE_x86_64) +WallClock::WallClock(bool invariant_, u64 rdtsc_frequency_) noexcept + : rdtsc_frequency{rdtsc_frequency_} + , ns_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(NsRatio::den, rdtsc_frequency_) : 0} + , us_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(UsRatio::den, rdtsc_frequency_) : 0} + , ms_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(MsRatio::den, rdtsc_frequency_) : 0} + , rdtsc_ns_factor{invariant_ ? GetFixedPoint64Factor(rdtsc_frequency_, NsRatio::den) : 1} + , cntpct_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(CNTFRQ, rdtsc_frequency_) : 0} + , gputick_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(GPUTickFreq, rdtsc_frequency_) : 0} + , invariant{invariant_} +{} + +std::chrono::nanoseconds WallClock::GetTimeNS() const { + if (!invariant) + return std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()); + return std::chrono::nanoseconds{MultiplyHigh(GetUptime(), ns_rdtsc_factor)}; +} + +std::chrono::microseconds WallClock::GetTimeUS() const { + if (!invariant) + return std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()); + return std::chrono::microseconds{MultiplyHigh(GetUptime(), us_rdtsc_factor)}; +} + +std::chrono::milliseconds WallClock::GetTimeMS() const { + if (!invariant) + return std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()); + return std::chrono::milliseconds{MultiplyHigh(GetUptime(), ms_rdtsc_factor)}; +} + +s64 WallClock::GetCNTPCT() const { + if (!invariant) + return GetUptime() * NsToCNTPCTRatio::num / NsToCNTPCTRatio::den; + return MultiplyHigh(GetUptime(), cntpct_rdtsc_factor); +} + +s64 WallClock::GetGPUTick() const { + if (!invariant) + return GetUptime() * NsToGPUTickRatio::num / NsToGPUTickRatio::den; + return MultiplyHigh(GetUptime(), gputick_rdtsc_factor); +} + +s64 WallClock::GetUptime() const { + if (!invariant) + return std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); + return s64(Common::X64::FencedRDTSC()); +} + +bool WallClock::IsNative() const { + return invariant; +} + +u64 WallClock::NsToTicks(std::chrono::nanoseconds ns) const { + return invariant ? MultiplyHigh(ns.count(), rdtsc_ns_factor) : ns.count(); +} +#elif defined(HAS_NCE) +namespace { +[[nodiscard]] Common::WallClock::FactorType GetFixedPointFactor(u64 num, u64 den) noexcept { + return (Common::WallClock::FactorType(num) << 64) / den; +} +[[nodiscard]] u64 MultiplyHigh(u64 m, Common::WallClock::FactorType factor) noexcept { + return static_cast((m * factor) >> 64); +} +[[nodiscard]] s64 GetHostCNTFRQ() noexcept { + u64 cntfrq_el0 = 0; +#ifdef __ANDROID__ + std::string_view board{""}; + char buffer[PROP_VALUE_MAX]; + int len{__system_property_get("ro.product.board", buffer)}; + board = std::string_view(buffer, static_cast(len)); + if (board == "s5e9925") { // Exynos 2200 + cntfrq_el0 = 25600000; + } else if (board == "exynos2100") { // Exynos 2100 + cntfrq_el0 = 26000000; + } else if (board == "exynos9810") { // Exynos 9810 + cntfrq_el0 = 26000000; + } else if (board == "s5e8825") { // Exynos 1280 + cntfrq_el0 = 26000000; + } else { + asm volatile("mrs %[cntfrq_el0], cntfrq_el0" : [cntfrq_el0] "=r"(cntfrq_el0)); + } + return cntfrq_el0; +#else + asm volatile("mrs %[cntfrq_el0], cntfrq_el0" : [cntfrq_el0] "=r"(cntfrq_el0)); + return cntfrq_el0; +#endif +} +} // namespace + +WallClock::WallClock(bool invariant_, u64 rdtsc_frequency_) noexcept { + const u64 host_cntfrq = std::max(GetHostCNTFRQ(), 1); + ns_cntfrq_factor = GetFixedPointFactor(NsRatio::den, host_cntfrq); + us_cntfrq_factor = GetFixedPointFactor(UsRatio::den, host_cntfrq); + ms_cntfrq_factor = GetFixedPointFactor(MsRatio::den, host_cntfrq); + cntfrq_ns_factor = GetFixedPointFactor(host_cntfrq, NsRatio::den); + guest_cntfrq_factor = GetFixedPointFactor(CNTFRQ, host_cntfrq); + gputick_cntfrq_factor = GetFixedPointFactor(GPUTickFreq, host_cntfrq); +} + +std::chrono::nanoseconds WallClock::GetTimeNS() const { + return std::chrono::nanoseconds{MultiplyHigh(GetUptime(), ns_cntfrq_factor)}; +} + +std::chrono::microseconds WallClock::GetTimeUS() const { + return std::chrono::microseconds{MultiplyHigh(GetUptime(), us_cntfrq_factor)}; +} + +std::chrono::milliseconds WallClock::GetTimeMS() const { + return std::chrono::milliseconds{MultiplyHigh(GetUptime(), ms_cntfrq_factor)}; +} + +s64 WallClock::GetCNTPCT() const { + return MultiplyHigh(GetUptime(), guest_cntfrq_factor); +} + +s64 WallClock::GetGPUTick() const { + return MultiplyHigh(GetUptime(), gputick_cntfrq_factor); +} + +s64 WallClock::GetUptime() const { + s64 cntvct_el0 = 0; + asm volatile( + "dsb ish\n\t" + "mrs %[cntvct_el0], cntvct_el0\n\t" + "dsb ish\n\t" + : [cntvct_el0] "=r"(cntvct_el0) + ); + return cntvct_el0; +} + +bool WallClock::IsNative() const { + return true; +} + +u64 WallClock::NsToTicks(std::chrono::nanoseconds ns) const { + return MultiplyHigh(ns.count(), cntfrq_ns_factor); +} +#else +WallClock::WallClock(bool invariant_, u64 rdtsc_frequency_) noexcept {} + +std::chrono::nanoseconds WallClock::GetTimeNS() const { + return std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()); +} + +std::chrono::microseconds WallClock::GetTimeUS() const { + return std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()); +} + +std::chrono::milliseconds WallClock::GetTimeMS() const { + return std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()); +} + +s64 WallClock::GetCNTPCT() const { + return GetUptime() * NsToCNTPCTRatio::num / NsToCNTPCTRatio::den; +} + +s64 WallClock::GetGPUTick() const { + return GetUptime() * NsToGPUTickRatio::num / NsToGPUTickRatio::den; +} + +s64 WallClock::GetUptime() const { + return std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); +} + +bool WallClock::IsNative() const { + return false; +} + +u64 WallClock::NsToTicks(std::chrono::nanoseconds ns) const { + return ns.count(); +} +#endif + +// Wall clock MUST be initialized AFTER g_cpu_caps +// C++ only guarantees ctor init in the order they appear in TU +const WallClock g_wall_clock = [] { +#if defined(ARCHITECTURE_x86_64) + auto const& caps = Common::g_cpu_caps; + return WallClock(caps.invariant_tsc && caps.tsc_frequency >= std::nano::den, caps.tsc_frequency); +#elif defined(HAS_NCE) + return WallClock(false, 1); +#else + return WallClock(true, 1); +#endif +}(); } // namespace Common diff --git a/src/common/cpu_features.h b/src/common/cpu_features.h new file mode 100644 index 0000000000..7dcb550f8f --- /dev/null +++ b/src/common/cpu_features.h @@ -0,0 +1,187 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2013 Dolphin Emulator Project / 2015 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include +#include + +#include "common/common_types.h" + +namespace Common { + +class WallClock { +public: + static constexpr u64 CNTFRQ = 19'200'000; // CNTPCT_EL0 Frequency = 19.2 MHz + static constexpr u64 GPUTickFreq = 614'400'000; // GM20B GPU Tick Frequency = 614.4 MHz + static constexpr u64 CPUTickFreq = 1'020'000'000; // T210/4 A57 CPU Tick Frequency = 1020.0 MHz + + explicit WallClock(bool invariant, u64 rdtsc_frequency_) noexcept; + + /// @returns The time in nanoseconds since the construction of this clock. + std::chrono::nanoseconds GetTimeNS() const; + + /// @returns The time in microseconds since the construction of this clock. + std::chrono::microseconds GetTimeUS() const; + + /// @returns The time in milliseconds since the construction of this clock. + std::chrono::milliseconds GetTimeMS() const; + + /// @returns The guest CNTPCT ticks since the construction of this clock. + s64 GetCNTPCT() const; + + /// @returns The guest GPU ticks since the construction of this clock. + s64 GetGPUTick() const; + + /// @returns The raw host timer ticks since an indeterminate epoch. + s64 GetUptime() const; + + /// @returns Whether the clock directly uses the host's hardware clock. + bool IsNative() const; + + // @returns Nanoseconds to native ticks + u64 NsToTicks(std::chrono::nanoseconds ns) const; + + static inline u64 NSToCNTPCT(u64 ns) { + return ns * NsToCNTPCTRatio::num / NsToCNTPCTRatio::den; + } + + static inline u64 NSToGPUTick(u64 ns) { + return ns * NsToGPUTickRatio::num / NsToGPUTickRatio::den; + } + + // Cycle Timing + + static inline u64 CPUTickToNS(u64 cpu_tick) { + return cpu_tick * CPUTickToNsRatio::num / CPUTickToNsRatio::den; + } + + static inline u64 CPUTickToUS(u64 cpu_tick) { + return cpu_tick * CPUTickToUsRatio::num / CPUTickToUsRatio::den; + } + + static inline u64 CPUTickToCNTPCT(u64 cpu_tick) { + return cpu_tick * CPUTickToCNTPCTRatio::num / CPUTickToCNTPCTRatio::den; + } + + static inline u64 CPUTickToGPUTick(u64 cpu_tick) { + return cpu_tick * CPUTickToGPUTickRatio::num / CPUTickToGPUTickRatio::den; + } + + using NsRatio = std::nano; + using UsRatio = std::micro; + using MsRatio = std::milli; + + using NsToUsRatio = std::ratio_divide; + using NsToMsRatio = std::ratio_divide; + using NsToCNTPCTRatio = std::ratio; + using NsToGPUTickRatio = std::ratio; + + // Cycle Timing + + using CPUTickToNsRatio = std::ratio; + using CPUTickToUsRatio = std::ratio; + using CPUTickToCNTPCTRatio = std::ratio; + using CPUTickToGPUTickRatio = std::ratio; + +#if defined(ARCHITECTURE_x86_64) + u64 rdtsc_frequency; + u64 ns_rdtsc_factor; + u64 us_rdtsc_factor; + u64 ms_rdtsc_factor; + u64 rdtsc_ns_factor; + u64 cntpct_rdtsc_factor; + u64 gputick_rdtsc_factor; + bool invariant; +#elif defined(HAS_NCE) + using FactorType = unsigned __int128; + [[nodiscard]] inline FactorType GetGuestCNTFRQFactor() const { + return guest_cntfrq_factor; + } + FactorType ns_cntfrq_factor; + FactorType us_cntfrq_factor; + FactorType ms_cntfrq_factor; + FactorType cntfrq_ns_factor; + FactorType guest_cntfrq_factor; + FactorType gputick_cntfrq_factor; +#endif +}; + +#ifdef ARCHITECTURE_x86_64 +/// x86/x64 CPU capabilities that may be detected by this module +struct CPUCaps { + enum class Manufacturer : u8 { + Unknown = 0, + Intel = 1, + AMD = 2, + Hygon = 3, + }; + + static Manufacturer ParseManufacturer(std::string_view brand_string); + + Manufacturer manufacturer; + char brand_string[13]; + + char cpu_string[48]; + + u32 base_frequency; + u32 max_frequency; + u32 bus_frequency; + + u32 tsc_crystal_ratio_denominator; + u32 tsc_crystal_ratio_numerator; + u32 crystal_frequency; + u64 tsc_frequency; // Derived from the above three values + + bool sse3 : 1; + bool ssse3 : 1; + bool sse4_1 : 1; + bool sse4_2 : 1; + + bool avx : 1; + bool avx2 : 1; + bool avx512f : 1; + bool avx512dq : 1; + bool avx512cd : 1; + bool avx512bw : 1; + bool avx512vl : 1; + bool avx512vbmi : 1; + bool avx512bitalg : 1; + + bool aes : 1; + bool bmi1 : 1; + bool bmi2 : 1; + bool f16c : 1; + bool fma : 1; + bool gfni : 1; + bool invariant_tsc : 1; + bool lzcnt : 1; + bool monitorx : 1; + bool movbe : 1; + bool pclmulqdq : 1; + bool popcnt : 1; + bool sha : 1; + bool waitpkg : 1; +}; +#else +struct CPUCaps { + bool padding; +}; +#endif + +/// Detects CPU core count +std::optional GetProcessorCount(); + +/// @brief Global cpu caps +extern const CPUCaps g_cpu_caps; +/// @brief Global wall clock +extern const WallClock g_wall_clock; + +} // namespace Common diff --git a/src/common/demangle.cpp b/src/common/demangle.cpp index 8cb8c35515..8f76a884a8 100644 --- a/src/common/demangle.cpp +++ b/src/common/demangle.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project @@ -6,30 +6,44 @@ #include #include +#ifdef _WIN32 #include - +#else +#include +#endif #include "common/demangle.h" -#include "common/scope_exit.h" + +static bool IsItanium(std::string_view name) { + // A valid Itanium encoding requires 1-4 leading underscores, followed by 'Z'. + auto const pos = name.find_first_not_of('_'); + return pos > 0 && pos <= 4 && pos < name.size() && name[pos] == 'Z'; +} namespace Common { - std::string DemangleSymbol(const std::string& mangled) { if (mangled.size() > 0) { - auto const is_itanium = [](std::string_view name) -> bool { - // A valid Itanium encoding requires 1-4 leading underscores, followed by 'Z'. - auto const pos = name.find_first_not_of('_'); - return pos > 0 && pos <= 4 && pos < name.size() && name[pos] == 'Z'; - }; - std::string ret = mangled; - if (is_itanium(mangled)) { + if (IsItanium(mangled)) { +#ifdef _WIN32 + // requires the use of llvm if (char* p = llvm::itaniumDemangle(mangled); p != nullptr) { - ret = std::string{p}; + std::string ret = std::string{p}; std::free(p); + return ret; } +#else + // can safely use libc++ and glibcxx provided demangling functions + // it's available since 2008(!) so no system should have issues with it + // see https://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-html-USERS-4.3/a01696.html + int status; + if (char* p = abi::__cxa_demangle(mangled.c_str(), NULL, NULL, &status); p != nullptr) { + std::string ret = std::string{p}; + std::free(p); + return ret; + } +#endif } - return ret; + return mangled; } return std::string{}; } - } // namespace Common diff --git a/src/common/demangle.h b/src/common/demangle.h index f072d22f33..c96f14ff42 100644 --- a/src/common/demangle.h +++ b/src/common/demangle.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/common/dynamic_library.cpp b/src/common/dynamic_library.cpp index 566fbed631..462faa673d 100644 --- a/src/common/dynamic_library.cpp +++ b/src/common/dynamic_library.cpp @@ -1,7 +1,15 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2019 Dolphin Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include + +#ifndef _WIN32 +#include +#endif + #include #include diff --git a/src/common/error.cpp b/src/common/error.cpp index 1b2009db71..070c9a5c7e 100644 --- a/src/common/error.cpp +++ b/src/common/error.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2013 Dolphin Emulator Project // SPDX-FileCopyrightText: 2014 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -30,7 +33,7 @@ std::string NativeErrorToString(int e) { return ret; #else char err_str[255]; -#if defined(ANDROID) || \ +#if defined(__ANDROID__) || \ (defined(__GLIBC__) && (_GNU_SOURCE || (_POSIX_C_SOURCE < 200112L && _XOPEN_SOURCE < 600))) // Thread safe (GNU-specific) const char* str = strerror_r(e, err_str, sizeof(err_str)); diff --git a/src/common/expected.h b/src/common/expected.h deleted file mode 100644 index 5fccfbcbdd..0000000000 --- a/src/common/expected.h +++ /dev/null @@ -1,986 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -// This is based on the proposed implementation of std::expected (P0323) -// https://github.com/TartanLlama/expected/blob/master/include/tl/expected.hpp - -#pragma once - -#include -#include - -namespace Common { - -template -class Expected; - -template -class Unexpected { -public: - Unexpected() = delete; - - constexpr explicit Unexpected(const E& e) : m_val{e} {} - - constexpr explicit Unexpected(E&& e) : m_val{std::move(e)} {} - - constexpr E& value() & { - return m_val; - } - - constexpr const E& value() const& { - return m_val; - } - - constexpr E&& value() && { - return std::move(m_val); - } - - constexpr const E&& value() const&& { - return std::move(m_val); - } - -private: - E m_val; -}; - -template -constexpr auto operator<=>(const Unexpected& lhs, const Unexpected& rhs) { - return lhs.value() <=> rhs.value(); -} - -struct unexpect_t { - constexpr explicit unexpect_t() = default; -}; - -namespace detail { - -struct no_init_t { - constexpr explicit no_init_t() = default; -}; - -/** - * This specialization is for when T is not trivially destructible, - * so the destructor must be called on destruction of `expected' - * Additionally, this requires E to be trivially destructible - */ -template > - requires std::is_trivially_destructible_v -struct expected_storage_base { - constexpr expected_storage_base() : m_val{T{}}, m_has_val{true} {} - - constexpr expected_storage_base(no_init_t) : m_has_val{false} {} - - template >* = nullptr> - constexpr expected_storage_base(std::in_place_t, Args&&... args) - : m_val{std::forward(args)...}, m_has_val{true} {} - - template &, Args&&...>>* = - nullptr> - constexpr expected_storage_base(std::in_place_t, std::initializer_list il, Args&&... args) - : m_val{il, std::forward(args)...}, m_has_val{true} {} - - template >* = nullptr> - constexpr explicit expected_storage_base(unexpect_t, Args&&... args) - : m_unexpect{std::forward(args)...}, m_has_val{false} {} - - template &, Args&&...>>* = - nullptr> - constexpr explicit expected_storage_base(unexpect_t, std::initializer_list il, - Args&&... args) - : m_unexpect{il, std::forward(args)...}, m_has_val{false} {} - - ~expected_storage_base() { - if (m_has_val) { - m_val.~T(); - } - } - - union { - T m_val; - Unexpected m_unexpect; - }; - - bool m_has_val; -}; - -/** - * This specialization is for when T is trivially destructible, - * so the destructor of `expected` can be trivial - * Additionally, this requires E to be trivially destructible - */ -template - requires std::is_trivially_destructible_v -struct expected_storage_base { - constexpr expected_storage_base() : m_val{T{}}, m_has_val{true} {} - - constexpr expected_storage_base(no_init_t) : m_has_val{false} {} - - template >* = nullptr> - constexpr expected_storage_base(std::in_place_t, Args&&... args) - : m_val{std::forward(args)...}, m_has_val{true} {} - - template &, Args&&...>>* = - nullptr> - constexpr expected_storage_base(std::in_place_t, std::initializer_list il, Args&&... args) - : m_val{il, std::forward(args)...}, m_has_val{true} {} - - template >* = nullptr> - constexpr explicit expected_storage_base(unexpect_t, Args&&... args) - : m_unexpect{std::forward(args)...}, m_has_val{false} {} - - template &, Args&&...>>* = - nullptr> - constexpr explicit expected_storage_base(unexpect_t, std::initializer_list il, - Args&&... args) - : m_unexpect{il, std::forward(args)...}, m_has_val{false} {} - - ~expected_storage_base() = default; - - union { - T m_val; - Unexpected m_unexpect; - }; - - bool m_has_val; -}; - -template -struct expected_operations_base : expected_storage_base { - using expected_storage_base::expected_storage_base; - - template - void construct(Args&&... args) noexcept { - new (std::addressof(this->m_val)) T{std::forward(args)...}; - this->m_has_val = true; - } - - template - void construct_with(Rhs&& rhs) noexcept { - new (std::addressof(this->m_val)) T{std::forward(rhs).get()}; - this->m_has_val = true; - } - - template - void construct_error(Args&&... args) noexcept { - new (std::addressof(this->m_unexpect)) Unexpected{std::forward(args)...}; - this->m_has_val = false; - } - - void assign(const expected_operations_base& rhs) noexcept { - if (!this->m_has_val && rhs.m_has_val) { - geterr().~Unexpected(); - construct(rhs.get()); - } else { - assign_common(rhs); - } - } - - void assign(expected_operations_base&& rhs) noexcept { - if (!this->m_has_val && rhs.m_has_val) { - geterr().~Unexpected(); - construct(std::move(rhs).get()); - } else { - assign_common(rhs); - } - } - - template - void assign_common(Rhs&& rhs) { - if (this->m_has_val) { - if (rhs.m_has_val) { - get() = std::forward(rhs).get(); - } else { - destroy_val(); - construct_error(std::forward(rhs).geterr()); - } - } else { - if (!rhs.m_has_val) { - geterr() = std::forward(rhs).geterr(); - } - } - } - - bool has_value() const { - return this->m_has_val; - } - - constexpr T& get() & { - return this->m_val; - } - - constexpr const T& get() const& { - return this->m_val; - } - - constexpr T&& get() && { - return std::move(this->m_val); - } - - constexpr const T&& get() const&& { - return std::move(this->m_val); - } - - constexpr Unexpected& geterr() & { - return this->m_unexpect; - } - - constexpr const Unexpected& geterr() const& { - return this->m_unexpect; - } - - constexpr Unexpected&& geterr() && { - return std::move(this->m_unexpect); - } - - constexpr const Unexpected&& geterr() const&& { - return std::move(this->m_unexpect); - } - - constexpr void destroy_val() { - get().~T(); - } -}; - -/** - * This manages conditionally having a trivial copy constructor - * This specialization is for when T is trivially copy constructible - * Additionally, this requires E to be trivially copy constructible - */ -template > - requires std::is_trivially_copy_constructible_v -struct expected_copy_base : expected_operations_base { - using expected_operations_base::expected_operations_base; -}; - -/** - * This specialization is for when T is not trivially copy constructible - * Additionally, this requires E to be trivially copy constructible - */ -template - requires std::is_trivially_copy_constructible_v -struct expected_copy_base : expected_operations_base { - using expected_operations_base::expected_operations_base; - - expected_copy_base() = default; - - expected_copy_base(const expected_copy_base& rhs) - : expected_operations_base{no_init_t{}} { - if (rhs.has_value()) { - this->construct_with(rhs); - } else { - this->construct_error(rhs.geterr()); - } - } - - expected_copy_base(expected_copy_base&&) = default; - - expected_copy_base& operator=(const expected_copy_base&) = default; - - expected_copy_base& operator=(expected_copy_base&&) = default; -}; - -/** - * This manages conditionally having a trivial move constructor - * This specialization is for when T is trivially move constructible - * Additionally, this requires E to be trivially move constructible - */ -template > - requires std::is_trivially_move_constructible_v -struct expected_move_base : expected_copy_base { - using expected_copy_base::expected_copy_base; -}; - -/** - * This specialization is for when T is not trivially move constructible - * Additionally, this requires E to be trivially move constructible - */ -template - requires std::is_trivially_move_constructible_v -struct expected_move_base : expected_copy_base { - using expected_copy_base::expected_copy_base; - - expected_move_base() = default; - - expected_move_base(const expected_move_base&) = default; - - expected_move_base(expected_move_base&& rhs) noexcept(std::is_nothrow_move_constructible_v) - : expected_copy_base{no_init_t{}} { - if (rhs.has_value()) { - this->construct_with(std::move(rhs)); - } else { - this->construct_error(std::move(rhs.geterr())); - } - } - - expected_move_base& operator=(const expected_move_base&) = default; - - expected_move_base& operator=(expected_move_base&&) = default; -}; - -/** - * This manages conditionally having a trivial copy assignment operator - * This specialization is for when T is trivially copy assignable - * Additionally, this requires E to be trivially copy assignable - */ -template , - std::is_trivially_copy_constructible, - std::is_trivially_destructible>> - requires std::conjunction_v, - std::is_trivially_copy_constructible, - std::is_trivially_destructible> -struct expected_copy_assign_base : expected_move_base { - using expected_move_base::expected_move_base; -}; - -/** - * This specialization is for when T is not trivially copy assignable - * Additionally, this requires E to be trivially copy assignable - */ -template - requires std::conjunction_v, - std::is_trivially_copy_constructible, - std::is_trivially_destructible> -struct expected_copy_assign_base : expected_move_base { - using expected_move_base::expected_move_base; - - expected_copy_assign_base() = default; - - expected_copy_assign_base(const expected_copy_assign_base&) = default; - - expected_copy_assign_base(expected_copy_assign_base&&) = default; - - expected_copy_assign_base& operator=(const expected_copy_assign_base& rhs) { - this->assign(rhs); - return *this; - } - - expected_copy_assign_base& operator=(expected_copy_assign_base&&) = default; -}; - -/** - * This manages conditionally having a trivial move assignment operator - * This specialization is for when T is trivially move assignable - * Additionally, this requires E to be trivially move assignable - */ -template , - std::is_trivially_move_constructible, - std::is_trivially_destructible>> - requires std::conjunction_v, - std::is_trivially_move_constructible, - std::is_trivially_destructible> -struct expected_move_assign_base : expected_copy_assign_base { - using expected_copy_assign_base::expected_copy_assign_base; -}; - -/** - * This specialization is for when T is not trivially move assignable - * Additionally, this requires E to be trivially move assignable - */ -template - requires std::conjunction_v, - std::is_trivially_move_constructible, - std::is_trivially_destructible> -struct expected_move_assign_base : expected_copy_assign_base { - using expected_copy_assign_base::expected_copy_assign_base; - - expected_move_assign_base() = default; - - expected_move_assign_base(const expected_move_assign_base&) = default; - - expected_move_assign_base(expected_move_assign_base&&) = default; - - expected_move_assign_base& operator=(const expected_move_assign_base&) = default; - - expected_move_assign_base& operator=(expected_move_assign_base&& rhs) noexcept( - std::conjunction_v, - std::is_nothrow_move_assignable>) { - this->assign(std::move(rhs)); - return *this; - } -}; - -/** - * expected_delete_ctor_base will conditionally delete copy and move constructors - * depending on whether T is copy/move constructible - * Additionally, this requires E to be copy/move constructible - */ -template , - bool EnableMove = std::is_move_constructible_v> - requires std::conjunction_v, std::is_move_constructible> -struct expected_delete_ctor_base { - expected_delete_ctor_base() = default; - expected_delete_ctor_base(const expected_delete_ctor_base&) = default; - expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = default; - expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) = default; - expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept = default; -}; - -template - requires std::conjunction_v, std::is_move_constructible> -struct expected_delete_ctor_base { - expected_delete_ctor_base() = default; - expected_delete_ctor_base(const expected_delete_ctor_base&) = default; - expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = delete; - expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) = default; - expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept = default; -}; - -template - requires std::conjunction_v, std::is_move_constructible> -struct expected_delete_ctor_base { - expected_delete_ctor_base() = default; - expected_delete_ctor_base(const expected_delete_ctor_base&) = delete; - expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = default; - expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) = default; - expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept = default; -}; - -template - requires std::conjunction_v, std::is_move_constructible> -struct expected_delete_ctor_base { - expected_delete_ctor_base() = default; - expected_delete_ctor_base(const expected_delete_ctor_base&) = delete; - expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = delete; - expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) = default; - expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept = default; -}; - -/** - * expected_delete_assign_base will conditionally delete copy and move assignment operators - * depending on whether T is copy/move constructible + assignable - * Additionally, this requires E to be copy/move constructible + assignable - */ -template < - typename T, typename E, - bool EnableCopy = std::conjunction_v, std::is_copy_assignable>, - bool EnableMove = std::conjunction_v, std::is_move_assignable>> - requires std::conjunction_v, std::is_move_constructible, - std::is_copy_assignable, std::is_move_assignable> -struct expected_delete_assign_base { - expected_delete_assign_base() = default; - expected_delete_assign_base(const expected_delete_assign_base&) = default; - expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default; - expected_delete_assign_base& operator=(const expected_delete_assign_base&) = default; - expected_delete_assign_base& operator=(expected_delete_assign_base&&) noexcept = default; -}; - -template - requires std::conjunction_v, std::is_move_constructible, - std::is_copy_assignable, std::is_move_assignable> -struct expected_delete_assign_base { - expected_delete_assign_base() = default; - expected_delete_assign_base(const expected_delete_assign_base&) = default; - expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default; - expected_delete_assign_base& operator=(const expected_delete_assign_base&) = default; - expected_delete_assign_base& operator=(expected_delete_assign_base&&) noexcept = delete; -}; - -template - requires std::conjunction_v, std::is_move_constructible, - std::is_copy_assignable, std::is_move_assignable> -struct expected_delete_assign_base { - expected_delete_assign_base() = default; - expected_delete_assign_base(const expected_delete_assign_base&) = default; - expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default; - expected_delete_assign_base& operator=(const expected_delete_assign_base&) = delete; - expected_delete_assign_base& operator=(expected_delete_assign_base&&) noexcept = default; -}; - -template - requires std::conjunction_v, std::is_move_constructible, - std::is_copy_assignable, std::is_move_assignable> -struct expected_delete_assign_base { - expected_delete_assign_base() = default; - expected_delete_assign_base(const expected_delete_assign_base&) = default; - expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default; - expected_delete_assign_base& operator=(const expected_delete_assign_base&) = delete; - expected_delete_assign_base& operator=(expected_delete_assign_base&&) noexcept = delete; -}; - -/** - * This is needed to be able to construct the expected_default_ctor_base which follows, - * while still conditionally deleting the default constructor. - */ -struct default_constructor_tag { - constexpr explicit default_constructor_tag() = default; -}; - -/** - * expected_default_ctor_base will ensure that expected - * has a deleted default constructor if T is not default constructible - * This specialization is for when T is default constructible - */ -template > -struct expected_default_ctor_base { - constexpr expected_default_ctor_base() noexcept = default; - constexpr expected_default_ctor_base(expected_default_ctor_base const&) noexcept = default; - constexpr expected_default_ctor_base(expected_default_ctor_base&&) noexcept = default; - expected_default_ctor_base& operator=(expected_default_ctor_base const&) noexcept = default; - expected_default_ctor_base& operator=(expected_default_ctor_base&&) noexcept = default; - - constexpr explicit expected_default_ctor_base(default_constructor_tag) {} -}; - -template -struct expected_default_ctor_base { - constexpr expected_default_ctor_base() noexcept = delete; - constexpr expected_default_ctor_base(expected_default_ctor_base const&) noexcept = default; - constexpr expected_default_ctor_base(expected_default_ctor_base&&) noexcept = default; - expected_default_ctor_base& operator=(expected_default_ctor_base const&) noexcept = default; - expected_default_ctor_base& operator=(expected_default_ctor_base&&) noexcept = default; - - constexpr explicit expected_default_ctor_base(default_constructor_tag) {} -}; - -template -using expected_enable_forward_value = - std::enable_if_t && - !std::is_same_v, std::in_place_t> && - !std::is_same_v, std::remove_cvref_t> && - !std::is_same_v, std::remove_cvref_t>>; - -template -using expected_enable_from_other = std::enable_if_t< - std::is_constructible_v && std::is_constructible_v && - !std::is_constructible_v&> && !std::is_constructible_v&&> && - !std::is_constructible_v&> && - !std::is_constructible_v&&> && - !std::is_convertible_v&, T> && !std::is_convertible_v&&, T> && - !std::is_convertible_v&, T> && - !std::is_convertible_v&&, T>>; - -} // namespace detail - -template -class Expected : private detail::expected_move_assign_base, - private detail::expected_delete_ctor_base, - private detail::expected_delete_assign_base, - private detail::expected_default_ctor_base { -public: - using value_type = T; - using error_type = E; - using unexpected_type = Unexpected; - - constexpr Expected() = default; - constexpr Expected(const Expected&) = default; - constexpr Expected(Expected&&) = default; - Expected& operator=(const Expected&) = default; - Expected& operator=(Expected&&) = default; - - template >* = nullptr> - constexpr Expected(std::in_place_t, Args&&... args) - : impl_base{std::in_place, std::forward(args)...}, - ctor_base{detail::default_constructor_tag{}} {} - - template &, Args&&...>>* = - nullptr> - constexpr Expected(std::in_place_t, std::initializer_list il, Args&&... args) - : impl_base{std::in_place, il, std::forward(args)...}, - ctor_base{detail::default_constructor_tag{}} {} - - template >* = nullptr, - std::enable_if_t>* = nullptr> - constexpr explicit Expected(const Unexpected& e) - : impl_base{unexpect_t{}, e.value()}, ctor_base{detail::default_constructor_tag{}} {} - - template >* = nullptr, - std::enable_if_t>* = nullptr> - constexpr Expected(Unexpected const& e) - : impl_base{unexpect_t{}, e.value()}, ctor_base{detail::default_constructor_tag{}} {} - - template >* = nullptr, - std::enable_if_t>* = nullptr> - constexpr explicit Expected(Unexpected&& e) noexcept(std::is_nothrow_constructible_v) - : impl_base{unexpect_t{}, std::move(e.value())}, ctor_base{ - detail::default_constructor_tag{}} {} - - template >* = nullptr, - std::enable_if_t>* = nullptr> - constexpr Expected(Unexpected&& e) noexcept(std::is_nothrow_constructible_v) - : impl_base{unexpect_t{}, std::move(e.value())}, ctor_base{ - detail::default_constructor_tag{}} {} - - template >* = nullptr> - constexpr explicit Expected(unexpect_t, Args&&... args) - : impl_base{unexpect_t{}, std::forward(args)...}, - ctor_base{detail::default_constructor_tag{}} {} - - template &, Args&&...>>* = - nullptr> - constexpr explicit Expected(unexpect_t, std::initializer_list il, Args&&... args) - : impl_base{unexpect_t{}, il, std::forward(args)...}, - ctor_base{detail::default_constructor_tag{}} {} - - template && - std::is_convertible_v)>* = nullptr, - detail::expected_enable_from_other* = nullptr> - constexpr explicit Expected(const Expected& rhs) - : ctor_base{detail::default_constructor_tag{}} { - if (rhs.has_value()) { - this->construct(*rhs); - } else { - this->construct_error(rhs.error()); - } - } - - template && - std::is_convertible_v)>* = nullptr, - detail::expected_enable_from_other* = nullptr> - constexpr Expected(const Expected& rhs) : ctor_base{detail::default_constructor_tag{}} { - if (rhs.has_value()) { - this->construct(*rhs); - } else { - this->construct_error(rhs.error()); - } - } - - template && std::is_convertible_v)>* = - nullptr, - detail::expected_enable_from_other* = nullptr> - constexpr explicit Expected(Expected&& rhs) - : ctor_base{detail::default_constructor_tag{}} { - if (rhs.has_value()) { - this->construct(std::move(*rhs)); - } else { - this->construct_error(std::move(rhs.error())); - } - } - - template && std::is_convertible_v)>* = - nullptr, - detail::expected_enable_from_other* = nullptr> - constexpr Expected(Expected&& rhs) : ctor_base{detail::default_constructor_tag{}} { - if (rhs.has_value()) { - this->construct(std::move(*rhs)); - } else { - this->construct_error(std::move(rhs.error())); - } - } - - template >* = nullptr, - detail::expected_enable_forward_value* = nullptr> - constexpr explicit Expected(U&& v) : Expected{std::in_place, std::forward(v)} {} - - template >* = nullptr, - detail::expected_enable_forward_value* = nullptr> - constexpr Expected(U&& v) : Expected{std::in_place, std::forward(v)} {} - - template >* = nullptr, - std::enable_if_t<( - !std::is_same_v, std::remove_cvref_t> && - !std::conjunction_v, std::is_same>> && - std::is_constructible_v && std::is_assignable_v && - std::is_nothrow_move_constructible_v)>* = nullptr> - Expected& operator=(U&& v) { - if (has_value()) { - val() = std::forward(v); - } else { - err().~Unexpected(); - new (valptr()) T{std::forward(v)}; - this->m_has_val = true; - } - - return *this; - } - - template >* = nullptr, - std::enable_if_t<( - !std::is_same_v, std::remove_cvref_t> && - !std::conjunction_v, std::is_same>> && - std::is_constructible_v && std::is_assignable_v && - std::is_nothrow_move_constructible_v)>* = nullptr> - Expected& operator=(U&& v) { - if (has_value()) { - val() = std::forward(v); - } else { - auto tmp = std::move(err()); - err().~Unexpected(); - new (valptr()) T{std::forward(v)}; - this->m_has_val = true; - } - - return *this; - } - - template && - std::is_assignable_v>* = nullptr> - Expected& operator=(const Unexpected& rhs) { - if (!has_value()) { - err() = rhs; - } else { - this->destroy_val(); - new (errptr()) Unexpected{rhs}; - this->m_has_val = false; - } - - return *this; - } - - template && - std::is_move_assignable_v>* = nullptr> - Expected& operator=(Unexpected&& rhs) noexcept { - if (!has_value()) { - err() = std::move(rhs); - } else { - this->destroy_val(); - new (errptr()) Unexpected{std::move(rhs)}; - this->m_has_val = false; - } - - return *this; - } - - template >* = nullptr> - void emplace(Args&&... args) { - if (has_value()) { - val() = T{std::forward(args)...}; - } else { - err().~Unexpected(); - new (valptr()) T{std::forward(args)...}; - this->m_has_val = true; - } - } - - template >* = nullptr> - void emplace(Args&&... args) { - if (has_value()) { - val() = T{std::forward(args)...}; - } else { - auto tmp = std::move(err()); - err().~Unexpected(); - new (valptr()) T{std::forward(args)...}; - this->m_has_val = true; - } - } - - template &, - Args&&...>>* = nullptr> - void emplace(std::initializer_list il, Args&&... args) { - if (has_value()) { - T t{il, std::forward(args)...}; - val() = std::move(t); - } else { - err().~Unexpected(); - new (valptr()) T{il, std::forward(args)...}; - this->m_has_val = true; - } - } - - template &, - Args&&...>>* = nullptr> - void emplace(std::initializer_list il, Args&&... args) { - if (has_value()) { - T t{il, std::forward(args)...}; - val() = std::move(t); - } else { - auto tmp = std::move(err()); - err().~Unexpected(); - new (valptr()) T{il, std::forward(args)...}; - this->m_has_val = true; - } - } - - constexpr T* operator->() { - return valptr(); - } - - constexpr const T* operator->() const { - return valptr(); - } - - template - constexpr U& operator*() & { - return val(); - } - - template - constexpr const U& operator*() const& { - return val(); - } - - template - constexpr U&& operator*() && { - return std::move(val()); - } - - template - constexpr const U&& operator*() const&& { - return std::move(val()); - } - - constexpr bool has_value() const noexcept { - return this->m_has_val; - } - - constexpr explicit operator bool() const noexcept { - return this->m_has_val; - } - - template - constexpr U& value() & { - return val(); - } - - template - constexpr const U& value() const& { - return val(); - } - - template - constexpr U&& value() && { - return std::move(val()); - } - - template - constexpr const U&& value() const&& { - return std::move(val()); - } - - constexpr E& error() & { - return err().value(); - } - - constexpr const E& error() const& { - return err().value(); - } - - constexpr E&& error() && { - return std::move(err().value()); - } - - constexpr const E&& error() const&& { - return std::move(err().value()); - } - - template - constexpr T value_or(U&& v) const& { - static_assert(std::is_copy_constructible_v && std::is_convertible_v, - "T must be copy-constructible and convertible from U&&"); - return bool(*this) ? **this : static_cast(std::forward(v)); - } - - template - constexpr T value_or(U&& v) && { - static_assert(std::is_move_constructible_v && std::is_convertible_v, - "T must be move-constructible and convertible from U&&"); - return bool(*this) ? std::move(**this) : static_cast(std::forward(v)); - } - -private: - static_assert(!std::is_reference_v, "T must not be a reference"); - static_assert(!std::is_same_v>, - "T must not be std::in_place_t"); - static_assert(!std::is_same_v>, "T must not be unexpect_t"); - static_assert(!std::is_same_v>>, - "T must not be Unexpected"); - static_assert(!std::is_reference_v, "E must not be a reference"); - - T* valptr() { - return std::addressof(this->m_val); - } - - const T* valptr() const { - return std::addressof(this->m_val); - } - - Unexpected* errptr() { - return std::addressof(this->m_unexpect); - } - - const Unexpected* errptr() const { - return std::addressof(this->m_unexpect); - } - - template - constexpr U& val() { - return this->m_val; - } - - template - constexpr const U& val() const { - return this->m_val; - } - - constexpr Unexpected& err() { - return this->m_unexpect; - } - - constexpr const Unexpected& err() const { - return this->m_unexpect; - } - - using impl_base = detail::expected_move_assign_base; - using ctor_base = detail::expected_default_ctor_base; -}; - -template -constexpr bool operator==(const Expected& lhs, const Expected& rhs) { - return (lhs.has_value() != rhs.has_value()) - ? false - : (!lhs.has_value() ? lhs.error() == rhs.error() : *lhs == *rhs); -} - -template -constexpr bool operator!=(const Expected& lhs, const Expected& rhs) { - return !operator==(lhs, rhs); -} - -template -constexpr bool operator==(const Expected& x, const U& v) { - return x.has_value() ? *x == v : false; -} - -template -constexpr bool operator==(const U& v, const Expected& x) { - return x.has_value() ? *x == v : false; -} - -template -constexpr bool operator!=(const Expected& x, const U& v) { - return !operator==(x, v); -} - -template -constexpr bool operator!=(const U& v, const Expected& x) { - return !operator==(v, x); -} - -template -constexpr bool operator==(const Expected& x, const Unexpected& e) { - return x.has_value() ? false : x.error() == e.value(); -} - -template -constexpr bool operator==(const Unexpected& e, const Expected& x) { - return x.has_value() ? false : x.error() == e.value(); -} - -template -constexpr bool operator!=(const Expected& x, const Unexpected& e) { - return !operator==(x, e); -} - -template -constexpr bool operator!=(const Unexpected& e, const Expected& x) { - return !operator==(e, x); -} - -} // namespace Common diff --git a/src/common/fs/file.cpp b/src/common/fs/file.cpp index 854090e7b2..461a5eee35 100644 --- a/src/common/fs/file.cpp +++ b/src/common/fs/file.cpp @@ -9,7 +9,7 @@ #include "common/assert.h" #include "common/fs/file.h" #include "common/fs/fs.h" -#ifdef ANDROID +#ifdef __ANDROID__ #include "common/fs/fs_android.h" #endif #include "common/logging.h" @@ -259,7 +259,7 @@ void IOFile::Open(const fs::path& path, FileAccessMode mode, FileType type, File } else { _wfopen_s(&file, path.c_str(), AccessModeToWStr(mode, type)); } -#elif ANDROID +#elif __ANDROID__ if (Android::IsContentUri(path)) { ASSERT_MSG(mode == FileAccessMode::Read, "Content URI file access is for read-only!"); const auto fd = Android::OpenContentUri(path, Android::OpenMode::Read); @@ -396,7 +396,7 @@ u64 IOFile::GetSize() const { // Flush any unwritten buffered data into the file prior to retrieving the file size. std::fflush(file); -#if ANDROID +#ifdef __ANDROID__ u64 file_size = 0; if (Android::IsContentUri(file_path)) { file_size = Android::GetSize(file_path); diff --git a/src/common/fs/fs.cpp b/src/common/fs/fs.cpp index 3858b0f0c3..cd2157490b 100644 --- a/src/common/fs/fs.cpp +++ b/src/common/fs/fs.cpp @@ -6,7 +6,7 @@ #include "common/fs/file.h" #include "common/fs/fs.h" -#ifdef ANDROID +#ifdef __ANDROID__ #include "common/fs/fs_android.h" #endif #include "common/fs/path_util.h" @@ -409,107 +409,62 @@ bool RenameDir(const fs::path& old_path, const fs::path& new_path) { return true; } -void IterateDirEntries(const std::filesystem::path& path, const DirEntryCallable& callback, - DirEntryFilter filter) { +void IterateDirEntries(const std::filesystem::path& path, const DirEntryCallable& callback, DirEntryFilter filter) { if (!ValidatePath(path)) { LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path)); return; } - if (!Exists(path)) { - LOG_ERROR(Common_Filesystem, "Filesystem object at path={} does not exist", - PathToUTF8String(path)); + LOG_ERROR(Common_Filesystem, "Filesystem object at path={} does not exist", PathToUTF8String(path)); return; } - if (!IsDir(path)) { - LOG_ERROR(Common_Filesystem, "Filesystem object at path={} is not a directory", - PathToUTF8String(path)); + LOG_ERROR(Common_Filesystem, "Filesystem object at path={} is not a directory", PathToUTF8String(path)); return; } - bool callback_error = false; - std::error_code ec; - - for (const auto& entry : fs::directory_iterator(path, ec)) { - if (ec) { - break; - } - - if (True(filter & DirEntryFilter::File) && - entry.status().type() == fs::file_type::regular) { + bool callback_error = false; + for (auto const& entry : fs::directory_iterator(path, ec)) { + if ((True(filter & DirEntryFilter::File) && entry.status().type() == fs::file_type::regular) + || (True(filter & DirEntryFilter::Directory) && entry.status().type() == fs::file_type::directory)) { if (!callback(entry)) { callback_error = true; - break; - } - } - - if (True(filter & DirEntryFilter::Directory) && - entry.status().type() == fs::file_type::directory) { - if (!callback(entry)) { - callback_error = true; - break; } } } if (callback_error || ec) { - LOG_ERROR(Common_Filesystem, - "Failed to visit all the directory entries of path={}, ec_message={}", - PathToUTF8String(path), ec.message()); - return; + LOG_ERROR(Common_Filesystem, "Failed to visit all the directory entries of path={}, ec_message={}, callback_error={}", PathToUTF8String(path), ec.message(), callback_error); + } else { + LOG_DEBUG(Common_Filesystem, "Visited all the directory entries of path={}", PathToUTF8String(path)); } - - LOG_DEBUG(Common_Filesystem, "Successfully visited all the directory entries of path={}", - PathToUTF8String(path)); } -void IterateDirEntriesRecursively(const std::filesystem::path& path, - const DirEntryCallable& callback, DirEntryFilter filter) { +void IterateDirEntriesRecursively(const std::filesystem::path& path, const DirEntryCallable& callback, DirEntryFilter filter) { if (!ValidatePath(path)) { LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path)); return; } - if (!Exists(path)) { - LOG_ERROR(Common_Filesystem, "Filesystem object at path={} does not exist", - PathToUTF8String(path)); + LOG_ERROR(Common_Filesystem, "Filesystem object at path={} does not exist", PathToUTF8String(path)); return; } - if (!IsDir(path)) { - LOG_ERROR(Common_Filesystem, "Filesystem object at path={} is not a directory", - PathToUTF8String(path)); + LOG_ERROR(Common_Filesystem, "Filesystem object at path={} is not a directory", PathToUTF8String(path)); return; } - bool callback_error = false; - - std::error_code ec; - // TODO (Morph): Replace this with recursive_directory_iterator once it's fixed in MSVC. + std::error_code ec; + bool callback_error = false; for (const auto& entry : fs::directory_iterator(path, ec)) { - if (ec) { - break; - } - - if (True(filter & DirEntryFilter::File) && - entry.status().type() == fs::file_type::regular) { + if ((True(filter & DirEntryFilter::File) && entry.status().type() == fs::file_type::regular) + || (True(filter & DirEntryFilter::Directory) && entry.status().type() == fs::file_type::directory)) { if (!callback(entry)) { callback_error = true; - break; } } - - if (True(filter & DirEntryFilter::Directory) && - entry.status().type() == fs::file_type::directory) { - if (!callback(entry)) { - callback_error = true; - break; - } - } - // TODO (Morph): Remove this when MSVC fixes recursive_directory_iterator. // recursive_directory_iterator throws an exception despite passing in a std::error_code. if (entry.status().type() == fs::file_type::directory) { @@ -518,21 +473,17 @@ void IterateDirEntriesRecursively(const std::filesystem::path& path, } if (callback_error || ec) { - LOG_ERROR(Common_Filesystem, - "Failed to visit all the directory entries of path={}, ec_message={}", - PathToUTF8String(path), ec.message()); - return; + LOG_ERROR(Common_Filesystem, "Failed to visit all the directory entries of path={}, ec_message={}, callback_error={}", PathToUTF8String(path), ec.message(), callback_error); + } else { + LOG_DEBUG(Common_Filesystem, "Visited all the directory entries of path={}", PathToUTF8String(path)); } - - LOG_DEBUG(Common_Filesystem, "Successfully visited all the directory entries of path={}", - PathToUTF8String(path)); } // Generic Filesystem Operations bool Exists(const fs::path& path) { std::error_code ec; -#ifdef ANDROID +#ifdef __ANDROID__ if (Android::IsContentUri(path)) { return Android::Exists(path); } else { @@ -545,7 +496,7 @@ bool Exists(const fs::path& path) { bool IsFile(const fs::path& path) { std::error_code ec; -#ifdef ANDROID +#ifdef __ANDROID__ if (Android::IsContentUri(path)) { return !Android::IsDirectory(path); } else { @@ -558,7 +509,7 @@ bool IsFile(const fs::path& path) { bool IsDir(const fs::path& path) { std::error_code ec; -#ifdef ANDROID +#ifdef __ANDROID__ if (Android::IsContentUri(path)) { return Android::IsDirectory(path); } else { @@ -611,7 +562,7 @@ fs::file_type GetEntryType(const fs::path& path) { } u64 GetSize(const fs::path& path) { -#ifdef ANDROID +#ifdef __ANDROID__ if (Android::IsContentUri(path)) { return Android::GetSize(path); } diff --git a/src/common/fs/path_util.cpp b/src/common/fs/path_util.cpp index 714e393293..3844cca06b 100644 --- a/src/common/fs/path_util.cpp +++ b/src/common/fs/path_util.cpp @@ -11,7 +11,7 @@ #include "common/assert.h" #include "common/fs/fs.h" -#ifdef ANDROID +#ifdef __ANDROID__ #include "common/fs/fs_android.h" #endif #include "common/fs/fs_paths.h" @@ -90,7 +90,7 @@ public: void CreateEdenPaths() { std::for_each(eden_paths.begin(), eden_paths.end(), [](auto &path) { - void(FS::CreateDir(path.second)); + void(FS::CreateDirs(path.second)); }); } @@ -126,7 +126,7 @@ public: LEGACY_PATH(Yuzu, YUZU) LEGACY_PATH(Suyu, SUYU) #undef LEGACY_PATH -#elif ANDROID +#elif __ANDROID__ ASSERT(!eden_path.empty()); eden_path_cache = eden_path / CACHE_DIR; eden_path_config = eden_path / CONFIG_DIR; @@ -149,10 +149,9 @@ public: LEGACY_PATH(Suyu, SUYU) #undef LEGACY_PATH #endif + // data GenerateEdenPath(EdenPath::EdenDir, eden_path); GenerateEdenPath(EdenPath::AmiiboDir, eden_path / AMIIBO_DIR); - GenerateEdenPath(EdenPath::CacheDir, eden_path_cache); - GenerateEdenPath(EdenPath::ConfigDir, eden_path_config); GenerateEdenPath(EdenPath::CrashDumpsDir, eden_path / CRASH_DUMPS_DIR); GenerateEdenPath(EdenPath::DumpDir, eden_path / DUMP_DIR); GenerateEdenPath(EdenPath::KeysDir, eden_path / KEYS_DIR); @@ -163,10 +162,13 @@ public: GenerateEdenPath(EdenPath::SaveDir, eden_path / NAND_DIR); GenerateEdenPath(EdenPath::ScreenshotsDir, eden_path / SCREENSHOTS_DIR); GenerateEdenPath(EdenPath::SDMCDir, eden_path / SDMC_DIR); - GenerateEdenPath(EdenPath::ShaderDir, eden_path / SHADER_DIR); GenerateEdenPath(EdenPath::TASDir, eden_path / TAS_DIR); GenerateEdenPath(EdenPath::IconsDir, eden_path / ICONS_DIR); - + // config + GenerateEdenPath(EdenPath::ConfigDir, eden_path_config); + // cache + GenerateEdenPath(EdenPath::CacheDir, eden_path_cache); + GenerateEdenPath(EdenPath::ShaderDir, eden_path_cache / SHADER_DIR); #ifdef _WIN32 GenerateLegacyPath(EmuPath::RyujinxDir, GetAppDataRoamingDirectory() / RYUJINX_DIR); #else @@ -447,11 +449,11 @@ std::vector SplitPathComponentsCopy(std::string_view filename) { std::string SanitizePath(std::string_view path_, DirectorySeparator directory_separator) { std::string path(path_); -#ifdef ANDROID +#ifdef __ANDROID__ if (Android::IsContentUri(path)) { return path; } -#endif // ANDROID +#endif // __ANDROID__ char type1 = directory_separator == DirectorySeparator::BackwardSlash ? '/' : '\\'; char type2 = directory_separator == DirectorySeparator::BackwardSlash ? '\\' : '/'; @@ -482,7 +484,7 @@ std::string GetParentPath(std::string_view path) { return std::string(path); } -#ifdef ANDROID +#ifdef __ANDROID__ if (path[0] != '/') { std::string path_string{path}; return FS::Android::GetParentDirectory(path_string); diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp index 04f3a65778..445d34c444 100644 --- a/src/common/host_memory.cpp +++ b/src/common/host_memory.cpp @@ -32,6 +32,8 @@ #include #elif defined(__FreeBSD__) #include +#elif defined(__OPENORBIS__) +#include #endif // FreeBSD @@ -122,54 +124,55 @@ static void GetFuncAddress(Common::DynamicLibrary& dll, const char* name, T& pfn class HostMemory::Impl { public: explicit Impl(size_t backing_size_, size_t virtual_size_) - : backing_size{backing_size_}, virtual_size{virtual_size_}, process{GetCurrentProcess()}, - kernelbase_dll("Kernelbase") { + : backing_size{backing_size_} + , virtual_size{virtual_size_} + , process{GetCurrentProcess()} + , kernelbase_dll("Kernelbase") + {} + + bool Init() { if (!kernelbase_dll.IsOpen()) { LOG_CRITICAL(HW_Memory, "Failed to load Kernelbase.dll"); - throw std::bad_alloc{}; + return false; } GetFuncAddress(kernelbase_dll, "CreateFileMapping2", pfn_CreateFileMapping2); GetFuncAddress(kernelbase_dll, "VirtualAlloc2", pfn_VirtualAlloc2); GetFuncAddress(kernelbase_dll, "MapViewOfFile3", pfn_MapViewOfFile3); GetFuncAddress(kernelbase_dll, "UnmapViewOfFile2", pfn_UnmapViewOfFile2); + if (!pfn_CreateFileMapping2 || !pfn_VirtualAlloc2 || !pfn_MapViewOfFile3 || !pfn_UnmapViewOfFile2) { + LOG_CRITICAL(HW_Memory, "Failed to find functions for virtual allocs"); + return false; + } + // Allocate backing file map - backing_handle = - pfn_CreateFileMapping2(INVALID_HANDLE_VALUE, nullptr, FILE_MAP_WRITE | FILE_MAP_READ, - PAGE_READWRITE, SEC_COMMIT, backing_size, nullptr, nullptr, 0); + backing_handle = pfn_CreateFileMapping2(INVALID_HANDLE_VALUE, nullptr, FILE_MAP_WRITE | FILE_MAP_READ, PAGE_READWRITE, SEC_COMMIT, backing_size, nullptr, nullptr, 0); if (!backing_handle) { - LOG_CRITICAL(HW_Memory, "Failed to allocate {} MiB of backing memory", - backing_size >> 20); - throw std::bad_alloc{}; + LOG_CRITICAL(HW_Memory, "Failed to allocate {} MiB of backing memory", backing_size >> 20); + return false; } // Allocate a virtual memory for the backing file map as placeholder - backing_base = static_cast(pfn_VirtualAlloc2(process, nullptr, backing_size, - MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, - PAGE_NOACCESS, nullptr, 0)); + backing_base = static_cast(pfn_VirtualAlloc2(process, nullptr, backing_size, MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, PAGE_NOACCESS, nullptr, 0)); if (!backing_base) { Release(); - LOG_CRITICAL(HW_Memory, "Failed to reserve {} MiB of virtual memory", - backing_size >> 20); - throw std::bad_alloc{}; + LOG_CRITICAL(HW_Memory, "Failed to reserve {} MiB of virtual memory", backing_size >> 20); + return false; } // Map backing placeholder - void* const ret = pfn_MapViewOfFile3(backing_handle, process, backing_base, 0, backing_size, - MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0); + void* const ret = pfn_MapViewOfFile3(backing_handle, process, backing_base, 0, backing_size, MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0); if (ret != backing_base) { Release(); LOG_CRITICAL(HW_Memory, "Failed to map {} MiB of virtual memory", backing_size >> 20); - throw std::bad_alloc{}; + return false; } // Allocate virtual address placeholder - virtual_base = static_cast(pfn_VirtualAlloc2(process, nullptr, virtual_size, - MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, - PAGE_NOACCESS, nullptr, 0)); + virtual_base = static_cast(pfn_VirtualAlloc2(process, nullptr, virtual_size, MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, PAGE_NOACCESS, nullptr, 0)); if (!virtual_base) { Release(); - LOG_CRITICAL(HW_Memory, "Failed to reserve {} GiB of virtual memory", - virtual_size >> 30); - throw std::bad_alloc{}; + LOG_CRITICAL(HW_Memory, "Failed to reserve {} GiB of virtual memory", virtual_size >> 30); + return false; } + return true; } ~Impl() { @@ -391,6 +394,9 @@ private: ankerl::unordered_dense::map placeholder_host_pointers; ///< Placeholder backing offset }; +#elif defined(__OPENORBIS__) || defined(__managarm__) +// None of the luxuries of POSIX, all of the suffering +// For managarm: see https://github.com/managarm/managarm/issues/1370 #else // ^^^ Windows ^^^ vvv POSIX vvv #ifdef ARCHITECTURE_arm64 @@ -496,10 +502,13 @@ static int shm_open_anon(int flags, mode_t mode) { class HostMemory::Impl { public: explicit Impl(size_t backing_size_, size_t virtual_size_) - : backing_size{backing_size_}, virtual_size{virtual_size_} { + : backing_size{backing_size_} + , virtual_size{virtual_size_} + {} + + bool Init() { long page_size = sysconf(_SC_PAGESIZE); - ASSERT_MSG(page_size == 0x1000, "page size {:#x} is incompatible with 4K paging", - page_size); + ASSERT_MSG(page_size == 0x1000, "page size {:#x} is incompatible with 4K paging", page_size); // Backing memory initialization #if defined(__sun__) || defined(__HAIKU__) || defined(__NetBSD__) || defined(__DragonFly__) fd = shm_open_anon(O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW, 0600); @@ -507,7 +516,7 @@ public: fd = shm_open_anon(O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW, 0600); #elif defined(__FreeBSD__) fd = shm_open(SHM_ANON, O_RDWR, 0600); -#elif defined(__APPLE__) +#elif defined(__APPLE__) || defined(__managarm__) // macOS doesn't have memfd_create, use anonymous temporary file char template_path[] = "/tmp/eden_mem_XXXXXX"; fd = mkstemp(template_path); @@ -540,15 +549,22 @@ public: } else { backing_base = static_cast(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)); } - ASSERT_MSG(backing_base != MAP_FAILED, "mmap failed: {}", strerror(errno)); + if (backing_base == MAP_FAILED) { + LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno)); + return false; + } // Virtual memory initialization virtual_base = virtual_map_base = static_cast(ChooseVirtualBase(virtual_size)); - ASSERT_MSG(virtual_base != MAP_FAILED, "mmap failed: {}", strerror(errno)); + if (virtual_base == MAP_FAILED) { + LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno)); + return false; + } #if defined(__linux__) madvise(virtual_base, virtual_size, MADV_HUGEPAGE); #endif free_manager.SetAddressSpace(virtual_base, virtual_size); + return true; } ~Impl() { @@ -669,17 +685,35 @@ private: #endif // ^^^ POSIX ^^^ -HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_) : backing_size(backing_size_), virtual_size(virtual_size_) { +HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_) + : backing_size(backing_size_) + , virtual_size(virtual_size_) +{ +#if defined(__OPENORBIS__) || defined(__managarm__) + LOG_WARNING(HW_Memory, "Platform doesn't support fastmem"); + fallback_buffer.emplace(backing_size); + backing_base = fallback_buffer->data(); + virtual_base = nullptr; +#else // Try to allocate a fastmem arena. // The implementation will fail with std::bad_alloc on errors. impl = std::make_unique(AlignUp(backing_size, PageAlignment), AlignUp(virtual_size, PageAlignment) + HugePageSize); - backing_base = impl->backing_base; - virtual_base = impl->virtual_base; - if (virtual_base) { - // Ensure the virtual base is aligned to the L2 block size. - virtual_base = reinterpret_cast(Common::AlignUp(uintptr_t(virtual_base), HugePageSize)); - virtual_base_offset = virtual_base - impl->virtual_base; + if (impl->Init()) { + backing_base = impl->backing_base; + virtual_base = impl->virtual_base; + if (virtual_base) { + // Ensure the virtual base is aligned to the L2 block size. + virtual_base = reinterpret_cast(Common::AlignUp(uintptr_t(virtual_base), HugePageSize)); + virtual_base_offset = virtual_base - impl->virtual_base; + } + } else { + impl.reset(); + LOG_WARNING(HW_Memory, "Platform can support fastmem, but can't create it"); + fallback_buffer.emplace(backing_size); + backing_base = fallback_buffer->data(); + virtual_base = nullptr; } +#endif } HostMemory::~HostMemory() = default; @@ -688,8 +722,8 @@ HostMemory::HostMemory(HostMemory&&) noexcept = default; HostMemory& HostMemory::operator=(HostMemory&&) noexcept = default; -void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length, - MemoryPermission perms, bool separate_heap) { +void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms, bool separate_heap) { +#if !(defined(__OPENORBIS__) || defined(__managarm__)) ASSERT(virtual_offset % PageAlignment == 0); ASSERT(host_offset % PageAlignment == 0); ASSERT(length % PageAlignment == 0); @@ -699,9 +733,11 @@ void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length, return; } impl->Map(virtual_offset + virtual_base_offset, host_offset, length, perms); +#endif } void HostMemory::Unmap(size_t virtual_offset, size_t length, bool separate_heap) { +#if !(defined(__OPENORBIS__) || defined(__managarm__)) ASSERT(virtual_offset % PageAlignment == 0); ASSERT(length % PageAlignment == 0); ASSERT(virtual_offset + length <= virtual_size); @@ -709,9 +745,11 @@ void HostMemory::Unmap(size_t virtual_offset, size_t length, bool separate_heap) return; } impl->Unmap(virtual_offset + virtual_base_offset, length); +#endif } void HostMemory::Protect(size_t virtual_offset, size_t length, MemoryPermission perm) { +#if !(defined(__OPENORBIS__) || defined(__managarm__)) ASSERT(virtual_offset % PageAlignment == 0); ASSERT(length % PageAlignment == 0); ASSERT(virtual_offset + length <= virtual_size); @@ -722,6 +760,7 @@ void HostMemory::Protect(size_t virtual_offset, size_t length, MemoryPermission const bool write = True(perm & MemoryPermission::Write); const bool execute = True(perm & MemoryPermission::Execute); impl->Protect(virtual_offset + virtual_base_offset, length, read, write, execute); +#endif } void HostMemory::ClearBackingRegion(size_t physical_offset, size_t length, u32 fill_value) { @@ -729,10 +768,12 @@ void HostMemory::ClearBackingRegion(size_t physical_offset, size_t length, u32 f } void HostMemory::EnableDirectMappedAddress() { +#if !(defined(__OPENORBIS__) || defined(__managarm__)) if (impl) { impl->EnableDirectMappedAddress(); virtual_size += reinterpret_cast(virtual_base); } +#endif } } // namespace Common diff --git a/src/common/host_memory.h b/src/common/host_memory.h index 8dd30aa9de..24f4670732 100644 --- a/src/common/host_memory.h +++ b/src/common/host_memory.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project @@ -7,6 +7,7 @@ #pragma once #include +#include #include "common/common_funcs.h" #include "common/common_types.h" #include "common/virtual_buffer.h" @@ -76,12 +77,16 @@ private: size_t backing_size{}; size_t virtual_size{}; +#if !(defined(__OPENORBIS__) || defined(__managarm__)) // Low level handler for the platform dependent memory routines class Impl; std::unique_ptr impl; +#endif u8* backing_base{}; u8* virtual_base{}; size_t virtual_base_offset{}; + // Windows requires it for kernels whom lack proper support for some functions! + std::optional> fallback_buffer; }; } // namespace Common diff --git a/src/common/logging.cpp b/src/common/logging.cpp index 68e033b129..ccf509f451 100644 --- a/src/common/logging.cpp +++ b/src/common/logging.cpp @@ -320,7 +320,7 @@ struct DebuggerBackend final : public Backend { void Flush() noexcept override {} }; #endif -#ifdef ANDROID +#ifdef __ANDROID__ /// @brief Backend that writes to the Android logcat struct LogcatBackend : public Backend { explicit LogcatBackend() noexcept = default; @@ -359,7 +359,7 @@ struct Impl { #ifdef _WIN32 lambda(static_cast(debugger_backend)); #endif -#ifdef ANDROID +#ifdef __ANDROID__ lambda(static_cast(lc_backend)); #endif } @@ -372,7 +372,7 @@ struct Impl { #ifdef _WIN32 DebuggerBackend debugger_backend{}; #endif -#ifdef ANDROID +#ifdef __ANDROID__ LogcatBackend lc_backend{}; #endif std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()}; diff --git a/src/common/logging.h b/src/common/logging.h index eabc683f63..2d5a2cfa93 100644 --- a/src/common/logging.h +++ b/src/common/logging.h @@ -89,7 +89,7 @@ void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename, u template void FmtLogMessage(Class log_class, Level log_level, const char* filename, unsigned int line_num, const char* function, fmt::format_string format, const Args&... args) { - FmtLogMessageImpl(log_class, log_level, filename, line_num, function, format, fmt::make_format_args(args...)); + FmtLogMessageImpl(log_class, log_level, filename, line_num, function, format.get(), fmt::make_format_args(args...)); } /// Implements a log message filter which allows different log classes to have different minimum diff --git a/src/common/lru_cache.h b/src/common/lru_cache.h index 36cea5d27e..952ba0eb8b 100644 --- a/src/common/lru_cache.h +++ b/src/common/lru_cache.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -60,7 +63,7 @@ public: template void ForEachItemBelow(TickType tick, Func&& func) { static constexpr bool RETURNS_BOOL = - std::is_same_v, bool>; + std::is_same_v, bool>; Item* iterator = first_item; while (iterator) { if (static_cast(tick) - static_cast(iterator->tick) < 0) { diff --git a/src/common/net/net.cpp b/src/common/net/net.cpp index 1d836cbd1d..a4f681cd33 100644 --- a/src/common/net/net.cpp +++ b/src/common/net/net.cpp @@ -62,16 +62,21 @@ std::vector Release::GetPlatformAssets() const { #ifdef _WIN32 #ifdef ARCHITECTURE_x86_64 - find_asset("Standard", {"amd64-msvc-standard.exe", "amd64-msvc-standard.zip", "mingw-amd64-gcc-standard.exe", "mingw-amd64-gcc-standard.zip"}); - find_asset("PGO", {"mingw-amd64-clang-pgo.exe", "mingw-amd64-clang-pgo.zip"}); +#ifdef _MSC_VER + find_asset("Standard", {"amd64-msvc-standard.exe", "amd64-msvc-standard.zip"}); +#else // _MSC_VER + find_asset("Standard", {BUILD_ID "-gcc-standard.exe", BUILD_ID "-gcc-standard.zip"}); + find_asset("PGO", {BUILD_ID "-clang-pgo.exe", BUILD_ID "-clang-pgo.zip"}); +#endif // _MSC_VER #elif defined(ARCHITECTURE_arm64) - find_asset("Standard", {"mingw-arm64-clang-standard.exe", "mingw-arm64-clang-standard.zip"}); - find_asset("PGO", {"mingw-arm64-clang-pgo.exe", "mingw-arm64-clang-pgo.zip"}); -#endif + find_asset("Standard", {"arm64-clang-standard.exe", "arm64-clang-standard.zip"}); + find_asset("PGO", {"arm64-clang-pgo.exe", "arm64-clang-pgo.zip"}); +#endif // ARCHITECTURE_arm64 #elif defined(__APPLE__) #ifdef ARCHITECTURE_arm64 - find_asset("Standard", {".dmg", ".tar.gz"}); -#endif + find_asset("Standard", {"standard.dmg", "standard.tar.gz", ".dmg", ".tar.gz"}); + find_asset("PGO", {"pgo.dmg", "pgo.tar.gz"}); +#endif // ARCHITECTURE_arm64 #elif defined(__ANDROID__) #ifdef ARCHITECTURE_x86_64 find_asset("Standard", {"chromeos.apk"}); @@ -82,9 +87,9 @@ std::vector Release::GetPlatformAssets() const { find_asset("Standard", {"optimized.apk"}); #else find_asset("Standard", {"standard.apk"}); -#endif -#endif -#endif +#endif // GENSHIN_SPOOF +#endif // ARCHITECTURE_arm64 +#endif // __APPLE__ return found_assets; } diff --git a/src/common/settings.cpp b/src/common/settings.cpp index 54848c4dd1..9b582f1c2a 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp @@ -54,7 +54,6 @@ SWITCHABLE(CpuBackend, true); SWITCHABLE(CpuAccuracy, true); SWITCHABLE(FullscreenMode, true); SWITCHABLE(GpuAccuracy, true); -SWITCHABLE(GpuLogLevel, true); SWITCHABLE(Language, true); SWITCHABLE(MemoryLayout, true); SWITCHABLE(NvdecEmulation, false); @@ -213,6 +212,16 @@ bool IsNceEnabled() { return is_nce_enabled; } +static u64 current_program_id = 0; + +void SetCurrentProgramID(u64 program_id) { + current_program_id = program_id; +} + +u64 GetCurrentProgramID() { + return current_program_id; +} + bool IsDockedMode() { return values.use_docked_mode.GetValue() == Settings::ConsoleMode::Docked; } @@ -435,4 +444,16 @@ void ToggleSlowMode() { SetSpeedMode(SpeedMode::Standard); } +bool IsOpenGL() { + const auto backend = Settings::values.renderer_backend.GetValue(); + switch (backend) { + case RendererBackend::OpenGL_GLSL: + case RendererBackend::OpenGL_GLASM: + case RendererBackend::OpenGL_SPIRV: + return true; + default: + return false; + } +} + } // namespace Settings diff --git a/src/common/settings.h b/src/common/settings.h index e10f5105a1..b80d6a5ec7 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -336,7 +336,7 @@ struct Values { RendererBackend::Vulkan, #endif "backend", Category::Renderer}; - SwitchableSetting vulkan_device{linkage, 0, "vulkan_device", Category::Renderer, Specialization::RuntimeList}; + SwitchableSetting vulkan_device{linkage, 0, "vulkan_device", Category::Renderer, Specialization::RuntimeList}; // Graphics Settings ResolutionScalingInfo resolution_info{}; @@ -359,7 +359,7 @@ struct Values { true, true}; SwitchableSetting fsr_sharpening_slider{linkage, -#ifdef ANDROID +#ifdef __ANDROID__ 0, #else 25, @@ -417,7 +417,7 @@ struct Values { linkage, 0, "bg_blue", Category::Renderer, Specialization::Default, true, true}; SwitchableSetting gpu_accuracy{linkage, -#ifdef ANDROID +#ifdef __ANDROID__ GpuAccuracy::Low, #else GpuAccuracy::Medium, @@ -447,7 +447,7 @@ struct Values { "nvdec_emulation", Category::RendererAdvanced}; SwitchableSetting max_anisotropy{linkage, -#ifdef ANDROID +#ifdef __ANDROID__ AnisotropyMode::Default, #else AnisotropyMode::Automatic, @@ -500,7 +500,7 @@ struct Values { Category::RendererAdvanced}; SwitchableSetting use_reactive_flushing{linkage, -#ifdef ANDROID +#ifdef __ANDROID__ false, #else true, @@ -519,7 +519,7 @@ struct Values { true, true}; -#ifdef ANDROID +#ifdef __ANDROID__ SwitchableSetting use_optimized_vertex_buffers{linkage, false, "use_optimized_vertex_buffers", @@ -545,8 +545,15 @@ struct Values { Specialization::Default, true, true}; + SwitchableSetting antiflicker{linkage, + false, + "antiflicker", + Category::RendererHacks, + Specialization::Default, + true, + true}; SwitchableSetting async_presentation{linkage, -#ifdef ANDROID +#ifdef __ANDROID__ false, #else false, @@ -566,6 +573,13 @@ struct Values { false, #endif "rescale_hack", Category::RendererHacks}; + SwitchableSetting enable_gpu_buffer_readback{linkage, + false, + "enable_gpu_buffer_readback", + Category::RendererAdvanced, + Specialization::Default, + true, + true}; SwitchableSetting use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders", Category::RendererHacks}; @@ -592,7 +606,7 @@ struct Values { Category::RendererHacks}; SwitchableSetting dyna_state{linkage, -#if defined(ANDROID) +#if defined(__ANDROID__) ExtendedDynamicState::Disabled, #elif defined(__APPLE__) ExtendedDynamicState::Disabled, @@ -611,7 +625,7 @@ struct Values { Specialization::Scalar}; SwitchableSetting vertex_input_dynamic_state{linkage, -#if defined (ANDROID) +#ifdef __ANDROID__ false, #else true, @@ -627,7 +641,7 @@ struct Values { linkage, false, "disable_shader_loop_safety_checks", Category::RendererDebug}; Setting enable_renderdoc_hotkey{linkage, false, "renderdoc_hotkey", Category::RendererDebug}; -#if defined(ANDROID) && defined(ARCHITECTURE_arm64) +#if defined(__ANDROID__) && defined(ARCHITECTURE_arm64) // Debug override for automatic BCn patching detection Setting patch_old_qcom_drivers{linkage, false, "patch_old_qcom_drivers", Category::RendererDebug}; @@ -654,8 +668,8 @@ struct Values { false, true, &custom_rtc_enabled}; SwitchableSetting custom_rtc_offset{linkage, 0, - (std::numeric_limits::min)(), - (std::numeric_limits::max)(), + (std::numeric_limits::min)(), + (std::numeric_limits::max)(), "custom_rtc_offset", Category::System, Specialization::Countable, @@ -672,7 +686,7 @@ struct Values { Setting current_user{linkage, 0, "current_user", Category::System}; SwitchableSetting use_docked_mode{linkage, -#ifdef ANDROID +#ifdef __ANDROID__ ConsoleMode::Handheld, #else ConsoleMode::Docked, @@ -744,7 +758,7 @@ struct Values { Setting touch_device{linkage, "min_x:100,min_y:50,max_x:1800,max_y:850", "touch_device", Category::Controls}; - Setting touch_from_button_map_index{linkage, 0, "touch_from_button_map", + Setting touch_from_button_map_index{linkage, 0, "touch_from_button_map", Category::Controls}; std::vector touch_from_button_maps; @@ -772,11 +786,17 @@ struct Values { bool record_frame_times; Setting use_gdbstub{linkage, false, "use_gdbstub", Category::Debugging}; Setting gdbstub_port{linkage, 6543, "gdbstub_port", Category::Debugging}; - Setting program_args{linkage, std::string(), "program_args", Category::Debugging}; + SwitchableSetting program_args{linkage, + std::string(), + "program_args", + Category::System, + Specialization::Default, + true, // save_ — persist in config file + false}; // runtime_modifiable_ — startup-only Setting dump_exefs{linkage, false, "dump_exefs", Category::Debugging}; Setting dump_nso{linkage, false, "dump_nso", Category::Debugging}; - Setting dump_shaders{ - linkage, false, "dump_shaders", Category::DebuggingGraphics, Specialization::Default, + Setting dump_guest_shaders{ + linkage, false, "dump_guest_shaders", Category::DebuggingGraphics, Specialization::Default, false}; Setting dump_macros{ linkage, false, "dump_macros", Category::DebuggingGraphics, Specialization::Default, false}; @@ -800,9 +820,8 @@ struct Values { Setting disable_web_applet{linkage, true, "disable_web_applet", Category::Debugging}; // GPU Logging - Setting gpu_logging_enabled{linkage, false, "gpu_logging_enabled", Category::Debugging}; - SwitchableSetting gpu_log_level{linkage, GpuLogLevel::Standard, "gpu_log_level", - Category::Debugging}; + Setting gpu_log_level{linkage, GpuLogLevel::Off, "gpu_log_level", + Category::Debugging}; Setting gpu_log_vulkan_calls{linkage, true, "gpu_log_vulkan_calls", Category::Debugging}; Setting gpu_log_shader_dumps{linkage, false, "gpu_log_shader_dumps", Category::Debugging}; Setting gpu_log_memory_tracking{linkage, true, "gpu_log_memory_tracking", @@ -863,6 +882,11 @@ bool IsFastmemEnabled(); void SetNceEnabled(bool is_64bit); bool IsNceEnabled(); +void SetCurrentProgramID(u64 program_id); +u64 GetCurrentProgramID(); + +bool IsOpenGL(); + bool IsDockedMode(); float Volume(); diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h index 638be4127f..b14ed9bc67 100644 --- a/src/common/settings_enums.h +++ b/src/common/settings_enums.h @@ -92,11 +92,13 @@ struct EnumMetadata { // AudioEngine must be specified discretely due to having existing but slightly different // canonicalizations // TODO (lat9nq): Remove explicit definition of AudioEngine/sink_id -enum class AudioEngine : u32 { Auto, Cubeb, Sdl2, Null, Oboe, }; +enum class AudioEngine : u32 { Auto, Cubeb, Sdl3, Null, Oboe, }; template<> inline std::vector> EnumMetadata::Canonicalizations() { return { - {"auto", AudioEngine::Auto}, {"cubeb", AudioEngine::Cubeb}, {"sdl2", AudioEngine::Sdl2}, + {"auto", AudioEngine::Auto}, + {"cubeb", AudioEngine::Cubeb}, + {"sdl3", AudioEngine::Sdl3}, {"null", AudioEngine::Null}, {"oboe", AudioEngine::Oboe}, }; } @@ -143,8 +145,8 @@ ENUM(ConfirmStop, Ask_Always, Ask_Based_On_Game, Ask_Never); ENUM(FullscreenMode, Borderless, Exclusive); ENUM(NvdecEmulation, Off, Cpu, Gpu); ENUM(ResolutionSetup, Res1_4X, Res1_2X, Res3_4X, Res1X, Res5_4X, Res3_2X, Res2X, Res3X, Res4X, Res5X, Res6X, Res7X, Res8X); -ENUM(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, Gaussian, Lanczos, ScaleForce, Fsr, Area, ZeroTangent, BSpline, Mitchell, Spline1, Mmpx, MaxEnum); -ENUM(AntiAliasing, None, Fxaa, Smaa, MaxEnum); +ENUM(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, Gaussian, Lanczos, ScaleForce, Fsr, Area, ZeroTangent, BSpline, Mitchell, Spline1, Mmpx, Sgsr, SgsrEdge); +ENUM(AntiAliasing, None, Fxaa, Smaa); ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, Stretch); ENUM(ConsoleMode, Handheld, Docked); ENUM(AppletMode, HLE, LLE); @@ -156,7 +158,7 @@ ENUM(GpuUnswizzleChunk, VeryLow, Low, Normal, Medium, High) ENUM(TemperatureUnits, Celsius, Fahrenheit) ENUM(ExtendedDynamicState, Disabled, EDS1, EDS2, EDS3); ENUM(GpuLogLevel, Off, Errors, Standard, Verbose, All) -ENUM(GameListMode, TreeView, GridView); +ENUM(GameListMode, TreeView, GridView, CarouselView); ENUM(SpeedMode, Standard, Turbo, Slow); template diff --git a/src/common/settings_setting.h b/src/common/settings_setting.h index 076aceef29..0a9243cc0d 100644 --- a/src/common/settings_setting.h +++ b/src/common/settings_setting.h @@ -6,14 +6,13 @@ #pragma once +#include #include -#include #include #include #include -#include -#include -#include +#include +#include #include "common/common_types.h" #include "common/settings_common.h" #include "common/settings_enums.h" @@ -101,7 +100,15 @@ public: * @param val The desired value */ virtual void SetValue(const Type& val) { - Type temp{ranged ? std::clamp(val, minimum, maximum) : val}; + // Enums have a maximal range which they're allowed + Type temp{}; + if constexpr (std::is_enum_v) { + auto const r_min = std::underlying_type_t(0); + auto const r_max = std::underlying_type_t(EnumMetadata::GetLast()); + temp = Type(std::clamp(std::underlying_type_t(val), r_min, r_max)); + } else { + temp = ranged ? std::clamp(val, this->minimum, this->maximum) : val; + } std::swap(value, temp); } @@ -129,7 +136,7 @@ protected: } else if constexpr (std::is_floating_point_v) { return fmt::format("{:f}", value_); } else if constexpr (std::is_enum_v) { - return std::to_string(u32(value_)); + return std::to_string(std::underlying_type_t(value_)); } else { return std::to_string(value_); } @@ -371,7 +378,15 @@ public: * @param val The new value */ void SetValue(const Type& val) override final { - Type temp{ranged ? std::clamp(val, this->minimum, this->maximum) : val}; + // Enums have a maximal range which they're allowed + Type temp{}; + if constexpr (std::is_enum_v) { + auto const r_min = std::underlying_type_t(0); + auto const r_max = std::underlying_type_t(EnumMetadata::GetLast()); + temp = Type(std::clamp(std::underlying_type_t(val), r_min, r_max)); + } else { + temp = ranged ? std::clamp(val, this->minimum, this->maximum) : val; + } if (use_global) { std::swap(this->value, temp); } else { diff --git a/src/common/slot_vector.h b/src/common/slot_vector.h index e464d3d948..f6da7a59d1 100644 --- a/src/common/slot_vector.h +++ b/src/common/slot_vector.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project @@ -20,10 +20,14 @@ namespace Common { struct SlotId { + static constexpr u32 TAGGED_MASK = 0x7fffffff; + static constexpr u32 TAGGED_VALUE = 0x80000000; static constexpr u32 INVALID_INDEX = (std::numeric_limits::max)(); + constexpr u32 Value() const noexcept { + return index & (~TAGGED_VALUE); + } constexpr auto operator<=>(const SlotId&) const noexcept = default; - constexpr explicit operator bool() const noexcept { return index != INVALID_INDEX; } @@ -47,12 +51,12 @@ public: Iterator& operator++() noexcept { const u64* const bitset = slot_vector->stored_bitset.data(); const u32 size = static_cast(slot_vector->stored_bitset.size()) * 64; - if (id.index < size) { + if (id.Value() < size) { do { ++id.index; - } while (id.index < size && !IsValid(bitset)); - if (id.index == size) { - id.index = SlotId::INVALID_INDEX; + } while (id.Value() < size && !IsValid(bitset)); + if (id.Value() == size) { + id = SlotId{}; } } return *this; @@ -85,7 +89,7 @@ public: : slot_vector{slot_vector_}, id{id_} {} bool IsValid(const u64* bitset) const noexcept { - return ((bitset[id.index / 64] >> (id.index % 64)) & 1) != 0; + return ((bitset[id.Value() / 64] >> (id.Value() % 64)) & 1) != 0; } SlotVector* slot_vector; @@ -107,12 +111,12 @@ public: [[nodiscard]] T& operator[](SlotId id) noexcept { ValidateIndex(id); - return values[id.index].object; + return values[id.Value()].object; } [[nodiscard]] const T& operator[](SlotId id) const noexcept { ValidateIndex(id); - return values[id.index].object; + return values[id.Value()].object; } template @@ -125,9 +129,9 @@ public: } void erase(SlotId id) noexcept { - values[id.index].object.~T(); - free_list.push_back(id.index); - ResetStorageBit(id.index); + values[id.Value()].object.~T(); + free_list.push_back(id.Value()); + ResetStorageBit(id.Value()); } [[nodiscard]] Iterator begin() noexcept { @@ -141,7 +145,7 @@ public: } [[nodiscard]] Iterator end() noexcept { - return Iterator(this, SlotId{SlotId::INVALID_INDEX}); + return Iterator(this, SlotId{}); } [[nodiscard]] size_t size() const noexcept { @@ -175,8 +179,8 @@ private: void ValidateIndex(SlotId id) const noexcept { DEBUG_ASSERT(id); - DEBUG_ASSERT(id.index / 64 < stored_bitset.size()); - DEBUG_ASSERT(((stored_bitset[id.index / 64] >> (id.index % 64)) & 1) != 0); + DEBUG_ASSERT(id.Value() / 64 < stored_bitset.size()); + DEBUG_ASSERT(((stored_bitset[id.Value() / 64] >> (id.Value() % 64)) & 1) != 0); } [[nodiscard]] u32 FreeValueIndex() noexcept { @@ -208,9 +212,7 @@ private: const size_t old_free_size = free_list.size(); free_list.resize(old_free_size + (new_capacity - values_capacity)); - std::iota(free_list.begin() + old_free_size, free_list.end(), - static_cast(values_capacity)); - + std::iota(free_list.begin() + old_free_size, free_list.end(), u32(values_capacity)); delete[] values; values = new_values; values_capacity = new_capacity; diff --git a/src/common/socket_types.h b/src/common/socket_types.h index 63824a5c48..5b1e4d7b1d 100644 --- a/src/common/socket_types.h +++ b/src/common/socket_types.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -28,9 +31,118 @@ enum class Type { /// Protocol values for sockets enum class Protocol : u8 { Unspecified, ///< Represents 0, usable in various places + IP, ICMP, TCP, UDP, + IPV6, + RAW, + IGMP, + GGP, + IPV4, + ST, + EGP, + PIGP, + RCCMON, + NVPII, + PUP, + ARGUS, + EMCON, + XNET, + CHAOS, + MUX, + MEAS, + HMP, + PRM, + IDP, + TRUNK1, + TRUNK2, + LEAF1, + LEAF2, + RDP, + IRTP, + TP, + BLT, + NSP, + INP, + DCCP, + //TODO: 3PC, + IDPR, + XTP, + DDP, + CMTP, + TPXX, + IL, + SDRP, + ROUTING, + FRAGMENT, + IDRP, + RSVP, + GRE, + MHRP, + BHA, + ESP, + AH, + INLSP, + SWIPE, + NHRP, + MOBILE, + TLSP, + SKIP, + ICMPV6, + NONE, + DSTOPTS, + AHIP, + CFTP, + HELLO, + SATEXPAK, + KRYPTOLAN, + RVD, + IPPC, + ADFS, + SATMON, + VISA, + IPCV, + CPNX, + CPHB, + WSN, + PVP, + BRSATMON, + ND, + WBMON, + WBEXPAK, + EON, + VMTP, + SVMTP, + VINES, + TTP, + IGP, + DGP, + TCF, + IGRP, + OSPFIGP, + SRPC, + LARP, + MTP, + AX25, + IPEIP, + MICP, + SCCSP, + ETHERIP, + ENCAP, + APES, + GMTP, + IPCOMP, + SCTP, + MH, + UDPLITE, + HIP, + SHIM6, + PIM, + CARP, + PGM, + MPLS, + PFSYNC }; /// Shutdown mode diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp index 7bcbe737b6..9543a62343 100644 --- a/src/common/string_util.cpp +++ b/src/common/string_util.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: 2013 Dolphin Emulator Project @@ -18,7 +18,7 @@ #include #endif -#ifdef ANDROID +#ifdef __ANDROID__ #include #endif @@ -45,7 +45,7 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _ if (full_path.empty()) return false; -#ifdef ANDROID +#ifdef __ANDROID__ if (full_path[0] != '/') { *_pPath = Common::FS::Android::GetParentDirectory(full_path); *_pFilename = Common::FS::Android::GetFilename(full_path); diff --git a/src/common/swap.h b/src/common/swap.h index 1cf8a6ba39..c7646ea24d 100644 --- a/src/common/swap.h +++ b/src/common/swap.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: 2012 PPSSPP Project @@ -10,12 +10,10 @@ #pragma once -#if defined(_MSC_VER) #include -#endif -#include #include #include +#include #include "common/common_types.h" namespace Common { diff --git a/src/common/thread.cpp b/src/common/thread.cpp index f4bdb3f7c0..c947c88373 100644 --- a/src/common/thread.cpp +++ b/src/common/thread.cpp @@ -4,6 +4,8 @@ // SPDX-FileCopyrightText: 2014 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include +#include #include #include @@ -18,24 +20,35 @@ #elif defined(_WIN32) #include #include "common/string_util.h" +#include "common/windows/timer_resolution.h" #else #if defined(__FreeBSD__) #include #include #include +// Compatibility with CPUset +#define cpu_set_t cpuset_t #elif defined(__DragonFly__) || defined(__OpenBSD__) || defined(__Bitrig__) #include #endif #include #include #endif + #ifndef _WIN32 #include #endif -#ifdef __FreeBSD__ -# define cpu_set_t cpuset_t +#include "common/cpu_features.h" +#ifdef ARCHITECTURE_x86_64 +#ifdef _MSC_VER +#include +#else +#include #endif +#include "common/x64/rdtsc.h" +#endif +#include "core/core_timing.h" namespace Common { @@ -144,4 +157,102 @@ void PinCurrentThreadToPerformanceCore(size_t core_id) { } } +#ifdef ARCHITECTURE_x86_64 +// On Linux and UNIX systems, a futex would nominally be used to cover the costs +// the idea is that it's intuitivelly cheaper to use a direct instruction as opposed to a full futex call +// the underlying libc++ implementation uses pthread_cond_timedwait which MAY invoke a futex +// Let's pretend the OS is too expensive to jump into, and avoid ANY context switches +// this should *IN THEORY* lower CPU usage while just waiting for stuff effectively +// For windows the minimal quanta resolution is about 500us, and normal CRT cond var is 1.5ms(?) +// so may as well avoid that too +// Let's just give ALL platforms the same mechanisms (almost) for when they have umonitor OR waitpkg +#ifdef __clang__ +__attribute__((target("waitpkg,mwaitx"))) +#elif defined(__GNUC__) +#pragma GCC target("waitpkg") +#pragma GCC target("mwaitx") +#endif +bool Event::WaitFor(const std::chrono::nanoseconds time) { +#ifdef _WIN32 + auto const start = Common::X64::FencedRDTSC(); + auto const& caps = Common::g_cpu_caps; + [[maybe_unused]] auto const end = start + Common::g_wall_clock.NsToTicks(time); + if (caps.monitorx) { + while (true) { + // Armed monitor, as per manual, MWAITX must be conditional if the condition isn't satisfied + // to prevent a race condition. + _mm_monitorx(reinterpret_cast(std::addressof(is_set)), 0, 0); + if (!is_set.load()) { + // RDTSC may be fenced here due to atomic load +#ifdef _MSC_VER + auto const now = __rdtsc(); +#else + auto const now = _rdtsc(); +#endif + if (end > now) { + u32 const cycles = std::min((std::numeric_limits::max)(), s64(end) - s64(now)); + // See here: https://github.com/torvalds/linux/blob/948a64995aca6820abefd17f1a4258f5835c5ad9/arch/x86/lib/delay.c#L93 + // MWAITX accepts a 32-bit input timer which determines the total number of cycles to wait for + // NOT THE TOTAL ABSOLUTE TSC VALUE, it's just a delta + // BIT[1] = use a timer + // Hint = 0: Use C1 state when sleepy (means slower wakeup but better savings) + _mm_mwaitx(1 << 1, 0u, cycles); + if (!is_set.load()) + return false; + } else + return false; //timeout + } + bool expected = true; + if (is_set.compare_exchange_weak(expected, false, std::memory_order_release)) + return true; + } + } else if (caps.waitpkg) { + // #UD If CPUID.7.0:ECX.WAITPKG[bit 5]=0. + while (true) { + _umonitor(std::addressof(is_set)); + if (!is_set.load() && !_umwait(0, end)) //umwait is absolute time!!! + return false; + bool expected = true; + if (is_set.compare_exchange_weak(expected, false, std::memory_order_release)) + return true; + } + } else { +#ifdef _MSC_VER + while (!is_set.load() && end > __rdtsc()) + Common::Windows::SleepForOneTick(); +#else + while (!is_set.load() && end > _rdtsc()) + Common::Windows::SleepForOneTick(); +#endif + if (is_set.load()) + Reset(); + return true; + } +#else + std::unique_lock lk{mutex}; + if (!condvar.wait_for(lk, time, [this] { return is_set.load(); })) + return false; + is_set = false; + return true; +#endif +} +#else +bool Event::WaitFor(const std::chrono::nanoseconds time) { +#ifdef _WIN32 + auto const end = Common::g_wall_clock.GetTimeNS() + time; + while (!is_set.load() && end > Common::g_wall_clock.GetTimeNS()) + Common::Windows::SleepForOneTick(); + if (is_set.load()) + Reset(); + return true; +#else + std::unique_lock lk{mutex}; + if (!condvar.wait_for(lk, time, [this] { return is_set.load(); })) + return false; + is_set = false; + return true; +#endif +} +#endif + } // namespace Common diff --git a/src/common/thread.h b/src/common/thread.h index ea6f5d6b3b..a75e342802 100644 --- a/src/common/thread.h +++ b/src/common/thread.h @@ -34,16 +34,10 @@ public: is_set = false; } - bool WaitFor(const std::chrono::nanoseconds& time) { - std::unique_lock lk{mutex}; - if (!condvar.wait_for(lk, time, [this] { return is_set.load(); })) - return false; - is_set = false; - return true; - } + bool WaitFor(const std::chrono::nanoseconds time); - template - bool WaitUntil(const std::chrono::time_point& time) { + template + bool WaitUntil(const std::chrono::time_point time) { std::unique_lock lk{mutex}; if (!condvar.wait_until(lk, time, [this] { return is_set.load(); })) return false; @@ -63,9 +57,9 @@ public: } private: + alignas(64) std::atomic is_set{false}; std::condition_variable condvar; std::mutex mutex; - std::atomic_bool is_set{false}; }; class Barrier { diff --git a/src/common/virtual_buffer.cpp b/src/common/virtual_buffer.cpp index 55ddfc243a..8a4265e3e6 100644 --- a/src/common/virtual_buffer.cpp +++ b/src/common/virtual_buffer.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project @@ -17,7 +17,11 @@ namespace Common { void* AllocateMemoryPages(std::size_t size) noexcept { #ifdef _WIN32 - void* base = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE); + void* base = VirtualAlloc(nullptr, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); + if (base == nullptr) { + // Probably failing to reserve is less likely than failing to commit + base = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE); + } #else void* base = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); if (base == MAP_FAILED) diff --git a/src/common/virtual_buffer.h b/src/common/virtual_buffer.h index af96317288..d6386e2a4d 100644 --- a/src/common/virtual_buffer.h +++ b/src/common/virtual_buffer.h @@ -38,7 +38,8 @@ public: VirtualBuffer& operator=(const VirtualBuffer&) = delete; VirtualBuffer(VirtualBuffer&& other) noexcept - : alloc_size{std::exchange(other.alloc_size, 0)}, base_ptr{std::exchange(other.base_ptr), nullptr} + : alloc_size{std::exchange(other.alloc_size, 0)} + , base_ptr{std::exchange(other.base_ptr, nullptr)} {} VirtualBuffer& operator=(VirtualBuffer&& other) noexcept { diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp deleted file mode 100644 index 4f9c240905..0000000000 --- a/src/common/wall_clock.cpp +++ /dev/null @@ -1,78 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/steady_clock.h" -#include "common/wall_clock.h" - -#ifdef ARCHITECTURE_x86_64 -#include "common/x64/cpu_detect.h" -#include "common/x64/native_clock.h" -#include "common/x64/rdtsc.h" -#endif -#ifdef HAS_NCE -#include "common/arm64/native_clock.h" -#endif - -namespace Common { - -class StandardWallClock final : public WallClock { -public: - explicit StandardWallClock() {} - - std::chrono::nanoseconds GetTimeNS() const override { - return std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()); - } - - std::chrono::microseconds GetTimeUS() const override { - return std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()); - } - - std::chrono::milliseconds GetTimeMS() const override { - return std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()); - } - - s64 GetCNTPCT() const override { - return GetUptime() * NsToCNTPCTRatio::num / NsToCNTPCTRatio::den; - } - - s64 GetGPUTick() const override { - return GetUptime() * NsToGPUTickRatio::num / NsToGPUTickRatio::den; - } - - s64 GetUptime() const override { - return std::chrono::duration_cast( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); - } - - bool IsNative() const override { - return false; - } -}; - -std::unique_ptr CreateOptimalClock() { -#if defined(ARCHITECTURE_x86_64) - const auto& caps = GetCPUCaps(); - - if (caps.invariant_tsc && caps.tsc_frequency >= std::nano::den) { - return std::make_unique(caps.tsc_frequency); - } else { - // Fallback to StandardWallClock if the hardware TSC - // - Is not invariant - // - Is not more precise than 1 GHz (1ns resolution) - return std::make_unique(); - } -#elif defined(HAS_NCE) - return std::make_unique(); -#else - return std::make_unique(); -#endif -} - -} // namespace Common diff --git a/src/common/wall_clock.h b/src/common/wall_clock.h deleted file mode 100644 index 7ad6536930..0000000000 --- a/src/common/wall_clock.h +++ /dev/null @@ -1,92 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include -#include - -#include "common/common_types.h" - -namespace Common { - -class WallClock { -public: - static constexpr u64 CNTFRQ = 19'200'000; // CNTPCT_EL0 Frequency = 19.2 MHz - static constexpr u64 GPUTickFreq = 614'400'000; // GM20B GPU Tick Frequency = 614.4 MHz - static constexpr u64 CPUTickFreq = 1'020'000'000; // T210/4 A57 CPU Tick Frequency = 1020.0 MHz - - virtual ~WallClock() = default; - - /// @returns The time in nanoseconds since the construction of this clock. - virtual std::chrono::nanoseconds GetTimeNS() const = 0; - - /// @returns The time in microseconds since the construction of this clock. - virtual std::chrono::microseconds GetTimeUS() const = 0; - - /// @returns The time in milliseconds since the construction of this clock. - virtual std::chrono::milliseconds GetTimeMS() const = 0; - - /// @returns The guest CNTPCT ticks since the construction of this clock. - virtual s64 GetCNTPCT() const = 0; - - /// @returns The guest GPU ticks since the construction of this clock. - virtual s64 GetGPUTick() const = 0; - - /// @returns The raw host timer ticks since an indeterminate epoch. - virtual s64 GetUptime() const = 0; - - /// @returns Whether the clock directly uses the host's hardware clock. - virtual bool IsNative() const = 0; - - static inline u64 NSToCNTPCT(u64 ns) { - return ns * NsToCNTPCTRatio::num / NsToCNTPCTRatio::den; - } - - static inline u64 NSToGPUTick(u64 ns) { - return ns * NsToGPUTickRatio::num / NsToGPUTickRatio::den; - } - - // Cycle Timing - - static inline u64 CPUTickToNS(u64 cpu_tick) { - return cpu_tick * CPUTickToNsRatio::num / CPUTickToNsRatio::den; - } - - static inline u64 CPUTickToUS(u64 cpu_tick) { - return cpu_tick * CPUTickToUsRatio::num / CPUTickToUsRatio::den; - } - - static inline u64 CPUTickToCNTPCT(u64 cpu_tick) { - return cpu_tick * CPUTickToCNTPCTRatio::num / CPUTickToCNTPCTRatio::den; - } - - static inline u64 CPUTickToGPUTick(u64 cpu_tick) { - return cpu_tick * CPUTickToGPUTickRatio::num / CPUTickToGPUTickRatio::den; - } - -protected: - using NsRatio = std::nano; - using UsRatio = std::micro; - using MsRatio = std::milli; - - using NsToUsRatio = std::ratio_divide; - using NsToMsRatio = std::ratio_divide; - using NsToCNTPCTRatio = std::ratio; - using NsToGPUTickRatio = std::ratio; - - // Cycle Timing - - using CPUTickToNsRatio = std::ratio; - using CPUTickToUsRatio = std::ratio; - using CPUTickToCNTPCTRatio = std::ratio; - using CPUTickToGPUTickRatio = std::ratio; -}; - -[[nodiscard]] std::unique_ptr CreateOptimalClock(); - -} // namespace Common diff --git a/src/common/x64/cpu_detect.h b/src/common/x64/cpu_detect.h deleted file mode 100644 index 756459417f..0000000000 --- a/src/common/x64/cpu_detect.h +++ /dev/null @@ -1,83 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project -// SPDX-FileCopyrightText: Copyright 2013 Dolphin Emulator Project / 2015 Citra Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include -#include "common/common_types.h" - -namespace Common { - -/// x86/x64 CPU capabilities that may be detected by this module -struct CPUCaps { - - enum class Manufacturer : u8 { - Unknown = 0, - Intel = 1, - AMD = 2, - Hygon = 3, - }; - - static Manufacturer ParseManufacturer(std::string_view brand_string); - - Manufacturer manufacturer; - char brand_string[13]; - - char cpu_string[48]; - - u32 base_frequency; - u32 max_frequency; - u32 bus_frequency; - - u32 tsc_crystal_ratio_denominator; - u32 tsc_crystal_ratio_numerator; - u32 crystal_frequency; - u64 tsc_frequency; // Derived from the above three values - - bool sse : 1; - bool sse2 : 1; - bool sse3 : 1; - bool ssse3 : 1; - bool sse4_1 : 1; - bool sse4_2 : 1; - - bool avx : 1; - bool avx_vnni : 1; - bool avx2 : 1; - bool avx512f : 1; - bool avx512dq : 1; - bool avx512cd : 1; - bool avx512bw : 1; - bool avx512vl : 1; - bool avx512vbmi : 1; - bool avx512bitalg : 1; - - bool aes : 1; - bool bmi1 : 1; - bool bmi2 : 1; - bool f16c : 1; - bool fma : 1; - bool fma4 : 1; - bool gfni : 1; - bool invariant_tsc : 1; - bool lzcnt : 1; - bool monitorx : 1; - bool movbe : 1; - bool pclmulqdq : 1; - bool popcnt : 1; - bool sha : 1; - bool waitpkg : 1; -}; - -/** - * Gets the supported capabilities of the host CPU - * @return Reference to a CPUCaps struct with the detected host CPU capabilities - */ -const CPUCaps& GetCPUCaps(); - -/// Detects CPU core count -std::optional GetProcessorCount(); - -} // namespace Common diff --git a/src/common/x64/cpu_wait.cpp b/src/common/x64/cpu_wait.cpp deleted file mode 100644 index 85d27161ba..0000000000 --- a/src/common/x64/cpu_wait.cpp +++ /dev/null @@ -1,75 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#include - -#ifdef _MSC_VER -#include -#endif - -#include "common/x64/cpu_detect.h" -#include "common/x64/cpu_wait.h" -#include "common/x64/rdtsc.h" - -namespace Common::X64 { - -namespace { - -// 100,000 cycles is a reasonable amount of time to wait to save on CPU resources. -// For reference: -// At 1 GHz, 100K cycles is 100us -// At 2 GHz, 100K cycles is 50us -// At 4 GHz, 100K cycles is 25us -constexpr auto PauseCycles = 100'000U; - -} // Anonymous namespace - -#if defined(_MSC_VER) && !defined(__clang__) -__forceinline static void TPAUSE() { - static constexpr auto RequestC02State = 0U; - _tpause(RequestC02State, FencedRDTSC() + PauseCycles); -} - -__forceinline static void MWAITX() { - static constexpr auto EnableWaitTimeFlag = 1U << 1; - static constexpr auto RequestC1State = 0U; - - // monitor_var should be aligned to a cache line. - alignas(64) u64 monitor_var{}; - _mm_monitorx(&monitor_var, 0, 0); - _mm_mwaitx(EnableWaitTimeFlag, RequestC1State, PauseCycles); -} -#else -static void TPAUSE() { - static constexpr auto RequestC02State = 0U; - const auto tsc = FencedRDTSC() + PauseCycles; - const auto eax = static_cast(tsc & 0xFFFFFFFF); - const auto edx = static_cast(tsc >> 32); - asm volatile("tpause %0" : : "r"(RequestC02State), "d"(edx), "a"(eax)); -} - -static void MWAITX() { - static constexpr auto EnableWaitTimeFlag = 1U << 1; - static constexpr auto RequestC1State = 0U; - - // monitor_var should be aligned to a cache line. - alignas(64) u64 monitor_var{}; - asm volatile("monitorx" : : "a"(&monitor_var), "c"(0), "d"(0)); - asm volatile("mwaitx" : : "a"(RequestC1State), "b"(PauseCycles), "c"(EnableWaitTimeFlag)); -} -#endif - -void MicroSleep() { - static const bool has_waitpkg = GetCPUCaps().waitpkg; - static const bool has_monitorx = GetCPUCaps().monitorx; - - if (has_waitpkg) { - TPAUSE(); - } else if (has_monitorx) { - MWAITX(); - } else { - std::this_thread::yield(); - } -} - -} // namespace Common::X64 diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp deleted file mode 100644 index d2d27fafea..0000000000 --- a/src/common/x64/native_clock.cpp +++ /dev/null @@ -1,46 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/uint128.h" -#include "common/x64/native_clock.h" -#include "common/x64/rdtsc.h" - -namespace Common::X64 { - -NativeClock::NativeClock(u64 rdtsc_frequency_) - : rdtsc_frequency{rdtsc_frequency_}, ns_rdtsc_factor{GetFixedPoint64Factor(NsRatio::den, - rdtsc_frequency)}, - us_rdtsc_factor{GetFixedPoint64Factor(UsRatio::den, rdtsc_frequency)}, - ms_rdtsc_factor{GetFixedPoint64Factor(MsRatio::den, rdtsc_frequency)}, - cntpct_rdtsc_factor{GetFixedPoint64Factor(CNTFRQ, rdtsc_frequency)}, - gputick_rdtsc_factor{GetFixedPoint64Factor(GPUTickFreq, rdtsc_frequency)} {} - -std::chrono::nanoseconds NativeClock::GetTimeNS() const { - return std::chrono::nanoseconds{MultiplyHigh(GetUptime(), ns_rdtsc_factor)}; -} - -std::chrono::microseconds NativeClock::GetTimeUS() const { - return std::chrono::microseconds{MultiplyHigh(GetUptime(), us_rdtsc_factor)}; -} - -std::chrono::milliseconds NativeClock::GetTimeMS() const { - return std::chrono::milliseconds{MultiplyHigh(GetUptime(), ms_rdtsc_factor)}; -} - -s64 NativeClock::GetCNTPCT() const { - return MultiplyHigh(GetUptime(), cntpct_rdtsc_factor); -} - -s64 NativeClock::GetGPUTick() const { - return MultiplyHigh(GetUptime(), gputick_rdtsc_factor); -} - -s64 NativeClock::GetUptime() const { - return static_cast(FencedRDTSC()); -} - -bool NativeClock::IsNative() const { - return true; -} - -} // namespace Common::X64 diff --git a/src/common/x64/native_clock.h b/src/common/x64/native_clock.h deleted file mode 100644 index b2629b0311..0000000000 --- a/src/common/x64/native_clock.h +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "common/wall_clock.h" - -namespace Common::X64 { - -class NativeClock final : public WallClock { -public: - explicit NativeClock(u64 rdtsc_frequency_); - - std::chrono::nanoseconds GetTimeNS() const override; - - std::chrono::microseconds GetTimeUS() const override; - - std::chrono::milliseconds GetTimeMS() const override; - - s64 GetCNTPCT() const override; - - s64 GetGPUTick() const override; - - s64 GetUptime() const override; - - bool IsNative() const override; - -private: - u64 rdtsc_frequency; - - u64 ns_rdtsc_factor; - u64 us_rdtsc_factor; - u64 ms_rdtsc_factor; - u64 cntpct_rdtsc_factor; - u64 gputick_rdtsc_factor; -}; - -} // namespace Common::X64 diff --git a/src/common/x64/xbyak_abi.h b/src/common/x64/xbyak.h similarity index 77% rename from src/common/x64/xbyak_abi.h rename to src/common/x64/xbyak.h index 8aea5db583..e541215994 100644 --- a/src/common/x64/xbyak_abi.h +++ b/src/common/x64/xbyak.h @@ -1,13 +1,37 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2016 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once +#include #include #include -#include #include "common/assert.h" +// xbyak hates human beings +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wconversion" +#pragma GCC diagnostic ignored "-Wshadow" +#endif +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wconversion" +#pragma clang diagnostic ignored "-Wshadow" +#endif + +// You must ensure this matches with src/common/x64/xbyak.h on root dir +#include +#include +#define XBYAK_STD_UNORDERED_SET ankerl::unordered_dense::set +#define XBYAK_STD_UNORDERED_MAP ankerl::unordered_dense::map +#define XBYAK_STD_UNORDERED_MULTIMAP boost::unordered_multimap +#include +#include + +#include + namespace Common::X64 { constexpr size_t RegToIndex(const Xbyak::Reg& reg) { @@ -174,12 +198,13 @@ inline ABIFrameInfo ABI_CalculateFrameSize(std::bitset<32> regs, size_t rsp_alig rsp_alignment -= subtraction; subtraction += rsp_alignment & 0xF; - return ABIFrameInfo{static_cast(subtraction), - static_cast(subtraction - xmm_base_subtraction)}; + return ABIFrameInfo{ + s32(subtraction), + s32(subtraction - xmm_base_subtraction) + }; } -inline size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::bitset<32> regs, - size_t rsp_alignment, size_t needed_frame_size = 0) { +inline size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::bitset<32> regs, size_t rsp_alignment, size_t needed_frame_size = 0) { auto frame_info = ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size); for (size_t i = 0; i < regs.size(); ++i) { @@ -202,8 +227,7 @@ inline size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::b return ABI_SHADOW_SPACE; } -inline void ABI_PopRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::bitset<32> regs, - size_t rsp_alignment, size_t needed_frame_size = 0) { +inline void ABI_PopRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::bitset<32> regs, size_t rsp_alignment, size_t needed_frame_size = 0) { auto frame_info = ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size); for (size_t i = 0; i < regs.size(); ++i) { @@ -226,4 +250,34 @@ inline void ABI_PopRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::bits } } +// Constants for use with cmpps/cmpss +enum { + CMP_EQ = 0, + CMP_LT = 1, + CMP_LE = 2, + CMP_UNORD = 3, + CMP_NEQ = 4, + CMP_NLT = 5, + CMP_NLE = 6, + CMP_ORD = 7, +}; + +constexpr bool IsWithin2G(uintptr_t ref, uintptr_t target) noexcept { + u64 const distance = target - (ref + 5); + return (distance & 0xffff'ffff) == distance; +} + +template +inline void CallFarFunction(Xbyak::CodeGenerator& code, const T f) { + static_assert(std::is_pointer_v, "Argument must be a (function) pointer."); + uintptr_t addr = uintptr_t(f); + if (IsWithin2G(uintptr_t(code.getCurr()), addr)) { + code.call(f); + } else { + // ABI_RETURN is a safe temp register to use before a call + code.mov(ABI_RETURN, addr); + code.call(ABI_RETURN); + } +} + } // namespace Common::X64 diff --git a/src/common/x64/xbyak_util.h b/src/common/x64/xbyak_util.h deleted file mode 100644 index 250e5cddb9..0000000000 --- a/src/common/x64/xbyak_util.h +++ /dev/null @@ -1,46 +0,0 @@ -// SPDX-FileCopyrightText: 2016 Citra Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include -#include "common/x64/xbyak_abi.h" - -namespace Common::X64 { - -// Constants for use with cmpps/cmpss -enum { - CMP_EQ = 0, - CMP_LT = 1, - CMP_LE = 2, - CMP_UNORD = 3, - CMP_NEQ = 4, - CMP_NLT = 5, - CMP_NLE = 6, - CMP_ORD = 7, -}; - -constexpr bool IsWithin2G(uintptr_t ref, uintptr_t target) { - const u64 distance = target - (ref + 5); - return !(distance >= 0x8000'0000ULL && distance <= ~0x8000'0000ULL); -} - -inline bool IsWithin2G(const Xbyak::CodeGenerator& code, uintptr_t target) { - return IsWithin2G(reinterpret_cast(code.getCurr()), target); -} - -template -inline void CallFarFunction(Xbyak::CodeGenerator& code, const T f) { - static_assert(std::is_pointer_v, "Argument must be a (function) pointer."); - size_t addr = reinterpret_cast(f); - if (IsWithin2G(code, addr)) { - code.call(f); - } else { - // ABI_RETURN is a safe temp register to use before a call - code.mov(ABI_RETURN, addr); - code.call(ABI_RETURN); - } -} - -} // namespace Common::X64 diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 39aebd5f48..16806ff0c0 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1121,6 +1121,10 @@ add_library(core STATIC hle/service/vi/vi_types.h hle/service/vi/vsync_manager.cpp hle/service/vi/vsync_manager.h + hle/service/gpio/gpio.cpp + hle/service/gpio/gpio.h + hle/service/i2c/i2c.cpp + hle/service/i2c/i2c.h internal_network/emu_net_state.cpp internal_network/emu_net_state.h internal_network/network.cpp @@ -1200,7 +1204,7 @@ else() $<$:-Wno-cast-function-type> $<$:-fsized-deallocation>) # pre-clang19 will spam with "OH DID YOU MEAN THIS?" otherwise... - if (CXX_CLANG AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19) + if (CXX_CLANG AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 20) target_compile_options(core PRIVATE $<$:-Wno-cast-function-type-mismatch>) endif() endif() @@ -1215,6 +1219,7 @@ else() endif() target_link_libraries(core PRIVATE + OpenSSL::SSL fmt::fmt nlohmann_json::nlohmann_json RenderDoc::API @@ -1246,7 +1251,7 @@ if (HAS_NCE) target_link_libraries(core PRIVATE merry::oaknut) endif() -if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64 OR ARCHITECTURE_riscv64) +if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64 OR ARCHITECTURE_riscv64 OR ARCHITECTURE_loongarch64) target_sources(core PRIVATE arm/dynarmic/arm_dynarmic.h arm/dynarmic/arm_dynarmic_64.cpp diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h index 46384f7e6d..8e6b1d9766 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.h +++ b/src/core/arm/dynarmic/arm_dynarmic.h @@ -1,9 +1,11 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#pragma once + #include #include "core/arm/arm_interface.h" diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index c0d6ff9516..e434a31d5d 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -113,8 +113,7 @@ void DynarmicCallbacks32::CallSVC(u32 swi) { } void DynarmicCallbacks32::AddTicks(u64 ticks) { - ASSERT_MSG(!m_parent.m_uses_wall_clock, "Dynarmic ticking disabled"); - + ASSERT(!m_parent.m_uses_wall_clock && "Dynarmic ticking disabled"); // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a // rough approximation of the amount of executed ticks in the system, it may be thrown off // if not all cores are doing a similar amount of work. Instead of doing this, we should @@ -123,14 +122,12 @@ void DynarmicCallbacks32::AddTicks(u64 ticks) { u64 amortized_ticks = ticks / Core::Hardware::NUM_CPU_CORES; // Always execute at least one tick. amortized_ticks = std::max(amortized_ticks, 1); - m_parent.m_system.CoreTiming().AddTicks(amortized_ticks); } u64 DynarmicCallbacks32::GetTicksRemaining() { - ASSERT_MSG(!m_parent.m_uses_wall_clock, "Dynarmic ticking disabled"); - - return std::max(m_parent.m_system.CoreTiming().GetDowncount(), 0); + ASSERT(!m_parent.m_uses_wall_clock && "Dynarmic ticking disabled"); + return std::max(m_parent.m_system.CoreTiming().downcount, 0); } bool DynarmicCallbacks32::CheckMemoryAccess(u64 addr, u64 size, Kernel::DebugWatchpointType type) { diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 63cc33d9c8..e6c7e61265 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -150,8 +150,7 @@ void DynarmicCallbacks64::CallSVC(u32 svc) { } void DynarmicCallbacks64::AddTicks(u64 ticks) { - ASSERT_MSG(!m_parent.m_uses_wall_clock, "Dynarmic ticking disabled"); - + ASSERT(!m_parent.m_uses_wall_clock && "Dynarmic ticking disabled"); // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a // rough approximation of the amount of executed ticks in the system, it may be thrown off // if not all cores are doing a similar amount of work. Instead of doing this, we should @@ -160,13 +159,12 @@ void DynarmicCallbacks64::AddTicks(u64 ticks) { u64 amortized_ticks = ticks / Core::Hardware::NUM_CPU_CORES; // Always execute at least one tick. amortized_ticks = std::max(amortized_ticks, 1); - m_parent.m_system.CoreTiming().AddTicks(amortized_ticks); } u64 DynarmicCallbacks64::GetTicksRemaining() { ASSERT(!m_parent.m_uses_wall_clock && "Dynarmic ticking disabled"); - return std::max(m_parent.m_system.CoreTiming().GetDowncount(), 0); + return std::max(m_parent.m_system.CoreTiming().downcount, 0); } u64 DynarmicCallbacks64::GetCNTPCT() { diff --git a/src/core/arm/nce/arm_nce.cpp b/src/core/arm/nce/arm_nce.cpp index bbff9f2829..63d5638fce 100644 --- a/src/core/arm/nce/arm_nce.cpp +++ b/src/core/arm/nce/arm_nce.cpp @@ -388,7 +388,7 @@ void ArmNce::SignalInterrupt(Kernel::KThread* thread) { } } -const std::size_t CACHE_PAGE_SIZE = 4096; +[[maybe_unused]] const std::size_t CACHE_PAGE_SIZE = 4096; void ArmNce::ClearInstructionCache() { #ifdef __aarch64__ diff --git a/src/core/arm/nce/instructions.h b/src/core/arm/nce/instructions.h index ada0da9559..451778df65 100644 --- a/src/core/arm/nce/instructions.h +++ b/src/core/arm/nce/instructions.h @@ -1,9 +1,11 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2020 Skyline Team and Contributors // SPDX-License-Identifier: MPL-2.0 +#pragma once + #include "common/bit_field.h" #include "common/common_types.h" diff --git a/src/core/arm/nce/interpreter_visitor.cpp b/src/core/arm/nce/interpreter_visitor.cpp index ec2b155fe4..2ccc176323 100644 --- a/src/core/arm/nce/interpreter_visitor.cpp +++ b/src/core/arm/nce/interpreter_visitor.cpp @@ -12,9 +12,9 @@ namespace Core { namespace { // Prefetch tuning parameters -constexpr size_t CACHE_LINE_SIZE = 64; -constexpr size_t PREFETCH_STRIDE = 128; // 2 cache lines ahead -constexpr size_t SIMD_PREFETCH_THRESHOLD = 32; // Bytes +[[maybe_unused]] constexpr size_t CACHE_LINE_SIZE = 64; +[[maybe_unused]] constexpr size_t PREFETCH_STRIDE = 128; // 2 cache lines ahead +[[maybe_unused]] constexpr size_t SIMD_PREFETCH_THRESHOLD = 32; // Bytes } // namespace template diff --git a/src/core/arm/nce/patcher.cpp b/src/core/arm/nce/patcher.cpp index ea77166645..79ff3b1e31 100644 --- a/src/core/arm/nce/patcher.cpp +++ b/src/core/arm/nce/patcher.cpp @@ -3,7 +3,7 @@ #include #include -#include "common/arm64/native_clock.h" +#include "common/cpu_features.h" #include "common/alignment.h" #include "common/literals.h" #include "core/arm/nce/arm_nce.h" @@ -578,7 +578,11 @@ void Patcher::WriteMsrHandler(ModuleDestLabel module_dest, oaknut::XReg src_reg, } void Patcher::WriteCntpctHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg, oaknut::VectorCodeGenerator& cg) { - static Common::Arm64::NativeClock clock{}; +#if defined(HAS_NCE) + static Common::WallClock clock(false, 1); +#else + static Common::WallClock clock(true, 1); +#endif const auto factor = clock.GetGuestCNTFRQFactor(); const auto raw_factor = std::bit_cast>(factor); diff --git a/src/core/core.cpp b/src/core/core.cpp index e730b808e0..eb37953351 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -108,7 +108,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, struct System::Impl { explicit Impl(System& system) - : kernel{system}, fs_controller{system}, hid_core{}, cpu_manager{system}, + : kernel{system}, fs_controller{system}, hid_core{system.Kernel()}, cpu_manager{system}, reporter{system}, applet_manager{system}, frontend_applets{system}, profile_manager{} {} u64 program_id; @@ -271,7 +271,7 @@ struct System::Impl { SystemResultStatus SetupForApplicationProcess(System& system, Frontend::EmuWindow& emu_window) { host1x_core.emplace(system); - gpu_core = VideoCore::CreateGPU(emu_window, system); + VideoCore::CreateGPU(gpu_core, emu_window, system); if (!gpu_core) return SystemResultStatus::ErrorVideoCore; @@ -326,6 +326,9 @@ struct System::Impl { LOG_INFO(Core, "Loading {} ({:016X}) ...", name, params.program_id); + // Expose program id to dump sites and other global readers. + Settings::SetCurrentProgramID(params.program_id); + // Track launch time for frontend launches LaunchTimestampCache::SaveLaunchTimestamp(params.program_id); @@ -391,10 +394,8 @@ struct System::Impl { is_powered_on = false; exit_locked = false; exit_requested = false; - - if (gpu_core != nullptr) { + if (gpu_core) gpu_core->NotifyShutdown(); - } stop_event.request_stop(); core_timing.SyncPause(false); @@ -478,6 +479,7 @@ struct System::Impl { std::optional cheat_engine; std::optional memory_freezer; std::optional renderdoc_api; + std::optional gpu_core; std::array gpu_dirty_memory_managers; std::vector> user_channel; @@ -492,7 +494,6 @@ struct System::Impl { std::unique_ptr content_provider; /// AppLoader used to load the current executing application std::unique_ptr app_loader; - std::unique_ptr gpu_core; std::stop_source stop_event; mutable std::mutex suspend_guard; @@ -925,7 +926,7 @@ void System::PushGeneralChannelData(std::vector&& data) { const bool was_empty = impl->general_channel.empty(); impl->general_channel.push_back(std::move(data)); if (was_empty) { - impl->general_channel_event->Signal(); + impl->general_channel_event->Signal(impl->kernel); } } @@ -937,7 +938,7 @@ bool System::TryPopGeneralChannel(std::vector& out_data) { out_data = std::move(impl->general_channel.back()); impl->general_channel.pop_back(); if (impl->general_channel.empty()) { - impl->general_channel_event->Clear(); + impl->general_channel_event->Clear(impl->kernel); } return true; } diff --git a/src/core/core.h b/src/core/core.h index 012533c1fa..93f7b057f7 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -438,7 +438,6 @@ public: /// Applies any changes to settings to this core instance. void ApplySettings(); -private: struct Impl; std::unique_ptr impl; }; diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index b216dc2094..3e4e7bdc68 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -8,15 +8,13 @@ #include #include #include +#include "common/cpu_features.h" +#include "common/cpu_features.h" #ifdef _WIN32 #include "common/windows/timer_resolution.h" #endif -#ifdef ARCHITECTURE_x86_64 -#include "common/x64/cpu_wait.h" -#endif - #include "common/settings.h" #include "core/core_timing.h" #include "core/hardware_properties.h" @@ -47,8 +45,7 @@ struct CoreTiming::Event { } }; -CoreTiming::CoreTiming() : clock{Common::CreateOptimalClock()} {} - +CoreTiming::CoreTiming() = default; CoreTiming::~CoreTiming() { Reset(); } @@ -57,15 +54,36 @@ void CoreTiming::Initialize(std::function&& on_thread_init_) { Reset(); on_thread_init = std::move(on_thread_init_); event_fifo_id = 0; - shutting_down = false; cpu_ticks = 0; if (is_multicore) { - timer_thread.emplace([](CoreTiming& instance) { + timer_thread = std::jthread([this](std::stop_token stop_token) { Common::SetCurrentThreadName("HostTiming"); Common::SetCurrentThreadPriority(Common::ThreadPriority::High); - instance.on_thread_init(); - instance.ThreadLoop(); - }, std::ref(*this)); + on_thread_init(); + has_started = true; + + // base frequency in MHz: 1ns (10^-9) = 1GHz (10^9) + while (!stop_token.stop_requested()) { + while (!paused && !stop_token.stop_requested()) { + paused_set = false; + if (auto const next_time = Advance(); next_time) { + // There are more events left in the queue, wait until the next event. + auto const wait_time = *next_time - GetGlobalTimeNs().count(); + if (wait_time > 0) { + event.WaitFor(std::chrono::nanoseconds(wait_time)); + } + } else { + // Queue is empty, wait until another event is scheduled and signals us to + // continue. + wait_set = true; + event.Wait(); + } + wait_set = false; + } + paused_set = true; + pause_event.Wait(); + } + }); } } @@ -90,7 +108,7 @@ void CoreTiming::SyncPause(bool is_paused) { } Pause(is_paused); - if (timer_thread) { + if (timer_thread.joinable()) { if (!is_paused) { pause_event.Set(); } @@ -190,33 +208,22 @@ void CoreTiming::ResetTicks() { } u64 CoreTiming::GetClockTicks() const { - u64 fres; - if (is_multicore) [[likely]] { - fres = clock->GetCNTPCT(); - } else { - fres = Common::WallClock::CPUTickToCNTPCT(cpu_ticks); + u64 fres = is_multicore ? Common::g_wall_clock.GetCNTPCT() : Common::WallClock::CPUTickToCNTPCT(cpu_ticks); + if (auto const overclock = Settings::values.fast_cpu_time.GetValue(); overclock != Settings::CpuClock::Off) { + fres = u64(f64(fres) * (1.7 + 0.3 * u32(overclock))); } - - const auto overclock = Settings::values.fast_cpu_time.GetValue(); - - if (overclock != Settings::CpuClock::Off) { - fres = (u64) ((double) fres * (1.7 + 0.3 * u32(overclock))); - } - - if (Settings::values.sync_core_speed.GetValue()) { - const auto ticks = double(fres); - const auto speed_limit = double(Settings::SpeedLimit())*0.01; - return u64(ticks/speed_limit); - } else { - return fres; - } + if (::Settings::values.sync_core_speed.GetValue()) { + auto const ticks = f64(fres); + auto const speed_limit = f64(Settings::SpeedLimit()) * 0.01; + return u64(ticks / speed_limit); + } + return fres; } u64 CoreTiming::GetGPUTicks() const { - if (is_multicore) [[likely]] { - return clock->GetGPUTick(); - } - return Common::WallClock::CPUTickToGPUTick(cpu_ticks); + return is_multicore + ? Common::g_wall_clock.GetGPUTick() + : Common::WallClock::CPUTickToGPUTick(cpu_ticks); } std::optional CoreTiming::Advance() { @@ -278,75 +285,29 @@ std::optional CoreTiming::Advance() { } } -void CoreTiming::ThreadLoop() { - has_started = true; - while (!shutting_down) { - while (!paused) { - paused_set = false; - const auto next_time = Advance(); - if (next_time) { - // There are more events left in the queue, wait until the next event. - auto wait_time = *next_time - GetGlobalTimeNs().count(); - if (wait_time > 0) { -#ifdef _WIN32 - while (!paused && !event.IsSet() && wait_time > 0) { - wait_time = *next_time - GetGlobalTimeNs().count(); - if (wait_time >= timer_resolution_ns) { - Common::Windows::SleepForOneTick(); - } else { -#ifdef ARCHITECTURE_x86_64 - Common::X64::MicroSleep(); -#else - std::this_thread::yield(); -#endif - } - } - - if (event.IsSet()) { - event.Reset(); - } -#else - event.WaitFor(std::chrono::nanoseconds(wait_time)); -#endif - } - } else { - // Queue is empty, wait until another event is scheduled and signals us to - // continue. - wait_set = true; - event.Wait(); - } - wait_set = false; - } - - paused_set = true; - pause_event.Wait(); - } -} - void CoreTiming::Reset() { paused = true; - shutting_down = true; pause_event.Set(); event.Set(); - if (timer_thread) { - timer_thread->join(); + if (timer_thread.joinable()) { + timer_thread.request_stop(); + timer_thread.join(); } - timer_thread.reset(); has_started = false; } -std::chrono::nanoseconds CoreTiming::GetGlobalTimeNs() const { - if (is_multicore) [[likely]] { - return clock->GetTimeNS(); - } - return std::chrono::nanoseconds{Common::WallClock::CPUTickToNS(cpu_ticks)}; +/// @brief Returns current time in nanoseconds. +std::chrono::nanoseconds CoreTiming::GetGlobalTimeNs() const noexcept { + return is_multicore + ? Common::g_wall_clock.GetTimeNS() + : std::chrono::nanoseconds{Common::WallClock::CPUTickToNS(cpu_ticks)}; } -std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const { - if (is_multicore) [[likely]] { - return clock->GetTimeUS(); - } - return std::chrono::microseconds{Common::WallClock::CPUTickToUS(cpu_ticks)}; +/// @brief Returns current time in microseconds. +std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const noexcept { + return is_multicore + ? Common::g_wall_clock.GetTimeUS() + : std::chrono::microseconds{Common::WallClock::CPUTickToUS(cpu_ticks)}; } #ifdef _WIN32 diff --git a/src/core/core_timing.h b/src/core/core_timing.h index ae9f56d519..298fc9595d 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project @@ -19,7 +19,7 @@ #include "common/common_types.h" #include "common/thread.h" -#include "common/wall_clock.h" +#include "common/cpu_features.h" namespace Core::Timing { @@ -118,7 +118,7 @@ public: void Idle(); - s64 GetDowncount() const { + s64 GetDowncount() const noexcept { return downcount; } @@ -128,11 +128,8 @@ public: /// Returns the current GPU tick value. u64 GetGPUTicks() const; - /// Returns current time in microseconds. - std::chrono::microseconds GetGlobalTimeUs() const; - - /// Returns current time in nanoseconds. - std::chrono::nanoseconds GetGlobalTimeNs() const; + [[nodiscard]] std::chrono::microseconds GetGlobalTimeUs() const noexcept; + [[nodiscard]] std::chrono::nanoseconds GetGlobalTimeNs() const noexcept; /// Checks for events manually and returns time in nanoseconds for next event, threadsafe. std::optional Advance(); @@ -141,44 +138,32 @@ public: void SetTimerResolutionNs(std::chrono::nanoseconds ns); #endif -private: struct Event; - void ThreadLoop(); void Reset(); - std::unique_ptr clock; - + using heap_t = boost::heap::fibonacci_heap>>; + heap_t event_queue; s64 global_timer = 0; - #ifdef _WIN32 s64 timer_resolution_ns; #endif - - using heap_t = - boost::heap::fibonacci_heap>>; - - heap_t event_queue; u64 event_fifo_id = 0; - - Common::Event event{}; - Common::Event pause_event{}; - mutable std::mutex basic_lock; - std::mutex advance_lock; - std::optional timer_thread; - std::atomic paused{}; - std::atomic paused_set{}; - std::atomic wait_set{}; - std::atomic shutting_down{}; - std::atomic has_started{}; - std::function on_thread_init{}; - - bool is_multicore{}; s64 pause_end_time{}; - /// Cycle timing u64 cpu_ticks{}; s64 downcount{}; + Common::Event event{}; + Common::Event pause_event{}; + std::function on_thread_init{}; + std::jthread timer_thread; + mutable std::mutex basic_lock; + std::mutex advance_lock; + std::atomic paused{}; + std::atomic paused_set{}; + std::atomic wait_set{}; + std::atomic has_started{}; + bool is_multicore{}; }; /// Creates a core timing event with the given name and callback. diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index 10fdcdf8a2..7194fa5022 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp @@ -25,9 +25,11 @@ CpuManager::~CpuManager() = default; void CpuManager::Initialize() { num_cores = is_multicore ? Core::Hardware::NUM_CPU_CORES : 1; - gpu_barrier = std::make_unique(num_cores + 1); + gpu_barrier.emplace(num_cores + 1); for (std::size_t core = 0; core < num_cores; core++) - core_data[core].host_thread = std::jthread([this, core](std::stop_token token) { RunThread(token, core); }); + core_data[core].host_thread = std::jthread([this, core](std::stop_token token) { + RunThread(token, core); + }); } void CpuManager::Shutdown() { @@ -39,69 +41,61 @@ void CpuManager::Shutdown() { } } -void CpuManager::GuestThreadFunction() { +void CpuManager::GuestThreadFunction(Kernel::KernelCore& kernel) { if (is_multicore) { - MultiCoreRunGuestThread(); + MultiCoreRunGuestThread(kernel); } else { - SingleCoreRunGuestThread(); + SingleCoreRunGuestThread(kernel); } } -void CpuManager::IdleThreadFunction() { +void CpuManager::IdleThreadFunction(Kernel::KernelCore& kernel) { if (is_multicore) { - MultiCoreRunIdleThread(); + MultiCoreRunIdleThread(kernel); } else { - SingleCoreRunIdleThread(); + SingleCoreRunIdleThread(kernel); } } -void CpuManager::ShutdownThreadFunction() { - ShutdownThread(); +void CpuManager::ShutdownThreadFunction(Kernel::KernelCore& kernel) { + ShutdownThread(kernel); } -void CpuManager::HandleInterrupt() { - auto& kernel = system.Kernel(); +void CpuManager::HandleInterrupt(Kernel::KernelCore& kernel) { auto core_index = kernel.CurrentPhysicalCoreIndex(); - - Kernel::KInterruptManager::HandleInterrupt(kernel, static_cast(core_index)); + Kernel::KInterruptManager::HandleInterrupt(kernel, s32(core_index)); } /////////////////////////////////////////////////////////////////////////////// /// MultiCore /// /////////////////////////////////////////////////////////////////////////////// -void CpuManager::MultiCoreRunGuestThread() { +void CpuManager::MultiCoreRunGuestThread(Kernel::KernelCore& kernel) { // Similar to UserModeThreadStarter in HOS - auto& kernel = system.Kernel(); auto* thread = Kernel::GetCurrentThreadPointer(kernel); - kernel.CurrentScheduler()->OnThreadStart(); + kernel.CurrentScheduler()->OnThreadStart(kernel); while (true) { auto* physical_core = &kernel.CurrentPhysicalCore(); while (!physical_core->IsInterrupted()) { - physical_core->RunThread(thread); + physical_core->RunThread(kernel, thread); physical_core = &kernel.CurrentPhysicalCore(); } - - HandleInterrupt(); + HandleInterrupt(kernel); } } -void CpuManager::MultiCoreRunIdleThread() { +void CpuManager::MultiCoreRunIdleThread(Kernel::KernelCore& kernel) { // Not accurate to HOS. Remove this entire method when singlecore is removed. // See notes in KScheduler::ScheduleImpl for more information about why this // is inaccurate. - - auto& kernel = system.Kernel(); - kernel.CurrentScheduler()->OnThreadStart(); - + kernel.CurrentScheduler()->OnThreadStart(kernel); while (true) { auto& physical_core = kernel.CurrentPhysicalCore(); if (!physical_core.IsInterrupted()) { physical_core.Idle(); } - - HandleInterrupt(); + HandleInterrupt(kernel); } } @@ -109,15 +103,14 @@ void CpuManager::MultiCoreRunIdleThread() { /// SingleCore /// /////////////////////////////////////////////////////////////////////////////// -void CpuManager::SingleCoreRunGuestThread() { - auto& kernel = system.Kernel(); +void CpuManager::SingleCoreRunGuestThread(Kernel::KernelCore& kernel) { auto* thread = Kernel::GetCurrentThreadPointer(kernel); - kernel.CurrentScheduler()->OnThreadStart(); + kernel.CurrentScheduler()->OnThreadStart(kernel); while (true) { auto* physical_core = &kernel.CurrentPhysicalCore(); if (!physical_core->IsInterrupted()) { - physical_core->RunThread(thread); + physical_core->RunThread(kernel, thread); physical_core = &kernel.CurrentPhysicalCore(); } @@ -125,26 +118,22 @@ void CpuManager::SingleCoreRunGuestThread() { system.CoreTiming().Advance(); kernel.SetIsPhantomModeForSingleCore(false); - PreemptSingleCore(); - HandleInterrupt(); + PreemptSingleCore(kernel); + HandleInterrupt(kernel); } } -void CpuManager::SingleCoreRunIdleThread() { - auto& kernel = system.Kernel(); - kernel.CurrentScheduler()->OnThreadStart(); - +void CpuManager::SingleCoreRunIdleThread(Kernel::KernelCore& kernel) { + kernel.CurrentScheduler()->OnThreadStart(kernel); while (true) { - PreemptSingleCore(false); + PreemptSingleCore(kernel, false); system.CoreTiming().AddTicks(1000U); idle_count++; - HandleInterrupt(); + HandleInterrupt(kernel); } } -void CpuManager::PreemptSingleCore(bool from_running_environment) { - auto& kernel = system.Kernel(); - +void CpuManager::PreemptSingleCore(Kernel::KernelCore& kernel, bool from_running_environment) { if (idle_count >= 4 || from_running_environment) { if (!from_running_environment) { system.CoreTiming().Idle(); @@ -156,7 +145,7 @@ void CpuManager::PreemptSingleCore(bool from_running_environment) { } current_core.store((current_core + 1) % Core::Hardware::NUM_CPU_CORES); system.CoreTiming().ResetTicks(); - kernel.Scheduler(current_core).PreemptSingleCore(); + kernel.Scheduler(current_core).PreemptSingleCore(kernel); // We've now been scheduled again, and we may have exchanged schedulers. // Reload the scheduler in case it's different. @@ -165,20 +154,16 @@ void CpuManager::PreemptSingleCore(bool from_running_environment) { } } -void CpuManager::GuestActivate() { +void CpuManager::GuestActivate(Kernel::KernelCore& kernel) { // Similar to the HorizonKernelMain callback in HOS - auto& kernel = system.Kernel(); auto* scheduler = kernel.CurrentScheduler(); - - scheduler->Activate(); + scheduler->Activate(kernel); UNREACHABLE(); } -void CpuManager::ShutdownThread() { - auto& kernel = system.Kernel(); +void CpuManager::ShutdownThread(Kernel::KernelCore& kernel) { auto* thread = kernel.GetCurrentEmuThread(); auto core = is_multicore ? kernel.CurrentPhysicalCoreIndex() : 0; - Common::Fiber::YieldTo(thread->GetHostContext(), *core_data[core].host_context); UNREACHABLE(); } diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h index a249dc5f76..5e97f51bfb 100644 --- a/src/core/cpu_manager.h +++ b/src/core/cpu_manager.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -14,6 +17,10 @@ #include "common/thread.h" #include "core/hardware_properties.h" +namespace Kernel { +class KernelCore; +} + namespace Common { class Event; class Fiber; @@ -51,57 +58,55 @@ public: void Initialize(); void Shutdown(); - std::function GetGuestActivateFunc() { - return [this] { GuestActivate(); }; + std::function GetGuestActivateFunc(Kernel::KernelCore& kernel) { + return [this, &kernel] { GuestActivate(kernel); }; } - std::function GetGuestThreadFunc() { - return [this] { GuestThreadFunction(); }; + std::function GetGuestThreadFunc(Kernel::KernelCore& kernel) { + return [this, &kernel] { GuestThreadFunction(kernel); }; } - std::function GetIdleThreadStartFunc() { - return [this] { IdleThreadFunction(); }; + std::function GetIdleThreadStartFunc(Kernel::KernelCore& kernel) { + return [this, &kernel] { IdleThreadFunction(kernel); }; } - std::function GetShutdownThreadStartFunc() { - return [this] { ShutdownThreadFunction(); }; + std::function GetShutdownThreadStartFunc(Kernel::KernelCore& kernel) { + return [this, &kernel] { ShutdownThreadFunction(kernel); }; } - void PreemptSingleCore(bool from_running_environment = true); + void PreemptSingleCore(Kernel::KernelCore& kernel, bool from_running_environment = true); std::size_t CurrentCore() const { return current_core.load(); } private: - void GuestThreadFunction(); - void IdleThreadFunction(); - void ShutdownThreadFunction(); + void GuestThreadFunction(Kernel::KernelCore& kernel); + void IdleThreadFunction(Kernel::KernelCore& kernel); + void ShutdownThreadFunction(Kernel::KernelCore& kernel); - void MultiCoreRunGuestThread(); - void MultiCoreRunIdleThread(); + void MultiCoreRunGuestThread(Kernel::KernelCore& kernel); + void MultiCoreRunIdleThread(Kernel::KernelCore& kernel); - void SingleCoreRunGuestThread(); - void SingleCoreRunIdleThread(); + void SingleCoreRunGuestThread(Kernel::KernelCore& kernel); + void SingleCoreRunIdleThread(Kernel::KernelCore& kernel); - void GuestActivate(); - void HandleInterrupt(); - void ShutdownThread(); + void GuestActivate(Kernel::KernelCore& kernel); + void HandleInterrupt(Kernel::KernelCore& kernel); + void ShutdownThread(Kernel::KernelCore& kernel); void RunThread(std::stop_token stop_token, std::size_t core); + static constexpr std::size_t max_cycle_runs = 5; + + std::optional gpu_barrier{}; struct CoreData { std::shared_ptr host_context; std::jthread host_thread; }; - - std::unique_ptr gpu_barrier{}; std::array core_data{}; - - bool is_async_gpu{}; - bool is_multicore{}; + Core::System& system; std::atomic current_core{}; std::size_t idle_count{}; std::size_t num_cores{}; - static constexpr std::size_t max_cycle_runs = 5; - - System& system; + bool is_async_gpu{}; + bool is_multicore{}; }; } // namespace Core diff --git a/src/core/debugger/debugger.cpp b/src/core/debugger/debugger.cpp index 3f089b7f80..d1c767721b 100644 --- a/src/core/debugger/debugger.cpp +++ b/src/core/debugger/debugger.cpp @@ -9,7 +9,7 @@ #include #include -#if BOOST_VERSION > 108400 && (!defined(_WINDOWS) && !defined(ANDROID)) || defined(YUZU_BOOST_v1) +#if BOOST_VERSION > 108400 && (!defined(_WINDOWS) && !defined(__ANDROID__)) || defined(YUZU_BOOST_v1) #define USE_BOOST_v1 #endif @@ -81,7 +81,10 @@ namespace Core { class DebuggerImpl : public DebuggerBackend { public: - explicit DebuggerImpl(Core::System& system_, u16 port) : system{system_} { + explicit DebuggerImpl(Core::System& system_, u16 port) + : system{system_} + , debug_process{system_.Kernel()} + { InitializeServer(port); } @@ -121,7 +124,7 @@ public: } void SetActiveThread(Kernel::KThread* thread) override { - state->active_thread = thread; + state->active_thread = {system.Kernel(), thread}; } Kernel::KThread* GetActiveThread() override { @@ -168,14 +171,7 @@ private: frontend = std::make_unique(*this, system, debug_process.GetPointerUnsafe()); // Set the new state. This will tear down any existing state. - state = ConnectionState{ - .client_socket{std::move(peer)}, - .signal_pipe{io_context}, - .info{}, - .active_thread{}, - .client_data{}, - .pipe_data{}, - }; + state.emplace(std::move(peer), io_context, system.Kernel()); // Set up the client signals for new data. AsyncReceiveInto(state->signal_pipe, state->pipe_data, [&](auto d) { PipeData(d); }); @@ -204,7 +200,7 @@ private: PauseEmulation(); // Notify the client. - state->active_thread = state->info.thread; + state->active_thread = {system.Kernel(), state->info.thread}; UpdateActiveThread(); if (state->info.type == SignalType::Watchpoint) { @@ -247,17 +243,19 @@ private: case DebuggerAction::Continue: MarkResumed([&] { ResumeEmulation(); }); break; - case DebuggerAction::StepThreadUnlocked: - MarkResumed([&] { - state->active_thread->SetStepState(Kernel::StepState::StepPending); - state->active_thread->Resume(Kernel::SuspendType::Debug); - ResumeEmulation(state->active_thread.GetPointerUnsafe()); + case DebuggerAction::ContinueThreads: { + auto* gdb = static_cast(frontend.get()); + MarkResumed([this, threads = std::move(gdb->resume_threads)] { + ResumeThreads(threads); }); break; - case DebuggerAction::StepThreadLocked: { - MarkResumed([&] { + } + case DebuggerAction::StepThread: { + auto* gdb = static_cast(frontend.get()); + MarkResumed([this, threads = std::move(gdb->resume_threads)] { state->active_thread->SetStepState(Kernel::StepState::StepPending); - state->active_thread->Resume(Kernel::SuspendType::Debug); + state->active_thread->Resume(system.Kernel(), Kernel::SuspendType::Debug); + ResumeThreads(threads, state->active_thread.GetPointerUnsafe()); }); break; } @@ -279,7 +277,7 @@ private: // Put all threads to sleep on next scheduler round. for (auto& thread : ThreadList()) { - thread.RequestSuspend(Kernel::SuspendType::Debug); + thread.RequestSuspend(system.Kernel(), Kernel::SuspendType::Debug); } } @@ -294,7 +292,23 @@ private: } thread.SetStepState(Kernel::StepState::NotStepping); - thread.Resume(Kernel::SuspendType::Debug); + thread.Resume(system.Kernel(), Kernel::SuspendType::Debug); + } + } + + void ResumeThreads(const std::vector& threads, + Kernel::KThread* except = nullptr) { + Kernel::KScopedLightLock ll{debug_process->GetListLock()}; + Kernel::KScopedSchedulerLock sl{system.Kernel()}; + + // Wake up only the specified threads. + for (auto* thread : threads) { + if (!thread || thread == except) { + continue; + } + + thread->SetStepState(Kernel::StepState::NotStepping); + thread->Resume(system.Kernel(), Kernel::SuspendType::Debug); } } @@ -314,7 +328,7 @@ private: return; } } - state->active_thread = std::addressof(threads.front()); + state->active_thread = {system.Kernel(), std::addressof(threads.front())}; } private: @@ -336,13 +350,20 @@ private: std::mutex connection_lock; struct ConnectionState { - boost::asio::ip::tcp::socket client_socket; #ifdef USE_BOOST_v1 - boost::process::v1::async_pipe signal_pipe; + using async_pipe = boost::process::v1::async_pipe; #else - boost::process::async_pipe signal_pipe; + using async_pipe = boost::process::async_pipe; #endif + ConnectionState(boost::asio::ip::tcp::socket&& client_socket_, async_pipe signal_pipe_, Kernel::KernelCore& kernel) + : client_socket{std::move(client_socket_)} + , signal_pipe{signal_pipe_} + , active_thread{kernel, nullptr} + {} + + boost::asio::ip::tcp::socket client_socket; + async_pipe signal_pipe; SignalInfo info; Kernel::KScopedAutoObject active_thread; std::array client_data; diff --git a/src/core/debugger/debugger_interface.h b/src/core/debugger/debugger_interface.h index 4e18749249..d48dd892da 100644 --- a/src/core/debugger/debugger_interface.h +++ b/src/core/debugger/debugger_interface.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project @@ -20,11 +20,11 @@ struct DebugWatchpoint; namespace Core { enum class DebuggerAction { - Interrupt, ///< Stop emulation as soon as possible. - Continue, ///< Resume emulation. - StepThreadLocked, ///< Step the currently-active thread without resuming others. - StepThreadUnlocked, ///< Step the currently-active thread and resume others. - ShutdownEmulation, ///< Shut down the emulator. + Interrupt, ///< Stop emulation as soon as possible. + Continue, ///< Resume emulation. + ContinueThreads, ///< Resume only specific threads (listed in frontend). + StepThread, ///< Step the active thread and resume only threads listed in frontend. + ShutdownEmulation, ///< Shut down the emulator. }; class DebuggerBackend { diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp index a5f49f6ff1..44ddf11c39 100644 --- a/src/core/debugger/gdbstub.cpp +++ b/src/core/debugger/gdbstub.cpp @@ -4,15 +4,15 @@ // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include #include +#include #include #include #include #include #include -#include - #include "common/hex_util.h" #include "common/logging.h" #include "common/scope_exit.h" @@ -273,10 +273,12 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vectorInsertWatchpoint(addr, size, Kernel::DebugWatchpointType::Write); + success = debug_process->InsertWatchpoint(system.Kernel(), addr, size, Kernel::DebugWatchpointType::Write); break; case BreakpointType::ReadWatch: - success = debug_process->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::Read); + success = debug_process->InsertWatchpoint(system.Kernel(), addr, size, Kernel::DebugWatchpointType::Read); break; case BreakpointType::AccessWatch: - success = debug_process->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::ReadOrWrite); + success = debug_process->InsertWatchpoint(system.Kernel(), addr, size, Kernel::DebugWatchpointType::ReadOrWrite); break; case BreakpointType::Hardware: default: @@ -366,13 +368,13 @@ void GDBStub::HandleBreakpointRemove(std::string_view sv) { break; } case BreakpointType::WriteWatch: - success = debug_process->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::Write); + success = debug_process->RemoveWatchpoint(system.Kernel(), addr, size, Kernel::DebugWatchpointType::Write); break; case BreakpointType::ReadWatch: - success = debug_process->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::Read); + success = debug_process->RemoveWatchpoint(system.Kernel(), addr, size, Kernel::DebugWatchpointType::Read); break; case BreakpointType::AccessWatch: - success = debug_process->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::ReadOrWrite); + success = debug_process->RemoveWatchpoint(system.Kernel(), addr, size, Kernel::DebugWatchpointType::ReadOrWrite); break; case BreakpointType::Hardware: default: @@ -467,29 +469,142 @@ void GDBStub::HandleQuery(std::string_view sv) { } void GDBStub::HandleVCont(std::string_view sv, std::vector& actions) { - // Continuing and stepping are supported (signal is ignored, but required for GDB to use vCont) + // Continuing and stepping are supported (signal is ignored, but required for GDB to use vCont). + // Reference: https://sourceware.org/gdb/current/onlinedocs/gdb.html/Packets.html#vCont-packet if (sv == "?") { SendReply("vCont;c;C;s;S"); - } else { - Kernel::KThread* stepped_thread = nullptr; - bool lock_execution = true; - std::vector entries; - boost::split(entries, sv.substr(1), boost::is_any_of(";")); - for (auto const& thread_action : entries) { - std::vector parts; - boost::split(parts, thread_action, boost::is_any_of(":")); - if (parts.size() == 1 && (parts[0] == "c" || parts[0].starts_with("C"))) - lock_execution = false; - if (parts.size() == 2 && (parts[0] == "s" || parts[0].starts_with("S"))) - stepped_thread = GetThreadByID(strtoll(parts[1].data(), nullptr, 16)); + return; + } + if (sv.empty() || sv.front() != ';') { + SendReply(GDB_STUB_REPLY_ERR); + return; + } + + enum class VContAction { + Continue, + Step, + }; + struct VContDirective { + VContAction action; + Kernel::KThread* thread{}; + bool all_threads{}; + + bool Matches(Kernel::KThread* candidate) const { + return all_threads || thread == candidate; + } + }; + + const auto is_hex_byte = [](std::string_view value) { + return value.size() == 2 && std::isxdigit(static_cast(value[0])) && + std::isxdigit(static_cast(value[1])); + }; + const auto is_hex_string = [](std::string_view value) { + return std::ranges::all_of(value, [](auto const c) { return std::isxdigit(int(c)); }); + }; + + resume_threads.clear(); + + std::vector directives; + std::string_view remaining = sv.substr(1); + while (!remaining.empty()) { + const auto entry_end = remaining.find(';'); + const auto entry = remaining.substr(0, entry_end); + remaining = entry_end == std::string_view::npos ? std::string_view{} : remaining.substr(entry_end + 1); + + if (entry.empty()) { + SendReply(GDB_STUB_REPLY_ERR); + return; } - if (stepped_thread) { - backend.SetActiveThread(stepped_thread); - actions.push_back(lock_execution ? DebuggerAction::StepThreadLocked : DebuggerAction::StepThreadUnlocked); - } else { - actions.push_back(DebuggerAction::Continue); + const auto thread_sep = entry.find(':'); + const auto action_token = entry.substr(0, thread_sep); + const auto thread_token = thread_sep == std::string_view::npos ? std::string_view{} : entry.substr(thread_sep + 1); + + if (action_token.empty()) { + SendReply(GDB_STUB_REPLY_ERR); + return; } + + VContDirective directive; + if (action_token == "c") { + directive.action = VContAction::Continue; + } else if (action_token.front() == 'C' && is_hex_byte(action_token.substr(1))) { + directive.action = VContAction::Continue; + } else if (action_token == "s") { + directive.action = VContAction::Step; + } else if (action_token.front() == 'S' && is_hex_byte(action_token.substr(1))) { + directive.action = VContAction::Step; + } else { + SendReply(GDB_STUB_REPLY_ERR); + return; + } + + if (thread_sep == std::string_view::npos || thread_token == "-1") { + directive.all_threads = true; + } else if (thread_token == "0") { + // A thread-id of 0 selects an arbitrary thread. While stopped, use the + // current active thread as that arbitrary choice. + directive.thread = backend.GetActiveThread(); + } else if (thread_token.starts_with('p')) { + // We do not currently support multiprocess thread selectors. + SendReply(GDB_STUB_REPLY_ERR); + return; + } else if (is_hex_string(thread_token)) { + directive.thread = GetThreadByID(strtoull(std::string(thread_token).c_str(), nullptr, 16)); + } else { + SendReply(GDB_STUB_REPLY_ERR); + return; + } + + directives.push_back(directive); + } + + if (directives.empty()) { + SendReply(GDB_STUB_REPLY_ERR); + return; + } + + // Resolve the packet exactly as specified by the protocol: for each thread, + // the leftmost action with a matching thread-id wins. + Kernel::KThread* stepped_thread = nullptr; + std::vector continue_threads; + auto& thread_list = debug_process->GetThreadList(); + for (auto& thread : thread_list) { + const auto directive = std::find_if(directives.begin(), directives.end(), + [&](const VContDirective& candidate) { + return candidate.Matches(std::addressof(thread)); + }); + if (directive == directives.end()) { + continue; + } + + switch (directive->action) { + case VContAction::Continue: + continue_threads.push_back(std::addressof(thread)); + break; + case VContAction::Step: + if (stepped_thread) { + // The core can step at most one thread at a time. + SendReply(GDB_STUB_REPLY_ERR); + return; + } + stepped_thread = std::addressof(thread); + break; + } + } + + if (stepped_thread) { + backend.SetActiveThread(stepped_thread); + resume_threads = std::move(continue_threads); + actions.push_back(DebuggerAction::StepThread); + } else if (continue_threads.size() == thread_list.size()) { + actions.push_back(DebuggerAction::Continue); + } else if (!continue_threads.empty()) { + resume_threads = std::move(continue_threads); + actions.push_back(DebuggerAction::ContinueThreads); + } else { + // A resume packet that leaves all threads stopped is not useful to execute. + SendReply(GDB_STUB_REPLY_ERR); } } diff --git a/src/core/debugger/gdbstub.h b/src/core/debugger/gdbstub.h index 140b0e8e25..5103536338 100644 --- a/src/core/debugger/gdbstub.h +++ b/src/core/debugger/gdbstub.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project @@ -52,6 +52,7 @@ struct GDBStub : public DebuggerFrontend { std::unique_ptr arch; std::vector current_command; std::map replaced_instructions; + std::vector resume_threads; bool no_ack{}; }; diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp index f86d22d627..2e182f1749 100644 --- a/src/core/file_sys/control_metadata.cpp +++ b/src/core/file_sys/control_metadata.cpp @@ -128,25 +128,32 @@ const LanguageEntry& NACP::GetLanguageEntry() const { case Settings::Language::Russian: return Language::Russian; case Settings::Language::Spanish: return Language::Spanish; case Settings::Language::SpanishLatin: return Language::LatinAmericanSpanish; - case Settings::Language::Taiwanese: return Language::SimplifiedChinese; + case Settings::Language::Taiwanese: return Language::TraditionalChinese; case Settings::Language::Thai: return Language::Thai; case Settings::Language::Polish: return Language::Polish; default: return Language::AmericanEnglish; } }(); - u32 index = u32(language); + const auto index = static_cast(language); - if (index < language_entries.size() && !language_entries[index].GetApplicationName().empty()) { + if (index < language_entries.size() && + !language_entries[index].GetApplicationName().empty()) { return language_entries[index]; } for (const auto& entry : language_entries) { - if (!entry.GetApplicationName().empty()) - return entry; + if (!entry.GetApplicationName().empty()) { + return entry; + } } - return language_entries.at(static_cast(Language::AmericanEnglish)); + if (!language_entries.empty()) { + return language_entries.front(); + } + + static const LanguageEntry empty_entry{}; + return empty_entry; } std::vector NACP::GetApplicationNames() const { diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp index af41820a36..ebecbaf74f 100644 --- a/src/core/file_sys/registered_cache.cpp +++ b/src/core/file_sys/registered_cache.cpp @@ -503,6 +503,9 @@ NcaID PlaceholderCache::Generate() { VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& open_dir, std::string_view path) const { + if (open_dir == nullptr) { + return nullptr; + } const auto file = open_dir->GetFileRelative(path); if (file != nullptr) { return file; diff --git a/src/core/file_sys/system_archive/time_zone_binary.cpp b/src/core/file_sys/system_archive/time_zone_binary.cpp index 316ff0dc6f..9b79b6fcd3 100644 --- a/src/core/file_sys/system_archive/time_zone_binary.cpp +++ b/src/core/file_sys/system_archive/time_zone_binary.cpp @@ -1,86 +1,48 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include #include #include "common/swap.h" #include "core/file_sys/system_archive/time_zone_binary.h" +#include "core/file_sys/vfs/vfs_static.h" +#include "core/file_sys/vfs/vfs_types.h" #include "core/file_sys/vfs/vfs_vector.h" #include "nx_tzdb.h" namespace FileSys::SystemArchive { -const static std::map>&> - tzdb_zoneinfo_dirs = {{"Africa", NxTzdb::africa}, - {"America", NxTzdb::america}, - {"Antarctica", NxTzdb::antarctica}, - {"Arctic", NxTzdb::arctic}, - {"Asia", NxTzdb::asia}, - {"Atlantic", NxTzdb::atlantic}, - {"Australia", NxTzdb::australia}, - {"Brazil", NxTzdb::brazil}, - {"Canada", NxTzdb::canada}, - {"Chile", NxTzdb::chile}, - {"Etc", NxTzdb::etc}, - {"Europe", NxTzdb::europe}, - {"Indian", NxTzdb::indian}, - {"Mexico", NxTzdb::mexico}, - {"Pacific", NxTzdb::pacific}, - {"US", NxTzdb::us}}; - -const static std::map>&> - tzdb_america_dirs = {{"Argentina", NxTzdb::america_argentina}, - {"Indiana", NxTzdb::america_indiana}, - {"Kentucky", NxTzdb::america_kentucky}, - {"North_Dakota", NxTzdb::america_north_dakota}}; - -static void GenerateFiles(std::vector& directory, - const std::map>& files) { - for (const auto& [filename, data] : files) { - const auto data_copy{data}; - const std::string filename_copy{filename}; - VirtualFile file{ - std::make_shared(std::move(data_copy), std::move(filename_copy))}; - directory.push_back(file); - } -} - -static std::vector GenerateZoneinfoFiles() { - std::vector zoneinfo_files; - GenerateFiles(zoneinfo_files, NxTzdb::zoneinfo); - return zoneinfo_files; -} - VirtualDir TimeZoneBinary() { std::vector america_sub_dirs; - for (const auto& [dir_name, files] : tzdb_america_dirs) { - std::vector vfs_files; - GenerateFiles(vfs_files, files); - america_sub_dirs.push_back(std::make_shared( - std::move(vfs_files), std::vector{}, dir_name)); - } - + america_sub_dirs.push_back(std::make_shared(NxTzdb::CollectFiles_america_argentina(), std::vector{}, "Argentina")); + america_sub_dirs.push_back(std::make_shared(NxTzdb::CollectFiles_america_indiana(), std::vector{}, "Indiana")); + america_sub_dirs.push_back(std::make_shared(NxTzdb::CollectFiles_america_kentucky(), std::vector{}, "Kentucky")); + america_sub_dirs.push_back(std::make_shared(NxTzdb::CollectFiles_america_north_dakota(), std::vector{}, "North_Dakota")); std::vector zoneinfo_sub_dirs; - for (const auto& [dir_name, files] : tzdb_zoneinfo_dirs) { - std::vector vfs_files; - GenerateFiles(vfs_files, files); - if (dir_name == "America") { - zoneinfo_sub_dirs.push_back(std::make_shared( - std::move(vfs_files), std::move(america_sub_dirs), dir_name)); - } else { - zoneinfo_sub_dirs.push_back(std::make_shared( - std::move(vfs_files), std::vector{}, dir_name)); - } - } - - std::vector zoneinfo_dir{std::make_shared( - GenerateZoneinfoFiles(), std::move(zoneinfo_sub_dirs), "zoneinfo")}; - std::vector root_files; - GenerateFiles(root_files, NxTzdb::base); - - return std::make_shared(std::move(root_files), std::move(zoneinfo_dir), - "data"); + zoneinfo_sub_dirs.push_back(std::make_shared(NxTzdb::CollectFiles_africa(), std::vector{}, "Africa")); + zoneinfo_sub_dirs.push_back(std::make_shared(NxTzdb::CollectFiles_america(), std::move(america_sub_dirs), "America")); + zoneinfo_sub_dirs.push_back(std::make_shared(NxTzdb::CollectFiles_antarctica(), std::vector{}, "Antarctica")); + zoneinfo_sub_dirs.push_back(std::make_shared(NxTzdb::CollectFiles_arctic(), std::vector{}, "Arctic")); + zoneinfo_sub_dirs.push_back(std::make_shared(NxTzdb::CollectFiles_asia(), std::vector{}, "Asia")); + zoneinfo_sub_dirs.push_back(std::make_shared(NxTzdb::CollectFiles_atlantic(), std::vector{}, "Atlantic")); + zoneinfo_sub_dirs.push_back(std::make_shared(NxTzdb::CollectFiles_australia(), std::vector{}, "Australia")); + zoneinfo_sub_dirs.push_back(std::make_shared(NxTzdb::CollectFiles_brazil(), std::vector{}, "Brazil")); + zoneinfo_sub_dirs.push_back(std::make_shared(NxTzdb::CollectFiles_canada(), std::vector{}, "Canada")); + zoneinfo_sub_dirs.push_back(std::make_shared(NxTzdb::CollectFiles_chile(), std::vector{}, "Chile")); + zoneinfo_sub_dirs.push_back(std::make_shared(NxTzdb::CollectFiles_etc(), std::vector{}, "Etc")); + zoneinfo_sub_dirs.push_back(std::make_shared(NxTzdb::CollectFiles_europe(), std::vector{}, "Europe")); + zoneinfo_sub_dirs.push_back(std::make_shared(NxTzdb::CollectFiles_indian(), std::vector{}, "Indian")); + zoneinfo_sub_dirs.push_back(std::make_shared(NxTzdb::CollectFiles_mexico(), std::vector{}, "Mexico")); + zoneinfo_sub_dirs.push_back(std::make_shared(NxTzdb::CollectFiles_pacific(), std::vector{}, "Pacific")); + zoneinfo_sub_dirs.push_back(std::make_shared(NxTzdb::CollectFiles_us(), std::vector{}, "US")); + std::vector zoneinfo_dir{std::make_shared(NxTzdb::CollectFiles_zoneinfo(), std::move(zoneinfo_sub_dirs), "zoneinfo")}; + // last files (root) + return std::make_shared(NxTzdb::CollectFiles_base(), std::move(zoneinfo_dir), "data"); } } // namespace FileSys::SystemArchive diff --git a/src/core/file_sys/vfs/vfs.h b/src/core/file_sys/vfs/vfs.h index f846a9669c..c3d73364bc 100644 --- a/src/core/file_sys/vfs/vfs.h +++ b/src/core/file_sys/vfs/vfs.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -16,6 +19,12 @@ #include "core/file_sys/fs_filesystem.h" #include "core/file_sys/vfs/vfs_types.h" +#undef CreateFile +#undef CopyFile +#undef MoveFile +#undef DeleteFile +#undef CreateDirectory + namespace FileSys { // An enumeration representing what can be at the end of a path in a VfsFilesystem diff --git a/src/core/file_sys/vfs/vfs_real.cpp b/src/core/file_sys/vfs/vfs_real.cpp index 1eafff6c06..887973d305 100644 --- a/src/core/file_sys/vfs/vfs_real.cpp +++ b/src/core/file_sys/vfs/vfs_real.cpp @@ -23,7 +23,7 @@ #define stat _stat64 #endif -#ifdef ANDROID +#ifdef __ANDROID__ #include "common/fs/fs_android.h" #endif @@ -33,7 +33,7 @@ namespace FS = Common::FS; namespace { -constexpr size_t MaxOpenFiles = 512; +constexpr size_t MaxOpenFiles = 8192; constexpr FS::FileAccessMode ModeFlagsToFileAccessMode(OpenMode mode) { switch (mode) { @@ -288,7 +288,7 @@ RealVfsFile::~RealVfsFile() { } std::string RealVfsFile::GetName() const { -#ifdef ANDROID +#ifdef __ANDROID__ if (path[0] != '/') { return FS::Android::GetFilename(path); } diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h index 416da15ecb..9cd2908823 100644 --- a/src/core/hle/ipc.h +++ b/src/core/hle/ipc.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2016 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -13,6 +16,16 @@ namespace IPC { /// Size of the command buffer area, in 32-bit words. constexpr std::size_t COMMAND_BUFFER_LENGTH = 0x100 / sizeof(u32); +/// Must match bitfields +constexpr std::size_t MAX_BUFFER_DESCRIPTORS = 16; +constexpr std::size_t MAX_INCOMING_MOVE_HANDLERS = 16; +constexpr std::size_t MAX_INCOMING_COPY_HANDLERS = 16; + +/// Doesn't need to match bitfields but usually not big enough +constexpr std::size_t MAX_OUTGOING_COPY_OBJECTS = 16; +constexpr std::size_t MAX_OUTGOING_MOVE_OBJECTS = 16; +constexpr std::size_t MAX_OUTGOING_DOMAIN_OBJECTS = 16; + enum class ControlCommand : u32 { ConvertSessionToDomain = 0, ConvertDomainToSession = 1, diff --git a/src/core/hle/kernel/global_scheduler_context.cpp b/src/core/hle/kernel/global_scheduler_context.cpp index 855a8226ef..6d1cd030e0 100644 --- a/src/core/hle/kernel/global_scheduler_context.cpp +++ b/src/core/hle/kernel/global_scheduler_context.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project @@ -17,7 +17,8 @@ namespace Kernel { GlobalSchedulerContext::GlobalSchedulerContext(KernelCore& kernel) - : m_kernel{kernel}, m_scheduler_lock{kernel} {} + : m_scheduler_lock{kernel} +{} GlobalSchedulerContext::~GlobalSchedulerContext() = default; @@ -37,7 +38,7 @@ void GlobalSchedulerContext::RemoveThread(KThread* thread) noexcept { /// and then does some core rebalancing. Preemption priorities can be found /// in the array 'preemption_priorities'. /// @note This operation happens every 10ms. -void GlobalSchedulerContext::PreemptThreads() noexcept { +void GlobalSchedulerContext::PreemptThreads(KernelCore& kernel) noexcept { // The priority levels at which the global scheduler preempts threads every 10 ms. They are // ordered from Core 0 to Core 3. static constexpr std::array per_core{ @@ -46,9 +47,9 @@ void GlobalSchedulerContext::PreemptThreads() noexcept { 59, 63, }; - ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel)); for (u32 core_id = 0; core_id < per_core.size(); core_id++) - KScheduler::RotateScheduledQueue(m_kernel, core_id, per_core[core_id]); + KScheduler::RotateScheduledQueue(kernel, core_id, per_core[core_id]); } /// @brief Returns true if the global scheduler lock is acquired @@ -69,11 +70,11 @@ void GlobalSchedulerContext::UnregisterDummyThreadForWakeup(KThread* thread) noe } } -void GlobalSchedulerContext::WakeupWaitingDummyThreads() noexcept { +void GlobalSchedulerContext::WakeupWaitingDummyThreads(KernelCore& kernel) noexcept { ASSERT(this->IsLocked()); if (m_woken_dummy_threads.size() > 0) { for (auto* thread : m_woken_dummy_threads) - thread->DummyThreadEndWait(); + thread->DummyThreadEndWait(kernel); m_woken_dummy_threads.clear(); } } diff --git a/src/core/hle/kernel/global_scheduler_context.h b/src/core/hle/kernel/global_scheduler_context.h index 64aee86af8..dd53477d2d 100644 --- a/src/core/hle/kernel/global_scheduler_context.h +++ b/src/core/hle/kernel/global_scheduler_context.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project @@ -50,17 +50,16 @@ public: } void AddThread(KThread* thread) noexcept; void RemoveThread(KThread* thread) noexcept; - void PreemptThreads() noexcept; + void PreemptThreads(KernelCore& kernel) noexcept; bool IsLocked() const noexcept; void UnregisterDummyThreadForWakeup(KThread* thread) noexcept; void RegisterDummyThreadForWakeup(KThread* thread) noexcept; - void WakeupWaitingDummyThreads() noexcept; + void WakeupWaitingDummyThreads(KernelCore& kernel) noexcept; private: friend class KScopedSchedulerLock; friend class KScopedSchedulerLockAndSleep; - KernelCore& m_kernel; std::atomic_bool m_scheduler_update_needed{}; KSchedulerPriorityQueue m_priority_queue; LockType m_scheduler_lock; diff --git a/src/core/hle/kernel/init/init_slab_setup.cpp b/src/core/hle/kernel/init/init_slab_setup.cpp index a0e20bbbb4..5dd8afaf96 100644 --- a/src/core/hle/kernel/init/init_slab_setup.cpp +++ b/src/core/hle/kernel/init/init_slab_setup.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -281,7 +284,7 @@ void KPageBufferSlabHeap::Initialize(Core::System& system) { // Reserve memory from the system resource limit. ASSERT( - kernel.GetSystemResourceLimit()->Reserve(LimitableResource::PhysicalMemoryMax, slab_size)); + kernel.GetSystemResourceLimit()->Reserve(kernel, LimitableResource::PhysicalMemoryMax, slab_size)); // Allocate memory for the slab. constexpr auto AllocateOption = KMemoryManager::EncodeOption( diff --git a/src/core/hle/kernel/k_address_arbiter.cpp b/src/core/hle/kernel/k_address_arbiter.cpp index 48889253dc..02634f8721 100644 --- a/src/core/hle/kernel/k_address_arbiter.cpp +++ b/src/core/hle/kernel/k_address_arbiter.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -16,8 +19,9 @@ namespace Kernel { -KAddressArbiter::KAddressArbiter(Core::System& system) - : m_system{system}, m_kernel{system.Kernel()} {} +KAddressArbiter::KAddressArbiter(Core::System& system_) + : system{system_} +{} KAddressArbiter::~KAddressArbiter() = default; namespace { @@ -112,7 +116,7 @@ public: explicit ThreadQueueImplForKAddressArbiter(KernelCore& kernel, KAddressArbiter::ThreadTree* t) : KThreadQueue(kernel), m_tree(t) {} - void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override { + void CancelWait(KernelCore& kernel, KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override { // If the thread is waiting on an address arbiter, remove it from the tree. if (waiting_thread->IsWaitingForAddressArbiter()) { m_tree->erase(m_tree->iterator_to(*waiting_thread)); @@ -120,7 +124,7 @@ public: } // Invoke the base cancel wait handler. - KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task); + KThreadQueue::CancelWait(kernel, waiting_thread, wait_result, cancel_timer_task); } private: @@ -133,14 +137,14 @@ Result KAddressArbiter::Signal(uint64_t addr, s32 count) { // Perform signaling. s32 num_waiters{}; { - KScopedSchedulerLock sl(m_kernel); + KScopedSchedulerLock sl(system.Kernel()); auto it = m_tree.nfind_key({addr, -1}); while ((it != m_tree.end()) && (count <= 0 || num_waiters < count) && (it->GetAddressArbiterKey() == addr)) { // End the thread's wait. KThread* target_thread = std::addressof(*it); - target_thread->EndWait(ResultSuccess); + target_thread->EndWait(system.Kernel(), ResultSuccess); ASSERT(target_thread->IsWaitingForAddressArbiter()); target_thread->ClearAddressArbiter(); @@ -156,11 +160,11 @@ Result KAddressArbiter::SignalAndIncrementIfEqual(uint64_t addr, s32 value, s32 // Perform signaling. s32 num_waiters{}; { - KScopedSchedulerLock sl(m_kernel); + KScopedSchedulerLock sl(system.Kernel()); // Check the userspace value. s32 user_value{}; - R_UNLESS(UpdateIfEqual(m_kernel, std::addressof(user_value), addr, value, value + 1), + R_UNLESS(UpdateIfEqual(system.Kernel(), std::addressof(user_value), addr, value, value + 1), ResultInvalidCurrentMemory); R_UNLESS(user_value == value, ResultInvalidState); @@ -169,7 +173,7 @@ Result KAddressArbiter::SignalAndIncrementIfEqual(uint64_t addr, s32 value, s32 (it->GetAddressArbiterKey() == addr)) { // End the thread's wait. KThread* target_thread = std::addressof(*it); - target_thread->EndWait(ResultSuccess); + target_thread->EndWait(system.Kernel(), ResultSuccess); ASSERT(target_thread->IsWaitingForAddressArbiter()); target_thread->ClearAddressArbiter(); @@ -185,7 +189,7 @@ Result KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(uint64_t addr, s32 // Perform signaling. s32 num_waiters{}; { - KScopedSchedulerLock sl(m_kernel); + KScopedSchedulerLock sl(system.Kernel()); auto it = m_tree.nfind_key({addr, -1}); // Determine the updated value. @@ -220,9 +224,9 @@ Result KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(uint64_t addr, s32 s32 user_value{}; bool succeeded{}; if (value != new_value) { - succeeded = UpdateIfEqual(m_kernel, std::addressof(user_value), addr, value, new_value); + succeeded = UpdateIfEqual(system.Kernel(), std::addressof(user_value), addr, value, new_value); } else { - succeeded = ReadFromUser(m_kernel, std::addressof(user_value), addr); + succeeded = ReadFromUser(system.Kernel(), std::addressof(user_value), addr); } R_UNLESS(succeeded, ResultInvalidCurrentMemory); @@ -232,7 +236,7 @@ Result KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(uint64_t addr, s32 (it->GetAddressArbiterKey() == addr)) { // End the thread's wait. KThread* target_thread = std::addressof(*it); - target_thread->EndWait(ResultSuccess); + target_thread->EndWait(system.Kernel(), ResultSuccess); ASSERT(target_thread->IsWaitingForAddressArbiter()); target_thread->ClearAddressArbiter(); @@ -246,12 +250,12 @@ Result KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(uint64_t addr, s32 Result KAddressArbiter::WaitIfLessThan(uint64_t addr, s32 value, bool decrement, s64 timeout) { // Prepare to wait. - KThread* cur_thread = GetCurrentThreadPointer(m_kernel); + KThread* cur_thread = GetCurrentThreadPointer(system.Kernel()); KHardwareTimer* timer{}; - ThreadQueueImplForKAddressArbiter wait_queue(m_kernel, std::addressof(m_tree)); + ThreadQueueImplForKAddressArbiter wait_queue(system.Kernel(), std::addressof(m_tree)); { - KScopedSchedulerLockAndSleep slp{m_kernel, std::addressof(timer), cur_thread, timeout}; + KScopedSchedulerLockAndSleep slp{system.Kernel(), std::addressof(timer), cur_thread, timeout}; // Check that the thread isn't terminating. if (cur_thread->IsTerminationRequested()) { @@ -263,9 +267,9 @@ Result KAddressArbiter::WaitIfLessThan(uint64_t addr, s32 value, bool decrement, s32 user_value{}; bool succeeded{}; if (decrement) { - succeeded = DecrementIfLessThan(m_kernel, std::addressof(user_value), addr, value); + succeeded = DecrementIfLessThan(system.Kernel(), std::addressof(user_value), addr, value); } else { - succeeded = ReadFromUser(m_kernel, std::addressof(user_value), addr); + succeeded = ReadFromUser(system.Kernel(), std::addressof(user_value), addr); } if (!succeeded) { @@ -291,7 +295,7 @@ Result KAddressArbiter::WaitIfLessThan(uint64_t addr, s32 value, bool decrement, // Wait for the thread to finish. wait_queue.SetHardwareTimer(timer); - cur_thread->BeginWait(std::addressof(wait_queue)); + cur_thread->BeginWait(system.Kernel(), std::addressof(wait_queue)); cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration); } @@ -301,12 +305,12 @@ Result KAddressArbiter::WaitIfLessThan(uint64_t addr, s32 value, bool decrement, Result KAddressArbiter::WaitIfEqual(uint64_t addr, s32 value, s64 timeout) { // Prepare to wait. - KThread* cur_thread = GetCurrentThreadPointer(m_kernel); + KThread* cur_thread = GetCurrentThreadPointer(system.Kernel()); KHardwareTimer* timer{}; - ThreadQueueImplForKAddressArbiter wait_queue(m_kernel, std::addressof(m_tree)); + ThreadQueueImplForKAddressArbiter wait_queue(system.Kernel(), std::addressof(m_tree)); { - KScopedSchedulerLockAndSleep slp{m_kernel, std::addressof(timer), cur_thread, timeout}; + KScopedSchedulerLockAndSleep slp{system.Kernel(), std::addressof(timer), cur_thread, timeout}; // Check that the thread isn't terminating. if (cur_thread->IsTerminationRequested()) { @@ -316,7 +320,7 @@ Result KAddressArbiter::WaitIfEqual(uint64_t addr, s32 value, s64 timeout) { // Read the value from userspace. s32 user_value{}; - if (!ReadFromUser(m_kernel, std::addressof(user_value), addr)) { + if (!ReadFromUser(system.Kernel(), std::addressof(user_value), addr)) { slp.CancelSleep(); R_THROW(ResultInvalidCurrentMemory); } @@ -339,7 +343,7 @@ Result KAddressArbiter::WaitIfEqual(uint64_t addr, s32 value, s64 timeout) { // Wait for the thread to finish. wait_queue.SetHardwareTimer(timer); - cur_thread->BeginWait(std::addressof(wait_queue)); + cur_thread->BeginWait(system.Kernel(), std::addressof(wait_queue)); cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration); } diff --git a/src/core/hle/kernel/k_address_arbiter.h b/src/core/hle/kernel/k_address_arbiter.h index 3b70e1ab28..b3afe19476 100644 --- a/src/core/hle/kernel/k_address_arbiter.h +++ b/src/core/hle/kernel/k_address_arbiter.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -60,8 +63,7 @@ private: private: ThreadTree m_tree; - Core::System& m_system; - KernelCore& m_kernel; + Core::System& system; }; } // namespace Kernel diff --git a/src/core/hle/kernel/k_auto_object.cpp b/src/core/hle/kernel/k_auto_object.cpp index a7a46d29bb..14d1c4624d 100644 --- a/src/core/hle/kernel/k_auto_object.cpp +++ b/src/core/hle/kernel/k_auto_object.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project @@ -15,8 +15,8 @@ KAutoObject* KAutoObject::Create(KAutoObject* obj) { return obj; } -void KAutoObject::RegisterWithKernel() { - m_kernel.RegisterKernelObject(this); +void KAutoObject::RegisterWithKernel(KernelCore& kernel) { + kernel.RegisterKernelObject(this); } void KAutoObject::UnregisterWithKernel(KernelCore& kernel, KAutoObject* self) { diff --git a/src/core/hle/kernel/k_auto_object.h b/src/core/hle/kernel/k_auto_object.h index 5e8c0030ce..392b9c9f1d 100644 --- a/src/core/hle/kernel/k_auto_object.h +++ b/src/core/hle/kernel/k_auto_object.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project @@ -87,21 +87,21 @@ private: KERNEL_AUTOOBJECT_TRAITS_IMPL(KAutoObject, KAutoObject, const); public: - explicit KAutoObject(KernelCore& kernel) : m_kernel(kernel) { + explicit KAutoObject(KernelCore& kernel) { m_class_token = GetStaticTypeObj().GetClassToken(); - RegisterWithKernel(); + RegisterWithKernel(kernel); } virtual ~KAutoObject() = default; static KAutoObject* Create(KAutoObject* ptr); // Destroy is responsible for destroying the auto object's resources when ref_count hits zero. - virtual void Destroy() { + virtual void Destroy(KernelCore& kernel) { UNIMPLEMENTED(); } // Finalize is responsible for cleaning up resource, but does not destroy the object. - virtual void Finalize() {} + virtual void Finalize(KernelCore& kernel) {} virtual KProcess* GetOwner() const { return nullptr; @@ -123,67 +123,50 @@ public: Derived DynamicCast() { static_assert(std::is_pointer_v); using DerivedType = std::remove_pointer_t; - - if (this->IsDerivedFrom(DerivedType::GetStaticTypeObj())) { - return static_cast(this); - } else { - return nullptr; - } + if (this->IsDerivedFrom(DerivedType::GetStaticTypeObj())) + return Derived(this); + return nullptr; } template const Derived DynamicCast() const { static_assert(std::is_pointer_v); using DerivedType = std::remove_pointer_t; - - if (this->IsDerivedFrom(DerivedType::GetStaticTypeObj())) { - return static_cast(this); - } else { - return nullptr; - } + if (this->IsDerivedFrom(DerivedType::GetStaticTypeObj())) + return Derived(this); + return nullptr; } - bool Open() { + bool Open(KernelCore& kernel) { // Atomically increment the reference count, only if it's positive. u32 cur_ref_count = m_ref_count.load(std::memory_order_acquire); do { - if (cur_ref_count == 0) { + if (cur_ref_count == 0) return false; - } ASSERT(cur_ref_count < cur_ref_count + 1); - } while (!m_ref_count.compare_exchange_weak(cur_ref_count, cur_ref_count + 1, - std::memory_order_relaxed)); - + } while (!m_ref_count.compare_exchange_weak(cur_ref_count, cur_ref_count + 1, std::memory_order_relaxed)); return true; } - void Close() { + void Close(KernelCore& kernel) { // Atomically decrement the reference count, not allowing it to become negative. u32 cur_ref_count = m_ref_count.load(std::memory_order_acquire); do { - if (cur_ref_count == 0) { + if (cur_ref_count == 0) return; - } ASSERT(cur_ref_count > 0); - } while (!m_ref_count.compare_exchange_weak(cur_ref_count, cur_ref_count - 1, - std::memory_order_acq_rel)); - + } while (!m_ref_count.compare_exchange_weak(cur_ref_count, cur_ref_count - 1, std::memory_order_acq_rel)); // If ref count hits 1, destroy the object. if (cur_ref_count == 1) { - KernelCore& kernel = m_kernel; - this->Destroy(); + this->Destroy(kernel); KAutoObject::UnregisterWithKernel(kernel, this); } } private: - void RegisterWithKernel(); + void RegisterWithKernel(KernelCore& kernel); static void UnregisterWithKernel(KernelCore& kernel, KAutoObject* self); -protected: - KernelCore& m_kernel; - -private: std::atomic m_ref_count{}; ClassTokenType m_class_token{}; }; @@ -225,17 +208,22 @@ class KScopedAutoObject { public: YUZU_NON_COPYABLE(KScopedAutoObject); - constexpr KScopedAutoObject() = default; + constexpr KScopedAutoObject(KernelCore& kernel_) + : kernel{kernel_} + {} - constexpr KScopedAutoObject(T* o) : m_obj(o) { + constexpr KScopedAutoObject(KernelCore& kernel_, T* o) + : kernel{kernel_} + , m_obj(o) + { if (m_obj != nullptr) { - m_obj->Open(); + m_obj->Open(kernel); } } ~KScopedAutoObject() { if (m_obj != nullptr) { - m_obj->Close(); + m_obj->Close(kernel); } m_obj = nullptr; } @@ -253,7 +241,7 @@ public: if (rhs.m_obj != nullptr) { derived = rhs.m_obj->template DynamicCast(); if (derived == nullptr) { - rhs.m_obj->Close(); + rhs.m_obj->Close(rhs.kernel); } } @@ -274,8 +262,16 @@ public: return *m_obj; } + constexpr void SetObject(T* o) { + if (m_obj) + m_obj->Close(kernel); + m_obj = o; + if (m_obj) + m_obj->Open(kernel); + } + constexpr void Reset(T* o) { - KScopedAutoObject(o).Swap(*this); + KScopedAutoObject(kernel, o).Swap(*this); } constexpr T* GetPointerUnsafe() { @@ -304,6 +300,7 @@ private: friend class KScopedAutoObject; private: + KernelCore& kernel; T* m_obj{}; private: diff --git a/src/core/hle/kernel/k_capabilities.cpp b/src/core/hle/kernel/k_capabilities.cpp index d2288c30d1..90f41dd1fc 100644 --- a/src/core/hle/kernel/k_capabilities.cpp +++ b/src/core/hle/kernel/k_capabilities.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -268,7 +271,8 @@ Result KCapabilities::SetHandleTableCapability(const u32 cap) { Result KCapabilities::SetDebugFlagsCapability(const u32 cap) { // Validate. const DebugFlags pack{cap}; - R_UNLESS(pack.reserved == 0, ResultReservedUsed); + // TODO: Enabling this breaks compatibility with HBloader and such + //R_UNLESS(pack.reserved == 0, ResultReservedUsed); DebugFlags debug_capabilities{m_debug_capabilities}; debug_capabilities.allow_debug.Assign(pack.allow_debug); diff --git a/src/core/hle/kernel/k_client_port.cpp b/src/core/hle/kernel/k_client_port.cpp index 68cea978a0..d13995988c 100644 --- a/src/core/hle/kernel/k_client_port.cpp +++ b/src/core/hle/kernel/k_client_port.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2021 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -15,7 +18,7 @@ namespace Kernel { KClientPort::KClientPort(KernelCore& kernel) : KSynchronizationObject{kernel} {} KClientPort::~KClientPort() = default; -void KClientPort::Initialize(KPort* parent, s32 max_sessions) { +void KClientPort::Initialize(KernelCore& kernel, KPort* parent, s32 max_sessions) { // Set member variables. m_num_sessions = 0; m_peak_sessions = 0; @@ -23,48 +26,48 @@ void KClientPort::Initialize(KPort* parent, s32 max_sessions) { m_max_sessions = max_sessions; } -void KClientPort::OnSessionFinalized() { - KScopedSchedulerLock sl{m_kernel}; +void KClientPort::OnSessionFinalized(KernelCore& kernel) { + KScopedSchedulerLock sl{kernel}; if (const auto prev = m_num_sessions--; prev == m_max_sessions) { - this->NotifyAvailable(); + this->NotifyAvailable(kernel); } } -void KClientPort::OnServerClosed() {} +void KClientPort::OnServerClosed(KernelCore& kernel) {} -bool KClientPort::IsLight() const { +bool KClientPort::IsLight(KernelCore& kernel) const { return this->GetParent()->IsLight(); } -bool KClientPort::IsServerClosed() const { - return this->GetParent()->IsServerClosed(); +bool KClientPort::IsServerClosed(KernelCore& kernel) const { + return this->GetParent()->IsServerClosed(kernel); } -void KClientPort::Destroy() { +void KClientPort::Destroy(KernelCore& kernel) { // Note with our parent that we're closed. - m_parent->OnClientClosed(); + m_parent->OnClientClosed(kernel); // Close our reference to our parent. - m_parent->Close(); + m_parent->Close(kernel); } -bool KClientPort::IsSignaled() const { +bool KClientPort::IsSignaled(KernelCore& kernel) const { return m_num_sessions.load() < m_max_sessions; } -Result KClientPort::CreateSession(KClientSession** out) { +Result KClientPort::CreateSession(KernelCore& kernel, KClientSession** out) { // Declare the session we're going to allocate. KSession* session{}; // Reserve a new session from the resource limit. - KScopedResourceReservation session_reservation(GetCurrentProcessPointer(m_kernel), + KScopedResourceReservation session_reservation(kernel, GetCurrentProcessPointer(kernel), LimitableResource::SessionCountMax); R_UNLESS(session_reservation.Succeeded(), ResultLimitReached); // Allocate a session normally. // TODO: Dynamic resource limits - session = KSession::Create(m_kernel); + session = KSession::Create(kernel); // Check that we successfully created a session. R_UNLESS(session != nullptr, ResultOutOfResource); @@ -72,7 +75,7 @@ Result KClientPort::CreateSession(KClientSession** out) { // Update the session counts. { ON_RESULT_FAILURE { - session->Close(); + session->Close(kernel); }; // Atomically increment the number of sessions. @@ -100,38 +103,37 @@ Result KClientPort::CreateSession(KClientSession** out) { } // Initialize the session. - session->Initialize(this, m_parent->GetName()); + session->Initialize(kernel, this, m_parent->GetName()); // Commit the session reservation. session_reservation.Commit(); // Register the session. - KSession::Register(m_kernel, session); + KSession::Register(kernel, session); ON_RESULT_FAILURE { - session->GetClientSession().Close(); - session->GetServerSession().Close(); + session->GetClientSession().Close(kernel); + session->GetServerSession().Close(kernel); }; // Enqueue the session with our parent. - R_TRY(m_parent->EnqueueSession(std::addressof(session->GetServerSession()))); + R_TRY(m_parent->EnqueueSession(kernel, std::addressof(session->GetServerSession()))); // We succeeded, so set the output. *out = std::addressof(session->GetClientSession()); R_SUCCEED(); } -Result KClientPort::CreateLightSession(KLightClientSession** out) { +Result KClientPort::CreateLightSession(KernelCore& kernel, KLightClientSession** out) { // Declare the session we're going to allocate. KLightSession* session{}; // Reserve a new session from the resource limit. - KScopedResourceReservation session_reservation(GetCurrentProcessPointer(m_kernel), - Svc::LimitableResource::SessionCountMax); + KScopedResourceReservation session_reservation(kernel, GetCurrentProcessPointer(kernel), Svc::LimitableResource::SessionCountMax); R_UNLESS(session_reservation.Succeeded(), ResultLimitReached); // Allocate a session normally. // TODO: Dynamic resource limits - session = KLightSession::Create(m_kernel); + session = KLightSession::Create(kernel); // Check that we successfully created a session. R_UNLESS(session != nullptr, ResultOutOfResource); @@ -139,7 +141,7 @@ Result KClientPort::CreateLightSession(KLightClientSession** out) { // Update the session counts. { ON_RESULT_FAILURE { - session->Close(); + session->Close(kernel); }; // Atomically increment the number of sessions. @@ -167,20 +169,20 @@ Result KClientPort::CreateLightSession(KLightClientSession** out) { } // Initialize the session. - session->Initialize(this, m_parent->GetName()); + session->Initialize(kernel, this, m_parent->GetName()); // Commit the session reservation. session_reservation.Commit(); // Register the session. - KLightSession::Register(m_kernel, session); + KLightSession::Register(kernel, session); ON_RESULT_FAILURE { - session->GetClientSession().Close(); - session->GetServerSession().Close(); + session->GetClientSession().Close(kernel); + session->GetServerSession().Close(kernel); }; // Enqueue the session with our parent. - R_TRY(m_parent->EnqueueSession(std::addressof(session->GetServerSession()))); + R_TRY(m_parent->EnqueueSession(kernel, std::addressof(session->GetServerSession()))); // We succeeded, so set the output. *out = std::addressof(session->GetClientSession()); diff --git a/src/core/hle/kernel/k_client_port.h b/src/core/hle/kernel/k_client_port.h index 28b3326081..3e48eeaa00 100644 --- a/src/core/hle/kernel/k_client_port.h +++ b/src/core/hle/kernel/k_client_port.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2016 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -23,9 +26,9 @@ public: explicit KClientPort(KernelCore& kernel); ~KClientPort() override; - void Initialize(KPort* parent, s32 max_sessions); - void OnSessionFinalized(); - void OnServerClosed(); + void Initialize(KernelCore& kernel, KPort* parent, s32 max_sessions); + void OnSessionFinalized(KernelCore& kernel); + void OnServerClosed(KernelCore& kernel); const KPort* GetParent() const { return m_parent; @@ -44,15 +47,15 @@ public: return m_max_sessions; } - bool IsLight() const; - bool IsServerClosed() const; + bool IsLight(KernelCore& kernel) const; + bool IsServerClosed(KernelCore& kernel) const; // Overridden virtual functions. - void Destroy() override; - bool IsSignaled() const override; + void Destroy(KernelCore& kernel) override; + bool IsSignaled(KernelCore& kernel) const override; - Result CreateSession(KClientSession** out); - Result CreateLightSession(KLightClientSession** out); + Result CreateSession(KernelCore& kernel, KClientSession** out); + Result CreateLightSession(KernelCore& kernel, KLightClientSession** out); private: std::atomic m_num_sessions{}; diff --git a/src/core/hle/kernel/k_client_session.cpp b/src/core/hle/kernel/k_client_session.cpp index 3e01e3b67f..95cb9896b9 100644 --- a/src/core/hle/kernel/k_client_session.cpp +++ b/src/core/hle/kernel/k_client_session.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -13,41 +16,41 @@ namespace Kernel { KClientSession::KClientSession(KernelCore& kernel) : KAutoObject{kernel} {} KClientSession::~KClientSession() = default; -void KClientSession::Destroy() { - m_parent->OnClientClosed(); - m_parent->Close(); +void KClientSession::Destroy(KernelCore& kernel) { + m_parent->OnClientClosed(kernel); + m_parent->Close(kernel); } void KClientSession::OnServerClosed() {} -Result KClientSession::SendSyncRequest(uintptr_t address, size_t size) { +Result KClientSession::SendSyncRequest(KernelCore& kernel, uintptr_t address, size_t size) { // Create a session request. - KSessionRequest* request = KSessionRequest::Create(m_kernel); + KSessionRequest* request = KSessionRequest::Create(kernel); R_UNLESS(request != nullptr, ResultOutOfResource); SCOPE_EXIT { - request->Close(); + request->Close(kernel); }; // Initialize the request. - request->Initialize(nullptr, address, size); + request->Initialize(kernel, nullptr, address, size); // Send the request. - R_RETURN(m_parent->OnRequest(request)); + R_RETURN(m_parent->OnRequest(kernel, request)); } -Result KClientSession::SendAsyncRequest(KEvent* event, uintptr_t address, size_t size) { +Result KClientSession::SendAsyncRequest(KernelCore& kernel, KEvent* event, uintptr_t address, size_t size) { // Create a session request. - KSessionRequest* request = KSessionRequest::Create(m_kernel); + KSessionRequest* request = KSessionRequest::Create(kernel); R_UNLESS(request != nullptr, ResultOutOfResource); SCOPE_EXIT { - request->Close(); + request->Close(kernel); }; // Initialize the request. - request->Initialize(event, address, size); + request->Initialize(kernel, event, address, size); // Send the request. - R_RETURN(m_parent->OnRequest(request)); + R_RETURN(m_parent->OnRequest(kernel, request)); } } // namespace Kernel diff --git a/src/core/hle/kernel/k_client_session.h b/src/core/hle/kernel/k_client_session.h index a39213e17f..876c3fc789 100644 --- a/src/core/hle/kernel/k_client_session.h +++ b/src/core/hle/kernel/k_client_session.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -26,14 +29,14 @@ public: m_parent = parent; } - void Destroy() override; + void Destroy(KernelCore& kernel) override; KSession* GetParent() const { return m_parent; } - Result SendSyncRequest(uintptr_t address, size_t size); - Result SendAsyncRequest(KEvent* event, uintptr_t address, size_t size); + Result SendSyncRequest(KernelCore& kernel, uintptr_t address, size_t size); + Result SendAsyncRequest(KernelCore& kernel, KEvent* event, uintptr_t address, size_t size); void OnServerClosed(); diff --git a/src/core/hle/kernel/k_code_memory.cpp b/src/core/hle/kernel/k_code_memory.cpp index 7454be55c9..aa8edce832 100644 --- a/src/core/hle/kernel/k_code_memory.cpp +++ b/src/core/hle/kernel/k_code_memory.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -19,16 +22,15 @@ namespace Kernel { KCodeMemory::KCodeMemory(KernelCore& kernel) : KAutoObjectWithSlabHeapAndContainer{kernel}, m_lock(kernel) {} -Result KCodeMemory::Initialize(Core::DeviceMemory& device_memory, KProcessAddress addr, - size_t size) { +Result KCodeMemory::Initialize(KernelCore& kernel, Core::DeviceMemory& device_memory, KProcessAddress addr, size_t size) { // Set members. - m_owner = GetCurrentProcessPointer(m_kernel); + m_owner = GetCurrentProcessPointer(kernel); // Get the owner page table. auto& page_table = m_owner->GetPageTable(); // Construct the page group. - m_page_group.emplace(m_kernel, page_table.GetBlockInfoManager()); + m_page_group.emplace(kernel, page_table.GetBlockInfoManager()); // Lock the memory. R_TRY(page_table.LockForCodeMemory(std::addressof(*m_page_group), addr, size)) @@ -39,7 +41,7 @@ Result KCodeMemory::Initialize(Core::DeviceMemory& device_memory, KProcessAddres } // Set remaining tracking members. - m_owner->Open(); + m_owner->Open(kernel); m_address = addr; m_is_initialized = true; m_is_owner_mapped = false; @@ -49,7 +51,7 @@ Result KCodeMemory::Initialize(Core::DeviceMemory& device_memory, KProcessAddres R_SUCCEED(); } -void KCodeMemory::Finalize() { +void KCodeMemory::Finalize(KernelCore& kernel) { // Unlock. if (!m_is_mapped && !m_is_owner_mapped) { const size_t size = m_page_group->GetNumPages() * PageSize; @@ -57,14 +59,14 @@ void KCodeMemory::Finalize() { } // Close the page group. - m_page_group->Close(); + m_page_group->Close(kernel); m_page_group->Finalize(); // Close our reference to our owner. - m_owner->Close(); + m_owner->Close(kernel); } -Result KCodeMemory::Map(KProcessAddress address, size_t size) { +Result KCodeMemory::Map(KernelCore& kernel, KProcessAddress address, size_t size) { // Validate the size. R_UNLESS(m_page_group->GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize); @@ -75,7 +77,7 @@ Result KCodeMemory::Map(KProcessAddress address, size_t size) { R_UNLESS(!m_is_mapped, ResultInvalidState); // Map the memory. - R_TRY(GetCurrentProcess(m_kernel).GetPageTable().MapPageGroup( + R_TRY(GetCurrentProcess(kernel).GetPageTable().MapPageGroup( address, *m_page_group, KMemoryState::CodeOut, KMemoryPermission::UserReadWrite)); // Mark ourselves as mapped. @@ -84,7 +86,7 @@ Result KCodeMemory::Map(KProcessAddress address, size_t size) { R_SUCCEED(); } -Result KCodeMemory::Unmap(KProcessAddress address, size_t size) { +Result KCodeMemory::Unmap(KernelCore& kernel, KProcessAddress address, size_t size) { // Validate the size. R_UNLESS(m_page_group->GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize); @@ -92,7 +94,7 @@ Result KCodeMemory::Unmap(KProcessAddress address, size_t size) { KScopedLightLock lk(m_lock); // Unmap the memory. - R_TRY(GetCurrentProcess(m_kernel).GetPageTable().UnmapPageGroup(address, *m_page_group, + R_TRY(GetCurrentProcess(kernel).GetPageTable().UnmapPageGroup(address, *m_page_group, KMemoryState::CodeOut)); // Mark ourselves as unmapped. @@ -101,7 +103,7 @@ Result KCodeMemory::Unmap(KProcessAddress address, size_t size) { R_SUCCEED(); } -Result KCodeMemory::MapToOwner(KProcessAddress address, size_t size, Svc::MemoryPermission perm) { +Result KCodeMemory::MapToOwner(KernelCore& kernel, KProcessAddress address, size_t size, Svc::MemoryPermission perm) { // Validate the size. R_UNLESS(m_page_group->GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize); @@ -135,7 +137,7 @@ Result KCodeMemory::MapToOwner(KProcessAddress address, size_t size, Svc::Memory R_SUCCEED(); } -Result KCodeMemory::UnmapFromOwner(KProcessAddress address, size_t size) { +Result KCodeMemory::UnmapFromOwner(KernelCore& kernel, KProcessAddress address, size_t size) { // Validate the size. R_UNLESS(m_page_group->GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize); diff --git a/src/core/hle/kernel/k_code_memory.h b/src/core/hle/kernel/k_code_memory.h index 26fe6b3dc6..4f3aab6b4f 100644 --- a/src/core/hle/kernel/k_code_memory.h +++ b/src/core/hle/kernel/k_code_memory.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -31,18 +34,18 @@ class KCodeMemory final public: explicit KCodeMemory(KernelCore& kernel); - Result Initialize(Core::DeviceMemory& device_memory, KProcessAddress address, size_t size); - void Finalize() override; + Result Initialize(KernelCore& kernel, Core::DeviceMemory& device_memory, KProcessAddress address, size_t size); + void Finalize(KernelCore& kernel) override; - Result Map(KProcessAddress address, size_t size); - Result Unmap(KProcessAddress address, size_t size); - Result MapToOwner(KProcessAddress address, size_t size, Svc::MemoryPermission perm); - Result UnmapFromOwner(KProcessAddress address, size_t size); + Result Map(KernelCore& kernel, KProcessAddress address, size_t size); + Result Unmap(KernelCore& kernel, KProcessAddress address, size_t size); + Result MapToOwner(KernelCore& kernel, KProcessAddress address, size_t size, Svc::MemoryPermission perm); + Result UnmapFromOwner(KernelCore& kernel, KProcessAddress address, size_t size); bool IsInitialized() const override { return m_is_initialized; } - static void PostDestroy(uintptr_t arg) {} + static void PostDestroy(KernelCore& kernel, uintptr_t arg) {} KProcess* GetOwner() const override { return m_owner; diff --git a/src/core/hle/kernel/k_condition_variable.cpp b/src/core/hle/kernel/k_condition_variable.cpp index 1a7431df07..3cf8b28e17 100644 --- a/src/core/hle/kernel/k_condition_variable.cpp +++ b/src/core/hle/kernel/k_condition_variable.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project @@ -66,14 +66,15 @@ bool UpdateLockAtomic(KernelCore& kernel, u32* out, KProcessAddress address, u32 class ThreadQueueImplForKConditionVariableWaitForAddress final : public KThreadQueue { public: explicit ThreadQueueImplForKConditionVariableWaitForAddress(KernelCore& kernel) - : KThreadQueue(kernel) {} + : KThreadQueue(kernel) + {} - void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override { + virtual void CancelWait(KernelCore& kernel, KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override { // Remove the thread as a waiter from its owner. - waiting_thread->GetLockOwner()->RemoveWaiter(waiting_thread); + waiting_thread->GetLockOwner(kernel)->RemoveWaiter(kernel, waiting_thread); // Invoke the base cancel wait handler. - KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task); + KThreadQueue::CancelWait(kernel, waiting_thread, wait_result, cancel_timer_task); } }; @@ -82,14 +83,15 @@ private: KConditionVariable::ThreadTree* m_tree; public: - explicit ThreadQueueImplForKConditionVariableWaitConditionVariable( - KernelCore& kernel, KConditionVariable::ThreadTree* t) - : KThreadQueue(kernel), m_tree(t) {} + explicit ThreadQueueImplForKConditionVariableWaitConditionVariable(KernelCore& kernel, KConditionVariable::ThreadTree* t) + : KThreadQueue(kernel) + , m_tree(t) + {} - void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override { + void CancelWait(KernelCore& kernel, KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override { // Remove the thread as a waiter from its owner. - if (KThread* owner = waiting_thread->GetLockOwner(); owner != nullptr) { - owner->RemoveWaiter(waiting_thread); + if (KThread* owner = waiting_thread->GetLockOwner(kernel); owner != nullptr) { + owner->RemoveWaiter(kernel, waiting_thread); } // If the thread is waiting on a condvar, remove it from the tree. @@ -99,14 +101,15 @@ public: } // Invoke the base cancel wait handler. - KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task); + KThreadQueue::CancelWait(kernel, waiting_thread, wait_result, cancel_timer_task); } }; } // namespace KConditionVariable::KConditionVariable(Core::System& system) - : m_system{system}, m_kernel{system.Kernel()} {} + : m_system{system} +{} KConditionVariable::~KConditionVariable() = default; @@ -119,8 +122,7 @@ Result KConditionVariable::SignalToAddress(KernelCore& kernel, KProcessAddress a // Remove waiter thread. bool has_waiters{}; - KThread* const next_owner_thread = - owner_thread->RemoveUserWaiterByKey(std::addressof(has_waiters), addr); + KThread* const next_owner_thread = owner_thread->RemoveUserWaiterByKey(kernel, std::addressof(has_waiters), addr); // Determine the next tag. u32 next_value{}; @@ -144,15 +146,14 @@ Result KConditionVariable::SignalToAddress(KernelCore& kernel, KProcessAddress a // If necessary, signal the next owner thread. if (next_owner_thread != nullptr) { - next_owner_thread->EndWait(result); + next_owner_thread->EndWait(kernel, result); } R_RETURN(result); } } -Result KConditionVariable::WaitForAddress(KernelCore& kernel, Handle handle, KProcessAddress addr, - u32 value) { +Result KConditionVariable::WaitForAddress(KernelCore& kernel, Handle handle, KProcessAddress addr, u32 value) { KThread* cur_thread = GetCurrentThreadPointer(kernel); ThreadQueueImplForKConditionVariableWaitForAddress wait_queue(kernel); @@ -173,30 +174,30 @@ Result KConditionVariable::WaitForAddress(KernelCore& kernel, Handle handle, KPr // Get the lock owner thread. owner_thread = GetCurrentProcess(kernel) - .GetHandleTable() - .GetObjectWithoutPseudoHandle(handle) - .ReleasePointerUnsafe(); + .GetHandleTable() + .GetObjectWithoutPseudoHandle(kernel, handle) + .ReleasePointerUnsafe(); R_UNLESS(owner_thread != nullptr, ResultInvalidHandle); // Update the lock. cur_thread->SetUserAddressKey(addr, value); - owner_thread->AddWaiter(cur_thread); + owner_thread->AddWaiter(kernel, cur_thread); // Begin waiting. - cur_thread->BeginWait(std::addressof(wait_queue)); + cur_thread->BeginWait(kernel, std::addressof(wait_queue)); cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar); } // Close our reference to the owner thread, now that the wait is over. - owner_thread->Close(); + owner_thread->Close(kernel); // Get the wait result. R_RETURN(cur_thread->GetWaitResult()); } -void KConditionVariable::SignalImpl(KThread* thread) { +void KConditionVariable::SignalImpl(KernelCore& kernel, KThread* thread) { // Check pre-conditions. - ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel)); // Update the tag. KProcessAddress address = thread->GetAddressKey(); @@ -211,35 +212,33 @@ void KConditionVariable::SignalImpl(KThread* thread) { // TODO(bunnei): We should call CanAccessAtomic(..) here. can_access = true; if (can_access) { - UpdateLockAtomic(m_kernel, std::addressof(prev_tag), address, own_tag, - Svc::HandleWaitMask); + UpdateLockAtomic(kernel, std::addressof(prev_tag), address, own_tag, Svc::HandleWaitMask); } } if (can_access) { if (prev_tag == Svc::InvalidHandle) { // If nobody held the lock previously, we're all good. - thread->EndWait(ResultSuccess); + thread->EndWait(kernel, ResultSuccess); } else { // Get the previous owner. - KThread* owner_thread = GetCurrentProcess(m_kernel) - .GetHandleTable() - .GetObjectWithoutPseudoHandle( - static_cast(prev_tag & ~Svc::HandleWaitMask)) - .ReleasePointerUnsafe(); + KThread* owner_thread = GetCurrentProcess(kernel) + .GetHandleTable() + .GetObjectWithoutPseudoHandle(kernel, Handle(prev_tag & ~Svc::HandleWaitMask)) + .ReleasePointerUnsafe(); if (owner_thread) { // Add the thread as a waiter on the owner. - owner_thread->AddWaiter(thread); - owner_thread->Close(); + owner_thread->AddWaiter(kernel, thread); + owner_thread->Close(kernel); } else { // The lock was tagged with a thread that doesn't exist. - thread->EndWait(ResultInvalidState); + thread->EndWait(kernel, ResultInvalidState); } } } else { // If the address wasn't accessible, note so. - thread->EndWait(ResultInvalidCurrentMemory); + thread->EndWait(kernel, ResultInvalidCurrentMemory); } } @@ -247,17 +246,16 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) { // Perform signaling. s32 num_waiters{}; { - KScopedSchedulerLock sl(m_kernel); + KScopedSchedulerLock sl(m_system.Kernel()); auto it = m_tree.nfind_key({cv_key, -1}); - while ((it != m_tree.end()) && (count <= 0 || num_waiters < count) && - (it->GetConditionVariableKey() == cv_key)) { + while ((it != m_tree.end()) && (count <= 0 || num_waiters < count) && (it->GetConditionVariableKey() == cv_key)) { KThread* target_thread = std::addressof(*it); it = m_tree.erase(it); target_thread->ClearConditionVariable(); - this->SignalImpl(target_thread); + this->SignalImpl(m_system.Kernel(), target_thread); ++num_waiters; } @@ -265,20 +263,20 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) { // If we have no waiters, clear the has waiter flag. if (it == m_tree.end() || it->GetConditionVariableKey() != cv_key) { constexpr u32 HasNoWaiterFlag = 0; - WriteToUser(m_kernel, cv_key, HasNoWaiterFlag); + WriteToUser(m_system.Kernel(), cv_key, HasNoWaiterFlag); } } } Result KConditionVariable::Wait(KProcessAddress addr, u64 key, u32 value, s64 timeout) { // Prepare to wait. - KThread* cur_thread = GetCurrentThreadPointer(m_kernel); + KThread* cur_thread = GetCurrentThreadPointer(m_system.Kernel()); KHardwareTimer* timer{}; - ThreadQueueImplForKConditionVariableWaitConditionVariable wait_queue(m_kernel, + ThreadQueueImplForKConditionVariableWaitConditionVariable wait_queue(m_system.Kernel(), std::addressof(m_tree)); { - KScopedSchedulerLockAndSleep slp(m_kernel, std::addressof(timer), cur_thread, timeout); + KScopedSchedulerLockAndSleep slp(m_system.Kernel(), std::addressof(timer), cur_thread, timeout); // Check that the thread isn't terminating. if (cur_thread->IsTerminationRequested()) { @@ -291,7 +289,7 @@ Result KConditionVariable::Wait(KProcessAddress addr, u64 key, u32 value, s64 ti // Remove waiter thread. bool has_waiters{}; KThread* next_owner_thread = - cur_thread->RemoveUserWaiterByKey(std::addressof(has_waiters), addr); + cur_thread->RemoveUserWaiterByKey(m_system.Kernel(), std::addressof(has_waiters), addr); // Update for the next owner thread. u32 next_value{}; @@ -303,18 +301,18 @@ Result KConditionVariable::Wait(KProcessAddress addr, u64 key, u32 value, s64 ti } // Wake up the next owner. - next_owner_thread->EndWait(ResultSuccess); + next_owner_thread->EndWait(m_system.Kernel(), ResultSuccess); } // Write to the cv key. { constexpr u32 HasWaiterFlag = 1; - WriteToUser(m_kernel, key, HasWaiterFlag); + WriteToUser(m_system.Kernel(), key, HasWaiterFlag); std::atomic_thread_fence(std::memory_order_seq_cst); } // Write the value to userspace. - if (!WriteToUser(m_kernel, addr, next_value)) { + if (!WriteToUser(m_system.Kernel(), addr, next_value)) { slp.CancelSleep(); R_THROW(ResultInvalidCurrentMemory); } @@ -329,7 +327,7 @@ Result KConditionVariable::Wait(KProcessAddress addr, u64 key, u32 value, s64 ti // Begin waiting. wait_queue.SetHardwareTimer(timer); - cur_thread->BeginWait(std::addressof(wait_queue)); + cur_thread->BeginWait(m_system.Kernel(), std::addressof(wait_queue)); cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar); } diff --git a/src/core/hle/kernel/k_condition_variable.h b/src/core/hle/kernel/k_condition_variable.h index 2620c8e39e..ca80de8837 100644 --- a/src/core/hle/kernel/k_condition_variable.h +++ b/src/core/hle/kernel/k_condition_variable.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -26,19 +29,17 @@ public: // Arbitration. static Result SignalToAddress(KernelCore& kernel, KProcessAddress addr); - static Result WaitForAddress(KernelCore& kernel, Handle handle, KProcessAddress addr, - u32 value); + static Result WaitForAddress(KernelCore& kernel, Handle handle, KProcessAddress addr, u32 value); // Condition variable. void Signal(u64 cv_key, s32 count); Result Wait(KProcessAddress addr, u64 key, u32 value, s64 timeout); private: - void SignalImpl(KThread* thread); + void SignalImpl(KernelCore& kernel, KThread* thread); private: Core::System& m_system; - KernelCore& m_kernel; ThreadTree m_tree{}; }; diff --git a/src/core/hle/kernel/k_debug.h b/src/core/hle/kernel/k_debug.h index 2290e3bcab..92ff70df74 100644 --- a/src/core/hle/kernel/k_debug.h +++ b/src/core/hle/kernel/k_debug.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -14,7 +17,7 @@ class KDebug final : public KAutoObjectWithSlabHeapAndContainerOpen(); + m_owner->Open(kernel); } // Mark initialized. m_initialized = true; } -void KEvent::Finalize() { - KAutoObjectWithSlabHeapAndContainer::Finalize(); +void KEvent::Finalize(KernelCore& kernel) { + KAutoObjectWithSlabHeapAndContainer::Finalize(kernel); } -Result KEvent::Signal() { - KScopedSchedulerLock sl{m_kernel}; +Result KEvent::Signal(KernelCore& kernel) { + KScopedSchedulerLock sl{kernel}; R_SUCCEED_IF(m_readable_event_destroyed); - return m_readable_event.Signal(); + return m_readable_event.Signal(kernel); } -Result KEvent::Clear() { - KScopedSchedulerLock sl{m_kernel}; +Result KEvent::Clear(KernelCore& kernel) { + KScopedSchedulerLock sl{kernel}; R_SUCCEED_IF(m_readable_event_destroyed); - return m_readable_event.Clear(); + return m_readable_event.Clear(kernel); } -void KEvent::PostDestroy(uintptr_t arg) { +void KEvent::PostDestroy(KernelCore& kernel, uintptr_t arg) { // Release the event count resource the owner process holds. KProcess* owner = reinterpret_cast(arg); if (owner != nullptr) { - owner->GetResourceLimit()->Release(LimitableResource::EventCountMax, 1); - owner->Close(); + owner->GetResourceLimit()->Release(kernel, LimitableResource::EventCountMax, 1); + owner->Close(kernel); } } diff --git a/src/core/hle/kernel/k_event.h b/src/core/hle/kernel/k_event.h index f522b0a84c..75b03efa69 100644 --- a/src/core/hle/kernel/k_event.h +++ b/src/core/hle/kernel/k_event.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -19,9 +22,9 @@ public: explicit KEvent(KernelCore& kernel); ~KEvent() override; - void Initialize(KProcess* owner); + void Initialize(KernelCore& kernel, KProcess* owner); - void Finalize() override; + void Finalize(KernelCore& kernel) override; bool IsInitialized() const override { return m_initialized; @@ -39,10 +42,10 @@ public: return m_readable_event; } - static void PostDestroy(uintptr_t arg); + static void PostDestroy(KernelCore& kernel, uintptr_t arg); - Result Signal(); - Result Clear(); + Result Signal(KernelCore& kernel); + Result Clear(KernelCore& kernel); void OnReadableEventDestroyed() { m_readable_event_destroyed = true; diff --git a/src/core/hle/kernel/k_handle_table.cpp b/src/core/hle/kernel/k_handle_table.cpp index 04ca235344..91d805fa79 100644 --- a/src/core/hle/kernel/k_handle_table.cpp +++ b/src/core/hle/kernel/k_handle_table.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project @@ -6,28 +6,27 @@ #include "core/hle/kernel/k_handle_table.h" #include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/kernel.h" namespace Kernel { -void KHandleTable::Finalize() { +void KHandleTable::Finalize(KernelCore& kernel) { // Get the table and clear our record of it. u16 saved_table_size = 0; { - KScopedDisableDispatch dd{m_kernel}; + KScopedDisableDispatch dd{kernel}; KScopedSpinLock lk(m_lock); std::swap(m_table_size, saved_table_size); } // Close and free all entries. - for (size_t i = 0; i < saved_table_size; i++) { - if (KAutoObject* obj = m_objects[i]; obj != nullptr) { - obj->Close(); - } - } + for (size_t i = 0; i < saved_table_size; i++) + if (KAutoObject* obj = m_objects[i]; obj != nullptr) + obj->Close(kernel); } -bool KHandleTable::Remove(Handle handle) { +bool KHandleTable::Remove(KernelCore& kernel, Handle handle) { // Don't allow removal of a pseudo-handle. if (Svc::IsPseudoHandle(handle)) [[unlikely]] { return false; @@ -42,7 +41,7 @@ bool KHandleTable::Remove(Handle handle) { // Find the object and free the entry. KAutoObject* obj = nullptr; { - KScopedDisableDispatch dd{m_kernel}; + KScopedDisableDispatch dd{kernel}; KScopedSpinLock lk(m_lock); if (this->IsValidHandle(handle)) [[likely]] { @@ -56,13 +55,13 @@ bool KHandleTable::Remove(Handle handle) { } // Close the object. - m_kernel.UnregisterInUseObject(obj); - obj->Close(); + kernel.UnregisterInUseObject(obj); + obj->Close(kernel); return true; } -Result KHandleTable::Add(Handle* out_handle, KAutoObject* obj) { - KScopedDisableDispatch dd{m_kernel}; +Result KHandleTable::Add(KernelCore& kernel, Handle* out_handle, KAutoObject* obj) { + KScopedDisableDispatch dd{kernel}; KScopedSpinLock lk(m_lock); // Never exceed our capacity. @@ -76,7 +75,7 @@ Result KHandleTable::Add(Handle* out_handle, KAutoObject* obj) { m_entry_infos[index].linear_id = linear_id; m_objects[index] = obj; - obj->Open(); + obj->Open(kernel); *out_handle = EncodeHandle(static_cast(index), linear_id); } @@ -84,24 +83,22 @@ Result KHandleTable::Add(Handle* out_handle, KAutoObject* obj) { R_SUCCEED(); } -KScopedAutoObject KHandleTable::GetObjectForIpc(Handle handle, - KThread* cur_thread) const { +KScopedAutoObject KHandleTable::GetObjectForIpc(KernelCore& kernel, Handle handle, KThread* cur_thread) const { // Handle pseudo-handles. ASSERT(cur_thread != nullptr); if (handle == Svc::PseudoHandle::CurrentProcess) { auto* const cur_process = cur_thread->GetOwnerProcess(); ASSERT(cur_process != nullptr); - return cur_process; + return {kernel, cur_process}; } if (handle == Svc::PseudoHandle::CurrentThread) { - return cur_thread; + return {kernel, cur_thread}; } - - return GetObjectForIpcWithoutPseudoHandle(handle); + return GetObjectForIpcWithoutPseudoHandle(kernel, handle); } -Result KHandleTable::Reserve(Handle* out_handle) { - KScopedDisableDispatch dd{m_kernel}; +Result KHandleTable::Reserve(KernelCore& kernel, Handle* out_handle) { + KScopedDisableDispatch dd{kernel}; KScopedSpinLock lk(m_lock); // Never exceed our capacity. @@ -111,8 +108,8 @@ Result KHandleTable::Reserve(Handle* out_handle) { R_SUCCEED(); } -void KHandleTable::Unreserve(Handle handle) { - KScopedDisableDispatch dd{m_kernel}; +void KHandleTable::Unreserve(KernelCore& kernel, Handle handle) { + KScopedDisableDispatch dd{kernel}; KScopedSpinLock lk(m_lock); // Unpack the handle. @@ -130,8 +127,8 @@ void KHandleTable::Unreserve(Handle handle) { } } -void KHandleTable::Register(Handle handle, KAutoObject* obj) { - KScopedDisableDispatch dd{m_kernel}; +void KHandleTable::Register(KernelCore& kernel, Handle handle, KAutoObject* obj) { + KScopedDisableDispatch dd{kernel}; KScopedSpinLock lk(m_lock); // Unpack the handle. @@ -149,7 +146,7 @@ void KHandleTable::Register(Handle handle, KAutoObject* obj) { m_entry_infos[index].linear_id = static_cast(linear_id); m_objects[index] = obj; - obj->Open(); + obj->Open(kernel); } } diff --git a/src/core/hle/kernel/k_handle_table.h b/src/core/hle/kernel/k_handle_table.h index 731a5284dc..0704fd56ef 100644 --- a/src/core/hle/kernel/k_handle_table.h +++ b/src/core/hle/kernel/k_handle_table.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project @@ -31,14 +31,14 @@ public: static constexpr size_t MaxTableSize = 1024; public: - explicit KHandleTable(KernelCore& kernel) : m_kernel(kernel) {} + explicit KHandleTable(KernelCore& kernel) {} - Result Initialize(s32 size) { + Result Initialize(KernelCore& kernel, s32 size) { // Check that the table size is valid. R_UNLESS(size <= static_cast(MaxTableSize), ResultOutOfMemory); // Lock. - KScopedDisableDispatch dd{m_kernel}; + KScopedDisableDispatch dd{kernel}; KScopedSpinLock lk(m_lock); // Initialize all fields. @@ -68,76 +68,72 @@ public: return m_max_count; } - void Finalize(); - bool Remove(Handle handle); + void Finalize(KernelCore& kernel); + bool Remove(KernelCore& kernel, Handle handle); template - KScopedAutoObject GetObjectWithoutPseudoHandle(Handle handle) const { + KScopedAutoObject GetObjectWithoutPseudoHandle(KernelCore& kernel, Handle handle) const { // Lock and look up in table. - KScopedDisableDispatch dd{m_kernel}; + KScopedDisableDispatch dd{kernel}; KScopedSpinLock lk(m_lock); if constexpr (std::is_same_v) { - return this->GetObjectImpl(handle); + return {kernel, this->GetObjectImpl(handle)}; } else { if (auto* obj = this->GetObjectImpl(handle); obj != nullptr) [[likely]] { - return obj->DynamicCast(); + return {kernel, obj->DynamicCast()}; } else { - return nullptr; + return {kernel, nullptr}; } } } template - KScopedAutoObject GetObject(Handle handle) const { + KScopedAutoObject GetObject(KernelCore& kernel, Handle handle) const { // Handle pseudo-handles. if constexpr (std::derived_from) { if (handle == Svc::PseudoHandle::CurrentProcess) { - auto* const cur_process = GetCurrentProcessPointer(m_kernel); + auto* const cur_process = GetCurrentProcessPointer(kernel); ASSERT(cur_process != nullptr); - return cur_process; + return {kernel, cur_process}; } } else if constexpr (std::derived_from) { if (handle == Svc::PseudoHandle::CurrentThread) { - auto* const cur_thread = GetCurrentThreadPointer(m_kernel); + auto* const cur_thread = GetCurrentThreadPointer(kernel); ASSERT(cur_thread != nullptr); - return cur_thread; + return {kernel, cur_thread}; } } - - return this->template GetObjectWithoutPseudoHandle(handle); + return this->template GetObjectWithoutPseudoHandle(kernel, handle); } - KScopedAutoObject GetObjectForIpcWithoutPseudoHandle(Handle handle) const { + KScopedAutoObject GetObjectForIpcWithoutPseudoHandle(KernelCore& kernel, Handle handle) const { // Lock and look up in table. - KScopedDisableDispatch dd{m_kernel}; + KScopedDisableDispatch dd{kernel}; + KScopedSpinLock lk(m_lock); + return {kernel, this->GetObjectImpl(handle)}; + } + KScopedAutoObject GetObjectForIpc(KernelCore& kernel, Handle handle, KThread* cur_thread) const; + KScopedAutoObject GetObjectByIndex(KernelCore& kernel, Handle* out_handle, size_t index) const { + KScopedDisableDispatch dd{kernel}; KScopedSpinLock lk(m_lock); - return this->GetObjectImpl(handle); + return {kernel, this->GetObjectByIndexImpl(out_handle, index)}; } - KScopedAutoObject GetObjectForIpc(Handle handle, KThread* cur_thread) const; + Result Reserve(KernelCore& kernel, Handle* out_handle); + void Unreserve(KernelCore& kernel, Handle handle); - KScopedAutoObject GetObjectByIndex(Handle* out_handle, size_t index) const { - KScopedDisableDispatch dd{m_kernel}; - KScopedSpinLock lk(m_lock); - - return this->GetObjectByIndexImpl(out_handle, index); - } - - Result Reserve(Handle* out_handle); - void Unreserve(Handle handle); - - Result Add(Handle* out_handle, KAutoObject* obj); - void Register(Handle handle, KAutoObject* obj); + Result Add(KernelCore& kernel, Handle* out_handle, KAutoObject* obj); + void Register(KernelCore& kernel, Handle handle, KAutoObject* obj); template - bool GetMultipleObjects(T** out, const Handle* handles, size_t num_handles) const { + bool GetMultipleObjects(KernelCore& kernel, T** out, const Handle* handles, size_t num_handles) const { // Try to convert and open all the handles. size_t num_opened; { // Lock the table. - KScopedDisableDispatch dd{m_kernel}; + KScopedDisableDispatch dd{kernel}; KScopedSpinLock lk(m_lock); for (num_opened = 0; num_opened < num_handles; num_opened++) { // Get the current handle. @@ -150,14 +146,14 @@ public: } // Cast the current object to the desired type. - T* cur_t = cur_object->DynamicCast(); - if (cur_t == nullptr) [[unlikely]] { + T* cur_thread = cur_object->DynamicCast(); + if (cur_thread == nullptr) [[unlikely]] { break; } // Open a reference to the current object. - cur_t->Open(); - out[num_opened] = cur_t; + cur_thread->Open(kernel); + out[num_opened] = cur_thread; } } @@ -168,7 +164,7 @@ public: // If we didn't convert entry object, close the ones we opened. for (size_t i = 0; i < num_opened; i++) { - out[i]->Close(); + out[i]->Close(kernel); } return false; @@ -177,13 +173,9 @@ public: private: s32 AllocateEntry() { ASSERT(m_count < m_table_size); - const auto index = m_free_head_index; - m_free_head_index = m_entry_infos[index].GetNextFreeIndex(); - m_max_count = (std::max)(m_max_count, ++m_count); - return index; } @@ -302,7 +294,6 @@ private: }; private: - KernelCore& m_kernel; std::array m_entry_infos{}; std::array m_objects{}; mutable KSpinLock m_lock; diff --git a/src/core/hle/kernel/k_hardware_timer_base.h b/src/core/hle/kernel/k_hardware_timer_base.h index 6318b35bdd..75a4a54763 100644 --- a/src/core/hle/kernel/k_hardware_timer_base.h +++ b/src/core/hle/kernel/k_hardware_timer_base.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -45,7 +48,7 @@ protected: this->RemoveTaskFromTree(task); // Handle the task. - task->OnTimer(); + task->OnTimer(m_kernel); } } diff --git a/src/core/hle/kernel/k_interrupt_manager.cpp b/src/core/hle/kernel/k_interrupt_manager.cpp index 22d79569a5..9bd182fd9b 100644 --- a/src/core/hle/kernel/k_interrupt_manager.cpp +++ b/src/core/hle/kernel/k_interrupt_manager.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -18,19 +21,19 @@ void HandleInterrupt(KernelCore& kernel, s32 core_id) { if (auto* process = GetCurrentProcessPointer(kernel); process) { // If the user disable count is set, we may need to pin the current thread. - if (current_thread.GetUserDisableCount() && !process->GetPinnedThread(core_id)) { + if (current_thread.GetUserDisableCount(kernel) && !process->GetPinnedThread(core_id)) { KScopedSchedulerLock sl{kernel}; // Pin the current thread. - process->PinCurrentThread(); + process->PinCurrentThread(kernel); // Set the interrupt flag for the thread. - GetCurrentThread(kernel).SetInterruptFlag(); + GetCurrentThread(kernel).SetInterruptFlag(kernel); } } // Request interrupt scheduling. - kernel.CurrentScheduler()->RequestScheduleOnInterrupt(); + kernel.CurrentScheduler()->RequestScheduleOnInterrupt(kernel); } void SendInterProcessorInterrupt(KernelCore& kernel, u64 core_mask) { diff --git a/src/core/hle/kernel/k_light_client_session.cpp b/src/core/hle/kernel/k_light_client_session.cpp index 8ce3e1ae49..23f77deffd 100644 --- a/src/core/hle/kernel/k_light_client_session.cpp +++ b/src/core/hle/kernel/k_light_client_session.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -11,21 +14,21 @@ KLightClientSession::KLightClientSession(KernelCore& kernel) : KAutoObject(kerne KLightClientSession::~KLightClientSession() = default; -void KLightClientSession::Destroy() { - m_parent->OnClientClosed(); +void KLightClientSession::Destroy(KernelCore& kernel) { + m_parent->OnClientClosed(kernel); } -void KLightClientSession::OnServerClosed() {} +void KLightClientSession::OnServerClosed(KernelCore& kernel) {} -Result KLightClientSession::SendSyncRequest(u32* data) { +Result KLightClientSession::SendSyncRequest(KernelCore& kernel, u32* data) { // Get the request thread. - KThread* cur_thread = GetCurrentThreadPointer(m_kernel); + KThread* cur_thread = GetCurrentThreadPointer(kernel); // Set the light data. cur_thread->SetLightSessionData(data); // Send the request. - R_RETURN(m_parent->OnRequest(cur_thread)); + R_RETURN(m_parent->OnRequest(kernel, cur_thread)); } } // namespace Kernel diff --git a/src/core/hle/kernel/k_light_client_session.h b/src/core/hle/kernel/k_light_client_session.h index 881a15cbda..c0f48c295e 100644 --- a/src/core/hle/kernel/k_light_client_session.h +++ b/src/core/hle/kernel/k_light_client_session.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -22,15 +25,15 @@ public: m_parent = parent; } - virtual void Destroy() override; + virtual void Destroy(KernelCore& kernel) override; const KLightSession* GetParent() const { return m_parent; } - Result SendSyncRequest(u32* data); + Result SendSyncRequest(KernelCore& kernel, u32* data); - void OnServerClosed(); + void OnServerClosed(KernelCore& kernel); private: KLightSession* m_parent; diff --git a/src/core/hle/kernel/k_light_condition_variable.cpp b/src/core/hle/kernel/k_light_condition_variable.cpp index 6d5a815aab..2c873162fb 100644 --- a/src/core/hle/kernel/k_light_condition_variable.cpp +++ b/src/core/hle/kernel/k_light_condition_variable.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -13,11 +16,13 @@ namespace { class ThreadQueueImplForKLightConditionVariable final : public KThreadQueue { public: - ThreadQueueImplForKLightConditionVariable(KernelCore& kernel, KThread::WaiterList* wl, - bool term) - : KThreadQueue(kernel), m_wait_list(wl), m_allow_terminating_thread(term) {} + ThreadQueueImplForKLightConditionVariable(KernelCore& kernel, KThread::WaiterList* wl, bool term) + : KThreadQueue(kernel) + , m_wait_list(wl) + , m_allow_terminating_thread(term) + {} - void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override { + virtual void CancelWait(KernelCore& kernel, KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override { // Only process waits if we're allowed to. if (ResultTerminationRequested == wait_result && m_allow_terminating_thread) { return; @@ -27,7 +32,7 @@ public: m_wait_list->erase(m_wait_list->iterator_to(*waiting_thread)); // Invoke the base cancel wait handler. - KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task); + KThreadQueue::CancelWait(kernel, waiting_thread, wait_result, cancel_timer_task); } private: @@ -61,7 +66,7 @@ void KLightConditionVariable::Wait(KLightLock* lock, s64 timeout, bool allow_ter // Begin waiting. wait_queue.SetHardwareTimer(timer); - owner->BeginWait(std::addressof(wait_queue)); + owner->BeginWait(m_kernel, std::addressof(wait_queue)); } // Re-acquire the lock. @@ -73,7 +78,7 @@ void KLightConditionVariable::Broadcast() { // Signal all threads. for (auto it = m_wait_list.begin(); it != m_wait_list.end(); it = m_wait_list.erase(it)) { - it->EndWait(ResultSuccess); + it->EndWait(m_kernel, ResultSuccess); } } diff --git a/src/core/hle/kernel/k_light_lock.cpp b/src/core/hle/kernel/k_light_lock.cpp index e87ee8b652..614e056d13 100644 --- a/src/core/hle/kernel/k_light_lock.cpp +++ b/src/core/hle/kernel/k_light_lock.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -15,29 +18,25 @@ class ThreadQueueImplForKLightLock final : public KThreadQueue { public: explicit ThreadQueueImplForKLightLock(KernelCore& kernel) : KThreadQueue(kernel) {} - void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override { + void CancelWait(KernelCore& kernel, KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override { // Remove the thread as a waiter from its owner. - if (KThread* owner = waiting_thread->GetLockOwner(); owner != nullptr) { - owner->RemoveWaiter(waiting_thread); + if (KThread* owner = waiting_thread->GetLockOwner(kernel); owner != nullptr) { + owner->RemoveWaiter(kernel, waiting_thread); } // Invoke the base cancel wait handler. - KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task); + KThreadQueue::CancelWait(kernel, waiting_thread, wait_result, cancel_timer_task); } }; } // namespace void KLightLock::Lock() { - const uintptr_t cur_thread = reinterpret_cast(GetCurrentThreadPointer(m_kernel)); - + const uintptr_t cur_thread = uintptr_t(GetCurrentThreadPointer(m_kernel)); while (true) { uintptr_t old_tag = m_tag.load(std::memory_order_relaxed); - - while (!m_tag.compare_exchange_weak(old_tag, (old_tag == 0) ? cur_thread : (old_tag | 1), - std::memory_order_acquire)) { - } - + while (!m_tag.compare_exchange_weak(old_tag, (old_tag == 0) ? cur_thread : (old_tag | 1), std::memory_order_acquire)) + ; if (old_tag == 0 || this->LockSlowPath(old_tag | 1, cur_thread)) { break; } @@ -69,13 +68,13 @@ bool KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) { // Add the current thread as a waiter on the owner. KThread* owner_thread = reinterpret_cast(_owner & ~1ULL); cur_thread->SetKernelAddressKey(reinterpret_cast(std::addressof(m_tag))); - owner_thread->AddWaiter(cur_thread); + owner_thread->AddWaiter(m_kernel, cur_thread); // Begin waiting to hold the lock. - cur_thread->BeginWait(std::addressof(wait_queue)); + cur_thread->BeginWait(m_kernel, std::addressof(wait_queue)); if (owner_thread->IsSuspended()) { - owner_thread->ContinueIfHasKernelWaiters(); + owner_thread->ContinueIfHasKernelWaiters(m_kernel); } } @@ -91,26 +90,25 @@ void KLightLock::UnlockSlowPath(uintptr_t _cur_thread) { // Get the next owner. bool has_waiters; - KThread* next_owner = owner_thread->RemoveKernelWaiterByKey( - std::addressof(has_waiters), reinterpret_cast(std::addressof(m_tag))); + KThread* next_owner = owner_thread->RemoveKernelWaiterByKey(m_kernel, + std::addressof(has_waiters), uintptr_t(std::addressof(m_tag))); // Pass the lock to the next owner. uintptr_t next_tag = 0; if (next_owner != nullptr) { - next_tag = - reinterpret_cast(next_owner) | static_cast(has_waiters); + next_tag = uintptr_t(next_owner) | uintptr_t(has_waiters); - next_owner->EndWait(ResultSuccess); + next_owner->EndWait(m_kernel, ResultSuccess); if (next_owner->IsSuspended()) { - next_owner->ContinueIfHasKernelWaiters(); + next_owner->ContinueIfHasKernelWaiters(m_kernel); } } // We may have unsuspended in the process of acquiring the lock, so we'll re-suspend now if // so. if (owner_thread->IsSuspended()) { - owner_thread->TrySuspend(); + owner_thread->TrySuspend(m_kernel); } // Write the new tag value. @@ -119,8 +117,7 @@ void KLightLock::UnlockSlowPath(uintptr_t _cur_thread) { } bool KLightLock::IsLockedByCurrentThread() const { - return (m_tag.load() | 1ULL) == - (reinterpret_cast(GetCurrentThreadPointer(m_kernel)) | 1ULL); + return (m_tag.load() | 1ULL) == (uintptr_t(GetCurrentThreadPointer(m_kernel)) | 1ULL); } } // namespace Kernel diff --git a/src/core/hle/kernel/k_light_server_session.cpp b/src/core/hle/kernel/k_light_server_session.cpp index a4d2b11231..6344f418ed 100644 --- a/src/core/hle/kernel/k_light_server_session.cpp +++ b/src/core/hle/kernel/k_light_server_session.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project @@ -24,21 +24,20 @@ public: ThreadQueueImplForKLightServerSessionRequest(KernelCore& kernel, KThread::WaiterList* wl) : KThreadQueue(kernel), m_wait_list(wl) {} - virtual void EndWait(KThread* waiting_thread, Result wait_result) override { + virtual void EndWait(KernelCore& kernel, KThread* waiting_thread, Result wait_result) override { // Remove the thread from our wait list. m_wait_list->erase(m_wait_list->iterator_to(*waiting_thread)); // Invoke the base end wait handler. - KThreadQueue::EndWait(waiting_thread, wait_result); + KThreadQueue::EndWait(kernel, waiting_thread, wait_result); } - virtual void CancelWait(KThread* waiting_thread, Result wait_result, - bool cancel_timer_task) override { + virtual void CancelWait(KernelCore& kernel, KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override { // Remove the thread from our wait list. m_wait_list->erase(m_wait_list->iterator_to(*waiting_thread)); // Invoke the base cancel wait handler. - KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task); + KThreadQueue::CancelWait(kernel, waiting_thread, wait_result, cancel_timer_task); } }; @@ -48,9 +47,11 @@ private: public: ThreadQueueImplForKLightServerSessionReceive(KernelCore& kernel, KThread** st) - : KThreadQueue(kernel), m_server_thread(st) {} + : KThreadQueue(kernel) + , m_server_thread(st) + {} - virtual void EndWait(KThread* waiting_thread, Result wait_result) override { + virtual void EndWait(KernelCore& kernel, KThread* waiting_thread, Result wait_result) override { // Clear the server thread. *m_server_thread = nullptr; @@ -58,11 +59,10 @@ public: waiting_thread->ClearCancellable(); // Invoke the base end wait handler. - KThreadQueue::EndWait(waiting_thread, wait_result); + KThreadQueue::EndWait(kernel, waiting_thread, wait_result); } - virtual void CancelWait(KThread* waiting_thread, Result wait_result, - bool cancel_timer_task) override { + virtual void CancelWait(KernelCore& kernel, KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override { // Clear the server thread. *m_server_thread = nullptr; @@ -70,7 +70,7 @@ public: waiting_thread->ClearCancellable(); // Invoke the base cancel wait handler. - KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task); + KThreadQueue::CancelWait(kernel, waiting_thread, wait_result, cancel_timer_task); } }; @@ -79,24 +79,21 @@ public: KLightServerSession::KLightServerSession(KernelCore& kernel) : KAutoObject(kernel) {} KLightServerSession::~KLightServerSession() = default; -void KLightServerSession::Destroy() { - this->CleanupRequests(); - - m_parent->OnServerClosed(); +void KLightServerSession::Destroy(KernelCore& kernel) { + this->CleanupRequests(kernel); + m_parent->OnServerClosed(kernel); } -void KLightServerSession::OnClientClosed() { - this->CleanupRequests(); +void KLightServerSession::OnClientClosed(KernelCore& kernel) { + this->CleanupRequests(kernel); } -Result KLightServerSession::OnRequest(KThread* request_thread) { - ThreadQueueImplForKLightServerSessionRequest wait_queue(m_kernel, - std::addressof(m_request_list)); - +Result KLightServerSession::OnRequest(KernelCore& kernel, KThread* request_thread) { + ThreadQueueImplForKLightServerSessionRequest wait_queue(kernel, std::addressof(m_request_list)); // Send the request. { // Lock the scheduler. - KScopedSchedulerLock sl(m_kernel); + KScopedSchedulerLock sl(kernel); // Check that the server isn't closed. R_UNLESS(!m_parent->IsServerClosed(), ResultSessionClosed); @@ -109,11 +106,11 @@ Result KLightServerSession::OnRequest(KThread* request_thread) { // Begin waiting on the request. request_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC); - request_thread->BeginWait(std::addressof(wait_queue)); + request_thread->BeginWait(kernel, std::addressof(wait_queue)); // If we have a server thread, end its wait. if (m_server_thread != nullptr) { - m_server_thread->EndWait(ResultSuccess); + m_server_thread->EndWait(kernel, ResultSuccess); } } @@ -123,13 +120,13 @@ Result KLightServerSession::OnRequest(KThread* request_thread) { R_RETURN(request_thread->GetWaitResult()); } -Result KLightServerSession::ReplyAndReceive(u32* data) { +Result KLightServerSession::ReplyAndReceive(KernelCore& kernel, u32* data) { // Set the server context. - GetCurrentThread(m_kernel).SetLightSessionData(data); + GetCurrentThread(kernel).SetLightSessionData(data); // Reply, if we need to. if (data[0] & KLightSession::ReplyFlag) { - KScopedSchedulerLock sl(m_kernel); + KScopedSchedulerLock sl(kernel); // Check that we're open. R_UNLESS(!m_parent->IsClientClosed(), ResultSessionClosed); @@ -139,17 +136,16 @@ Result KLightServerSession::ReplyAndReceive(u32* data) { R_UNLESS(m_current_request != nullptr, ResultInvalidState); // Check that the server thread id is correct. - R_UNLESS(m_server_thread_id == GetCurrentThread(m_kernel).GetId(), ResultInvalidState); + R_UNLESS(m_server_thread_id == GetCurrentThread(kernel).GetId(), ResultInvalidState); // If we can reply, do so. if (!m_current_request->IsTerminationRequested()) { - std::memcpy(m_current_request->GetLightSessionData(), - GetCurrentThread(m_kernel).GetLightSessionData(), KLightSession::DataSize); - m_current_request->EndWait(ResultSuccess); + std::memcpy(m_current_request->GetLightSessionData(), GetCurrentThread(kernel).GetLightSessionData(), KLightSession::DataSize); + m_current_request->EndWait(kernel, ResultSuccess); } // Close our current request. - m_current_request->Close(); + m_current_request->Close(kernel); // Clear our current request. m_current_request = nullptr; @@ -157,14 +153,13 @@ Result KLightServerSession::ReplyAndReceive(u32* data) { } // Create the wait queue for our receive. - ThreadQueueImplForKLightServerSessionReceive wait_queue(m_kernel, - std::addressof(m_server_thread)); + ThreadQueueImplForKLightServerSessionReceive wait_queue(kernel, std::addressof(m_server_thread)); // Receive. while (true) { // Try to receive a request. { - KScopedSchedulerLock sl(m_kernel); + KScopedSchedulerLock sl(kernel); // Check that we aren't already receiving. R_UNLESS(m_server_thread == nullptr, ResultInvalidState); @@ -175,20 +170,19 @@ Result KLightServerSession::ReplyAndReceive(u32* data) { R_UNLESS(!m_parent->IsServerClosed(), ResultSessionClosed); // Check that we're not terminating. - R_UNLESS(!GetCurrentThread(m_kernel).IsTerminationRequested(), - ResultTerminationRequested); + R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(), ResultTerminationRequested); // If we have a request available, use it. if (auto head = m_request_list.begin(); head != m_request_list.end()) { // Set our current request. m_current_request = std::addressof(*head); - m_current_request->Open(); + m_current_request->Open(kernel); // Set our server thread id. - m_server_thread_id = GetCurrentThread(m_kernel).GetId(); + m_server_thread_id = GetCurrentThread(kernel).GetId(); // Copy the client request data. - std::memcpy(GetCurrentThread(m_kernel).GetLightSessionData(), + std::memcpy(GetCurrentThread(kernel).GetLightSessionData(), m_current_request->GetLightSessionData(), KLightSession::DataSize); // We successfully received. @@ -198,51 +192,51 @@ Result KLightServerSession::ReplyAndReceive(u32* data) { // We need to wait for a request to come in. // Check if we were cancelled. - if (GetCurrentThread(m_kernel).IsWaitCancelled()) { - GetCurrentThread(m_kernel).ClearWaitCancelled(); + if (GetCurrentThread(kernel).IsWaitCancelled()) { + GetCurrentThread(kernel).ClearWaitCancelled(); R_THROW(ResultCancelled); } // Mark ourselves as cancellable. - GetCurrentThread(m_kernel).SetCancellable(); + GetCurrentThread(kernel).SetCancellable(); // Wait for a request to come in. - m_server_thread = GetCurrentThreadPointer(m_kernel); - GetCurrentThread(m_kernel).SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC); - GetCurrentThread(m_kernel).BeginWait(std::addressof(wait_queue)); + m_server_thread = GetCurrentThreadPointer(kernel); + GetCurrentThread(kernel).SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC); + GetCurrentThread(kernel).BeginWait(kernel, std::addressof(wait_queue)); } // We waited to receive a request; if our wait failed, return the failing result. - R_TRY(GetCurrentThread(m_kernel).GetWaitResult()); + R_TRY(GetCurrentThread(kernel).GetWaitResult()); } } -void KLightServerSession::CleanupRequests() { +void KLightServerSession::CleanupRequests(KernelCore& kernel) { // Cleanup all pending requests. { - KScopedSchedulerLock sl(m_kernel); + KScopedSchedulerLock sl(kernel); // Handle the current request. if (m_current_request != nullptr) { // Reply to the current request. if (!m_current_request->IsTerminationRequested()) { - m_current_request->EndWait(ResultSessionClosed); + m_current_request->EndWait(kernel, ResultSessionClosed); } // Clear our current request. - m_current_request->Close(); + m_current_request->Close(kernel); m_current_request = nullptr; m_server_thread_id = InvalidThreadId; } // Reply to all other requests. for (auto& thread : m_request_list) { - thread.EndWait(ResultSessionClosed); + thread.EndWait(kernel, ResultSessionClosed); } // Wait up our server thread, if we have one. if (m_server_thread != nullptr) { - m_server_thread->EndWait(ResultSessionClosed); + m_server_thread->EndWait(kernel, ResultSessionClosed); } } } diff --git a/src/core/hle/kernel/k_light_server_session.h b/src/core/hle/kernel/k_light_server_session.h index 67db3f76ca..5849a41c7b 100644 --- a/src/core/hle/kernel/k_light_server_session.h +++ b/src/core/hle/kernel/k_light_server_session.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project @@ -34,19 +34,19 @@ public: m_parent = parent; } - virtual void Destroy() override; + virtual void Destroy(KernelCore& kernel) override; constexpr const KLightSession* GetParent() const { return m_parent; } - Result OnRequest(KThread* request_thread); - Result ReplyAndReceive(u32* data); + Result OnRequest(KernelCore& kernel, KThread* request_thread); + Result ReplyAndReceive(KernelCore& kernel, u32* data); - void OnClientClosed(); + void OnClientClosed(KernelCore& kernel); private: - void CleanupRequests(); + void CleanupRequests(KernelCore& kernel); }; } // namespace Kernel diff --git a/src/core/hle/kernel/k_light_session.cpp b/src/core/hle/kernel/k_light_session.cpp index d8b1e69582..add72d404b 100644 --- a/src/core/hle/kernel/k_light_session.cpp +++ b/src/core/hle/kernel/k_light_session.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -10,15 +13,18 @@ namespace Kernel { KLightSession::KLightSession(KernelCore& kernel) - : KAutoObjectWithSlabHeapAndContainer(kernel), m_server(kernel), m_client(kernel) {} + : KAutoObjectWithSlabHeapAndContainer(kernel) + , m_server(kernel) + , m_client(kernel) +{} KLightSession::~KLightSession() = default; -void KLightSession::Initialize(KClientPort* client_port, uintptr_t name) { +void KLightSession::Initialize(KernelCore& kernel, KClientPort* client_port, uintptr_t name) { // Increment reference count. // Because reference count is one on creation, this will result // in a reference count of two. Thus, when both server and client are closed // this object will be destroyed. - this->Open(); + this->Open(kernel); // Create our sub sessions. KAutoObject::Create(std::addressof(m_server)); @@ -33,49 +39,47 @@ void KLightSession::Initialize(KClientPort* client_port, uintptr_t name) { m_name = name; // Set our owner process. - m_process = GetCurrentProcessPointer(m_kernel); - m_process->Open(); + m_process = GetCurrentProcessPointer(kernel); + m_process->Open(kernel); // Set our port. m_port = client_port; if (m_port != nullptr) { - m_port->Open(); + m_port->Open(kernel); } // Mark initialized. m_initialized = true; } -void KLightSession::Finalize() { +void KLightSession::Finalize(KernelCore& kernel) { if (m_port != nullptr) { - m_port->OnSessionFinalized(); - m_port->Close(); + m_port->OnSessionFinalized(kernel); + m_port->Close(kernel); } } -void KLightSession::OnServerClosed() { +void KLightSession::OnServerClosed(KernelCore& kernel) { if (m_state == State::Normal) { m_state = State::ServerClosed; - m_client.OnServerClosed(); + m_client.OnServerClosed(kernel); } - - this->Close(); + this->Close(kernel); } -void KLightSession::OnClientClosed() { +void KLightSession::OnClientClosed(KernelCore& kernel) { if (m_state == State::Normal) { m_state = State::ClientClosed; - m_server.OnClientClosed(); + m_server.OnClientClosed(kernel); } - - this->Close(); + this->Close(kernel); } -void KLightSession::PostDestroy(uintptr_t arg) { +void KLightSession::PostDestroy(KernelCore& kernel, uintptr_t arg) { // Release the session count resource the owner process holds. KProcess* owner = reinterpret_cast(arg); - owner->ReleaseResource(Svc::LimitableResource::SessionCountMax, 1); - owner->Close(); + owner->ReleaseResource(kernel, Svc::LimitableResource::SessionCountMax, 1); + owner->Close(kernel); } } // namespace Kernel diff --git a/src/core/hle/kernel/k_light_session.h b/src/core/hle/kernel/k_light_session.h index f78d8e6899..5bbb942b35 100644 --- a/src/core/hle/kernel/k_light_session.h +++ b/src/core/hle/kernel/k_light_session.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -43,20 +46,20 @@ public: explicit KLightSession(KernelCore& kernel); ~KLightSession(); - void Initialize(KClientPort* client_port, uintptr_t name); - void Finalize() override; + void Initialize(KernelCore& kernel, KClientPort* client_port, uintptr_t name); + void Finalize(KernelCore& kernel) override; bool IsInitialized() const override { return m_initialized; } uintptr_t GetPostDestroyArgument() const override { - return reinterpret_cast(m_process); + return uintptr_t(m_process); } - static void PostDestroy(uintptr_t arg); + static void PostDestroy(KernelCore& kernel, uintptr_t arg); - void OnServerClosed(); - void OnClientClosed(); + void OnServerClosed(KernelCore& kernel); + void OnClientClosed(KernelCore& kernel); bool IsServerClosed() const { return m_state != State::Normal; @@ -65,8 +68,8 @@ public: return m_state != State::Normal; } - Result OnRequest(KThread* request_thread) { - R_RETURN(m_server.OnRequest(request_thread)); + Result OnRequest(KernelCore& kernel, KThread* request_thread) { + R_RETURN(m_server.OnRequest(kernel, request_thread)); } KLightClientSession& GetClientSession() { diff --git a/src/core/hle/kernel/k_object_name.cpp b/src/core/hle/kernel/k_object_name.cpp index df3a1c4c56..da0b04b450 100644 --- a/src/core/hle/kernel/k_object_name.cpp +++ b/src/core/hle/kernel/k_object_name.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -8,14 +11,14 @@ namespace Kernel { KObjectNameGlobalData::KObjectNameGlobalData(KernelCore& kernel) : m_object_list_lock{kernel} {} KObjectNameGlobalData::~KObjectNameGlobalData() = default; -void KObjectName::Initialize(KAutoObject* obj, const char* name) { +void KObjectName::Initialize(KernelCore& kernel, KAutoObject* obj, const char* name) { // Set member variables. m_object = obj; std::strncpy(m_name.data(), name, sizeof(m_name) - 1); m_name[sizeof(m_name) - 1] = '\x00'; // Open a reference to the object we hold. - m_object->Open(); + m_object->Open(kernel); } bool KObjectName::MatchesName(const char* name) const { @@ -28,7 +31,7 @@ Result KObjectName::NewFromName(KernelCore& kernel, KAutoObject* obj, const char R_UNLESS(new_name != nullptr, ResultOutOfResource); // Initialize the new name. - new_name->Initialize(obj, name); + new_name->Initialize(kernel, obj, name); // Check if there's an existing name. { @@ -47,7 +50,7 @@ Result KObjectName::NewFromName(KernelCore& kernel, KAutoObject* obj, const char } // The object already exists, which is an error condition. Perform cleanup. - obj->Close(); + obj->Close(kernel); KObjectName::Free(kernel, new_name); R_THROW(ResultInvalidState); } @@ -63,7 +66,7 @@ Result KObjectName::Delete(KernelCore& kernel, KAutoObject* obj, const char* com for (auto& name : gd.GetObjectList()) { if (name.MatchesName(compare_name) && obj == name.GetObject()) { // We found a match, clean up its resources. - obj->Close(); + obj->Close(kernel); gd.GetObjectList().erase(gd.GetObjectList().iterator_to(name)); KObjectName::Free(kernel, std::addressof(name)); R_SUCCEED(); @@ -89,14 +92,12 @@ KScopedAutoObject KObjectName::FindImpl(KernelCore& kernel, const c KObjectNameGlobalData& gd{kernel.ObjectNameGlobalData()}; // Try to find a matching object in the global list. - for (const auto& name : gd.GetObjectList()) { - if (name.MatchesName(compare_name)) { - return name.GetObject(); - } - } + for (const auto& name : gd.GetObjectList()) + if (name.MatchesName(compare_name)) + return {kernel, name.GetObject()}; // There's no matching entry in the list. - return nullptr; + return {kernel, nullptr}; } } // namespace Kernel diff --git a/src/core/hle/kernel/k_object_name.h b/src/core/hle/kernel/k_object_name.h index a8876fe370..2ae9edcf51 100644 --- a/src/core/hle/kernel/k_object_name.h +++ b/src/core/hle/kernel/k_object_name.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -41,7 +44,7 @@ public: R_UNLESS(derived != nullptr, ResultNotFound); // Check that the object is closed. - R_UNLESS(derived->IsServerClosed(), ResultInvalidState); + R_UNLESS(derived->IsServerClosed(kernel), ResultInvalidState); R_RETURN(Delete(kernel, obj.GetPointerUnsafe(), name)); } @@ -49,13 +52,13 @@ public: template requires(std::derived_from) static KScopedAutoObject Find(KernelCore& kernel, const char* name) { - return Find(kernel, name); + return {kernel, static_cast(Find(kernel, name).GetPointerUnsafe())}; } private: static KScopedAutoObject FindImpl(KernelCore& kernel, const char* name); - void Initialize(KAutoObject* obj, const char* name); + void Initialize(KernelCore& kernel, KAutoObject* obj, const char* name); bool MatchesName(const char* name) const; KAutoObject* GetObject() const { diff --git a/src/core/hle/kernel/k_page_group.cpp b/src/core/hle/kernel/k_page_group.cpp index d8c644a336..ed7f219718 100644 --- a/src/core/hle/kernel/k_page_group.cpp +++ b/src/core/hle/kernel/k_page_group.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -21,8 +24,8 @@ void KPageGroup::Finalize() { m_last_block = nullptr; } -void KPageGroup::CloseAndReset() { - auto& mm = m_kernel.MemoryManager(); +void KPageGroup::CloseAndReset(KernelCore& kernel) { + auto& mm = kernel.MemoryManager(); KBlockInfo* cur = m_first_block; while (cur != nullptr) { @@ -76,28 +79,22 @@ Result KPageGroup::AddBlock(KPhysicalAddress addr, size_t num_pages) { R_SUCCEED(); } -void KPageGroup::Open() const { - auto& mm = m_kernel.MemoryManager(); - - for (const auto& it : *this) { +void KPageGroup::Open(KernelCore& kernel) const { + auto& mm = kernel.MemoryManager(); + for (const auto& it : *this) mm.Open(it.GetAddress(), it.GetNumPages()); - } } -void KPageGroup::OpenFirst() const { - auto& mm = m_kernel.MemoryManager(); - - for (const auto& it : *this) { +void KPageGroup::OpenFirst(KernelCore& kernel) const { + auto& mm = kernel.MemoryManager(); + for (const auto& it : *this) mm.OpenFirst(it.GetAddress(), it.GetNumPages()); - } } -void KPageGroup::Close() const { - auto& mm = m_kernel.MemoryManager(); - - for (const auto& it : *this) { +void KPageGroup::Close(KernelCore& kernel) const { + auto& mm = kernel.MemoryManager(); + for (const auto& it : *this) mm.Close(it.GetAddress(), it.GetNumPages()); - } } bool KPageGroup::IsEquivalentTo(const KPageGroup& rhs) const { diff --git a/src/core/hle/kernel/k_page_group.h b/src/core/hle/kernel/k_page_group.h index de9d63a8d2..60a6faa2fd 100644 --- a/src/core/hle/kernel/k_page_group.h +++ b/src/core/hle/kernel/k_page_group.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -139,12 +142,12 @@ public: }; explicit KPageGroup(KernelCore& kernel, KBlockInfoManager* m) - : m_kernel{kernel}, m_manager{m} {} + : m_manager{m} {} ~KPageGroup() { this->Finalize(); } - void CloseAndReset(); + void CloseAndReset(KernelCore& kernel); void Finalize(); Iterator begin() const { @@ -158,9 +161,9 @@ public: } Result AddBlock(KPhysicalAddress addr, size_t num_pages); - void Open() const; - void OpenFirst() const; - void Close() const; + void Open(KernelCore& kernel) const; + void OpenFirst(KernelCore& kernel) const; + void Close(KernelCore& kernel) const; size_t GetNumPages() const; @@ -175,7 +178,6 @@ public: } private: - KernelCore& m_kernel; KBlockInfo* m_first_block{}; KBlockInfo* m_last_block{}; KBlockInfoManager* m_manager{}; @@ -183,21 +185,24 @@ private: class KScopedPageGroup { public: - explicit KScopedPageGroup(const KPageGroup* gp, bool not_first = true) : m_pg(gp) { + explicit KScopedPageGroup(KernelCore& kernel, const KPageGroup* gp, bool not_first = true) + : m_kernel{kernel} + , m_pg{gp} + { if (m_pg) { if (not_first) { - m_pg->Open(); + m_pg->Open(kernel); } else { - m_pg->OpenFirst(); + m_pg->OpenFirst(kernel); } } } - explicit KScopedPageGroup(const KPageGroup& gp, bool not_first = true) - : KScopedPageGroup(std::addressof(gp), not_first) {} + explicit KScopedPageGroup(KernelCore& kernel, const KPageGroup& gp, bool not_first = true) + : KScopedPageGroup(kernel, std::addressof(gp), not_first) {} + ~KScopedPageGroup() { - if (m_pg) { - m_pg->Close(); - } + if (m_pg) + m_pg->Close(m_kernel); } void CancelClose() { @@ -205,6 +210,7 @@ public: } private: + KernelCore& m_kernel; const KPageGroup* m_pg{}; }; diff --git a/src/core/hle/kernel/k_page_table_base.cpp b/src/core/hle/kernel/k_page_table_base.cpp index 3696f9f251..a727355a9f 100644 --- a/src/core/hle/kernel/k_page_table_base.cpp +++ b/src/core/hle/kernel/k_page_table_base.cpp @@ -127,22 +127,22 @@ constexpr Common::MemoryPermission ConvertToMemoryPermission(KMemoryPermission p } // namespace -void KPageTableBase::MemoryRange::Open() { +void KPageTableBase::MemoryRange::Open(KernelCore& kernel) { // If the range contains heap pages, open them. if (this->IsHeap()) { - m_kernel.MemoryManager().Open(this->GetAddress(), this->GetSize() / PageSize); + kernel.MemoryManager().Open(this->GetAddress(), this->GetSize() / PageSize); } } -void KPageTableBase::MemoryRange::Close() { +void KPageTableBase::MemoryRange::Close(KernelCore& kernel) { // If the range contains heap pages, close them. if (this->IsHeap()) { - m_kernel.MemoryManager().Close(this->GetAddress(), this->GetSize() / PageSize); + kernel.MemoryManager().Close(this->GetAddress(), this->GetSize() / PageSize); } } KPageTableBase::KPageTableBase(KernelCore& kernel) - : m_kernel(kernel), m_system(kernel.System()), m_general_lock(kernel), + : m_system(kernel.System()), m_general_lock(kernel), m_map_physical_memory_lock(kernel), m_device_map_lock(kernel) {} KPageTableBase::~KPageTableBase() = default; @@ -177,9 +177,9 @@ Result KPageTableBase::InitializeForKernel(bool is_64_bit, KVirtualAddress start m_mapped_ipc_server_memory = 0; m_memory_block_slab_manager = - m_kernel.GetSystemSystemResource().GetMemoryBlockSlabManagerPointer(); - m_block_info_manager = m_kernel.GetSystemSystemResource().GetBlockInfoManagerPointer(); - m_resource_limit = m_kernel.GetSystemResourceLimit(); + m_system.Kernel().GetSystemSystemResource().GetMemoryBlockSlabManagerPointer(); + m_block_info_manager = m_system.Kernel().GetSystemSystemResource().GetBlockInfoManagerPointer(); + m_resource_limit = m_system.Kernel().GetSystemResourceLimit(); m_allocate_option = KMemoryManager::EncodeOption(KMemoryManager::Pool::System, KMemoryManager::Direction::FromFront); @@ -469,11 +469,11 @@ void KPageTableBase::Finalize() { } // Get physical pages. - KPageGroup pg(m_kernel, m_block_info_manager); + KPageGroup pg(m_system.Kernel(), m_block_info_manager); this->MakePageGroup(pg, addr, size / PageSize); // Free the pages. - pg.CloseAndReset(); + pg.CloseAndReset(m_system.Kernel()); }; // Finalize memory blocks. @@ -490,16 +490,16 @@ void KPageTableBase::Finalize() { // Release any insecure mapped memory. if (m_mapped_insecure_memory) { if (auto* const insecure_resource_limit = - KSystemControl::GetInsecureMemoryResourceLimit(m_kernel); + KSystemControl::GetInsecureMemoryResourceLimit(m_system.Kernel()); insecure_resource_limit != nullptr) { - insecure_resource_limit->Release(Svc::LimitableResource::PhysicalMemoryMax, + insecure_resource_limit->Release(m_system.Kernel(), Svc::LimitableResource::PhysicalMemoryMax, m_mapped_insecure_memory); } } // Release any ipc server memory. if (m_mapped_ipc_server_memory) { - m_resource_limit->Release(Svc::LimitableResource::PhysicalMemoryMax, + m_resource_limit->Release(m_system.Kernel(), Svc::LimitableResource::PhysicalMemoryMax, m_mapped_ipc_server_memory); } } @@ -833,7 +833,7 @@ Result KPageTableBase::LockMemoryAndOpen(KPageGroup* out_pg, KPhysicalAddress* o // If we have an output group, open. if (out_pg) { - out_pg->Open(); + out_pg->Open(m_system.Kernel()); } R_SUCCEED(); @@ -1041,7 +1041,7 @@ Result KPageTableBase::MapMemory(KProcessAddress dst_address, KProcessAddress sr const size_t num_pages = size / PageSize; // Create page groups for the memory being unmapped. - KPageGroup pg(m_kernel, m_block_info_manager); + KPageGroup pg(m_system.Kernel(), m_block_info_manager); // Create the page group representing the source. R_TRY(this->MakePageGroup(pg, src_address, num_pages)); @@ -1129,7 +1129,7 @@ Result KPageTableBase::UnmapMemory(KProcessAddress dst_address, KProcessAddress const size_t num_pages = size / PageSize; // Create page groups for the memory being unmapped. - KPageGroup pg(m_kernel, m_block_info_manager); + KPageGroup pg(m_system.Kernel(), m_block_info_manager); // Create the page group representing the destination. R_TRY(this->MakePageGroup(pg, dst_address, num_pages)); @@ -1217,7 +1217,7 @@ Result KPageTableBase::MapCodeMemory(KProcessAddress dst_address, KProcessAddres const size_t num_pages = size / PageSize; // Create page groups for the memory being unmapped. - KPageGroup pg(m_kernel, m_block_info_manager); + KPageGroup pg(m_system.Kernel(), m_block_info_manager); // Create the page group representing the source. R_TRY(this->MakePageGroup(pg, src_address, num_pages)); @@ -1312,7 +1312,7 @@ Result KPageTableBase::UnmapCodeMemory(KProcessAddress dst_address, KProcessAddr bool reprotected_pages = false; SCOPE_EXIT { if (reprotected_pages && any_code_pages) { - InvalidateInstructionCache(m_kernel, this, dst_address, size); + InvalidateInstructionCache(m_system.Kernel(), this, dst_address, size); } }; @@ -1322,7 +1322,7 @@ Result KPageTableBase::UnmapCodeMemory(KProcessAddress dst_address, KProcessAddr const size_t num_pages = size / PageSize; // Create page groups for the memory being unmapped. - KPageGroup pg(m_kernel, m_block_info_manager); + KPageGroup pg(m_system.Kernel(), m_block_info_manager); // Create the page group representing the destination. R_TRY(this->MakePageGroup(pg, dst_address, num_pages)); @@ -1383,19 +1383,19 @@ Result KPageTableBase::UnmapCodeMemory(KProcessAddress dst_address, KProcessAddr Result KPageTableBase::MapInsecureMemory(KProcessAddress address, size_t size) { // Get the insecure memory resource limit and pool. - auto* const insecure_resource_limit = KSystemControl::GetInsecureMemoryResourceLimit(m_kernel); + auto* const insecure_resource_limit = KSystemControl::GetInsecureMemoryResourceLimit(m_system.Kernel()); const auto insecure_pool = static_cast(KSystemControl::GetInsecureMemoryPool()); // Reserve the insecure memory. // NOTE: ResultOutOfMemory is returned here instead of the usual LimitReached. - KScopedResourceReservation memory_reservation(insecure_resource_limit, + KScopedResourceReservation memory_reservation(m_system.Kernel(), insecure_resource_limit, Svc::LimitableResource::PhysicalMemoryMax, size); R_UNLESS(memory_reservation.Succeeded(), ResultOutOfMemory); // Allocate pages for the insecure memory. - KPageGroup pg(m_kernel, m_block_info_manager); - R_TRY(m_kernel.MemoryManager().AllocateAndOpen( + KPageGroup pg(m_system.Kernel(), m_block_info_manager); + R_TRY(m_system.Kernel().MemoryManager().AllocateAndOpen( std::addressof(pg), size / PageSize, KMemoryManager::EncodeOption(insecure_pool, KMemoryManager::Direction::FromFront))); @@ -1403,7 +1403,7 @@ Result KPageTableBase::MapInsecureMemory(KProcessAddress address, size_t size) { // If the mapping succeeds, each page will gain an extra reference, otherwise they will be freed // automatically. SCOPE_EXIT { - pg.Close(); + pg.Close(m_system.Kernel()); }; // Clear all the newly allocated pages. @@ -1491,9 +1491,9 @@ Result KPageTableBase::UnmapInsecureMemory(KProcessAddress address, size_t size) // Release the insecure memory from the insecure limit. if (auto* const insecure_resource_limit = - KSystemControl::GetInsecureMemoryResourceLimit(m_kernel); + KSystemControl::GetInsecureMemoryResourceLimit(m_system.Kernel()); insecure_resource_limit != nullptr) { - insecure_resource_limit->Release(Svc::LimitableResource::PhysicalMemoryMax, size); + insecure_resource_limit->Release(m_system.Kernel(), Svc::LimitableResource::PhysicalMemoryMax, size); } R_SUCCEED(); @@ -1603,15 +1603,15 @@ Result KPageTableBase::AllocateAndMapPagesImpl(PageLinkedList* page_list, KProce ASSERT(this->IsLockedByCurrentThread()); // Create a page group to hold the pages we allocate. - KPageGroup pg(m_kernel, m_block_info_manager); + KPageGroup pg(m_system.Kernel(), m_block_info_manager); // Allocate the pages. R_TRY( - m_kernel.MemoryManager().AllocateAndOpen(std::addressof(pg), num_pages, m_allocate_option)); + m_system.Kernel().MemoryManager().AllocateAndOpen(std::addressof(pg), num_pages, m_allocate_option)); // Ensure that the page group is closed when we're done working with it. SCOPE_EXIT { - pg.Close(); + pg.Close(m_system.Kernel()); }; // Clear all pages. @@ -1992,7 +1992,7 @@ Result KPageTableBase::SetProcessMemoryPermission(KProcessAddress addr, size_t s KMemoryAttribute::All, KMemoryAttribute::None)); // Make a new page group for the region. - KPageGroup pg(m_kernel, m_block_info_manager); + KPageGroup pg(m_system.Kernel(), m_block_info_manager); // Determine new perm/state. const KMemoryPermission new_perm = ConvertToKMemoryPermission(svc_perm); @@ -2048,9 +2048,9 @@ Result KPageTableBase::SetProcessMemoryPermission(KProcessAddress addr, size_t s // Ensure cache coherency, if we're setting pages as executable. if (is_x) { for (const auto& block : pg) { - StoreDataCache(GetHeapVirtualPointer(m_kernel, block.GetAddress()), block.GetSize()); + StoreDataCache(GetHeapVirtualPointer(m_system.Kernel(), block.GetAddress()), block.GetSize()); } - InvalidateInstructionCache(m_kernel, this, addr, size); + InvalidateInstructionCache(m_system.Kernel(), this, addr, size); } R_SUCCEED(); @@ -2158,7 +2158,7 @@ Result KPageTableBase::SetHeapSize(KProcessAddress* out, size_t size) { false, unmap_properties, OperationType::Unmap, false)); // Release the memory from the resource limit. - m_resource_limit->Release(Svc::LimitableResource::PhysicalMemoryMax, + m_resource_limit->Release(m_system.Kernel(), Svc::LimitableResource::PhysicalMemoryMax, num_pages * PageSize); // Apply the memory block update. @@ -2188,20 +2188,20 @@ Result KPageTableBase::SetHeapSize(KProcessAddress* out, size_t size) { } // Reserve memory for the heap extension. - KScopedResourceReservation memory_reservation( + KScopedResourceReservation memory_reservation(m_system.Kernel(), m_resource_limit, Svc::LimitableResource::PhysicalMemoryMax, allocation_size); R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); // Allocate pages for the heap extension. - KPageGroup pg(m_kernel, m_block_info_manager); - R_TRY(m_kernel.MemoryManager().AllocateAndOpen(std::addressof(pg), allocation_size / PageSize, + KPageGroup pg(m_system.Kernel(), m_block_info_manager); + R_TRY(m_system.Kernel().MemoryManager().AllocateAndOpen(std::addressof(pg), allocation_size / PageSize, m_allocate_option)); // Close the opened pages when we're done with them. // If the mapping succeeds, each page will gain an extra reference, otherwise they will be freed // automatically. SCOPE_EXIT { - pg.Close(); + pg.Close(m_system.Kernel()); }; // Clear all the newly allocated pages. @@ -2406,7 +2406,7 @@ Result KPageTableBase::MapIoImpl(KProcessAddress* out, PageLinkedList* page_list ASSERT(this->CanContain(region_start, region_size, state)); // Locate the memory region. - const KMemoryRegion* region = KMemoryLayout::Find(m_kernel.MemoryLayout(), phys_addr); + const KMemoryRegion* region = KMemoryLayout::Find(m_system.Kernel().MemoryLayout(), phys_addr); R_UNLESS(region != nullptr, ResultInvalidAddress); ASSERT(region->Contains(GetInteger(phys_addr))); @@ -2640,7 +2640,7 @@ Result KPageTableBase::MapStatic(KPhysicalAddress phys_addr, size_t size, KMemor const size_t region_num_pages = region_size / PageSize; // Locate the memory region. - const KMemoryRegion* region = KMemoryLayout::Find(m_kernel.MemoryLayout(), phys_addr); + const KMemoryRegion* region = KMemoryLayout::Find(m_system.Kernel().MemoryLayout(), phys_addr); R_UNLESS(region != nullptr, ResultInvalidAddress); ASSERT(region->Contains(GetInteger(phys_addr))); @@ -2707,7 +2707,7 @@ Result KPageTableBase::MapStatic(KPhysicalAddress phys_addr, size_t size, KMemor Result KPageTableBase::MapRegion(KMemoryRegionType region_type, KMemoryPermission perm) { // Get the memory region. const KMemoryRegion* region = - m_kernel.MemoryLayout().GetPhysicalMemoryRegionTree().FindFirstDerived(region_type); + m_system.Kernel().MemoryLayout().GetPhysicalMemoryRegionTree().FindFirstDerived(region_type); R_UNLESS(region != nullptr, ResultOutOfRange); // Check that the region is valid. @@ -3004,7 +3004,7 @@ Result KPageTableBase::MakeAndOpenPageGroup(KPageGroup* out, KProcessAddress add R_TRY(this->MakePageGroup(*out, address, num_pages)); // Open a new reference to the pages in the group. - out->Open(); + out->Open(m_system.Kernel()); R_SUCCEED(); } @@ -3051,7 +3051,7 @@ Result KPageTableBase::InvalidateProcessDataCache(KProcessAddress address, size_ // Invalidate the block. if (cur_size > 0) { // NOTE: Nintendo does not check the result of invalidation. - InvalidateDataCache(GetLinearMappedVirtualPointer(m_kernel, cur_addr), cur_size); + InvalidateDataCache(GetLinearMappedVirtualPointer(m_system.Kernel(), cur_addr), cur_size); } // Advance. @@ -3075,7 +3075,7 @@ Result KPageTableBase::InvalidateProcessDataCache(KProcessAddress address, size_ // Invalidate the last block. if (cur_size > 0) { // NOTE: Nintendo does not check the result of invalidation. - InvalidateDataCache(GetLinearMappedVirtualPointer(m_kernel, cur_addr), cur_size); + InvalidateDataCache(GetLinearMappedVirtualPointer(m_system.Kernel(), cur_addr), cur_size); } R_SUCCEED(); @@ -3083,7 +3083,7 @@ Result KPageTableBase::InvalidateProcessDataCache(KProcessAddress address, size_ Result KPageTableBase::InvalidateCurrentProcessDataCache(KProcessAddress address, size_t size) { // Check pre-condition: this is being called on the current process. - ASSERT(this == std::addressof(GetCurrentProcess(m_kernel).GetPageTable().GetBasePageTable())); + ASSERT(this == std::addressof(GetCurrentProcess(m_system.Kernel()).GetPageTable().GetBasePageTable())); // Check that the region is in range. R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); @@ -3144,7 +3144,7 @@ Result KPageTableBase::ReadDebugMemory(KProcessAddress dst_address, KProcessAddr // Copy as much aligned data as we can. if (cur_size >= sizeof(u32)) { const size_t copy_size = Common::AlignDown(cur_size, sizeof(u32)); - const void* copy_src = GetLinearMappedVirtualPointer(m_kernel, cur_addr); + const void* copy_src = GetLinearMappedVirtualPointer(m_system.Kernel(), cur_addr); FlushDataCache(copy_src, copy_size); R_UNLESS(dst_memory.WriteBlock(dst_address, copy_src, copy_size), ResultInvalidPointer); @@ -3155,7 +3155,7 @@ Result KPageTableBase::ReadDebugMemory(KProcessAddress dst_address, KProcessAddr // Copy remaining data. if (cur_size > 0) { - const void* copy_src = GetLinearMappedVirtualPointer(m_kernel, cur_addr); + const void* copy_src = GetLinearMappedVirtualPointer(m_system.Kernel(), cur_addr); FlushDataCache(copy_src, cur_size); R_UNLESS(dst_memory.WriteBlock(dst_address, copy_src, cur_size), ResultInvalidPointer); } @@ -3240,11 +3240,11 @@ Result KPageTableBase::WriteDebugMemory(KProcessAddress dst_address, KProcessAdd // Copy as much aligned data as we can. if (cur_size >= sizeof(u32)) { const size_t copy_size = Common::AlignDown(cur_size, sizeof(u32)); - void* copy_dst = GetLinearMappedVirtualPointer(m_kernel, cur_addr); + void* copy_dst = GetLinearMappedVirtualPointer(m_system.Kernel(), cur_addr); R_UNLESS(src_memory.ReadBlock(src_address, copy_dst, copy_size), ResultInvalidCurrentMemory); - StoreDataCache(GetLinearMappedVirtualPointer(m_kernel, cur_addr), copy_size); + StoreDataCache(GetLinearMappedVirtualPointer(m_system.Kernel(), cur_addr), copy_size); src_address += copy_size; cur_addr += copy_size; @@ -3253,11 +3253,11 @@ Result KPageTableBase::WriteDebugMemory(KProcessAddress dst_address, KProcessAdd // Copy remaining data. if (cur_size > 0) { - void* copy_dst = GetLinearMappedVirtualPointer(m_kernel, cur_addr); + void* copy_dst = GetLinearMappedVirtualPointer(m_system.Kernel(), cur_addr); R_UNLESS(src_memory.ReadBlock(src_address, copy_dst, cur_size), ResultInvalidCurrentMemory); - StoreDataCache(GetLinearMappedVirtualPointer(m_kernel, cur_addr), cur_size); + StoreDataCache(GetLinearMappedVirtualPointer(m_system.Kernel(), cur_addr), cur_size); } R_SUCCEED(); @@ -3295,7 +3295,7 @@ Result KPageTableBase::WriteDebugMemory(KProcessAddress dst_address, KProcessAdd R_TRY(PerformCopy()); // Invalidate the instruction cache, as this svc allows modifying executable pages. - InvalidateInstructionCache(m_kernel, this, dst_address, size); + InvalidateInstructionCache(m_system.Kernel(), this, dst_address, size); R_SUCCEED(); } @@ -3311,7 +3311,7 @@ Result KPageTableBase::ReadIoMemoryImpl(KProcessAddress dst_addr, KPhysicalAddre const size_t map_size = map_end - map_start; // Get the memory reference to write into. - auto& dst_memory = GetCurrentMemory(m_kernel); + auto& dst_memory = GetCurrentMemory(m_system.Kernel()); // We're going to perform an update, so create a helper. KScopedPageTableUpdater updater(this); @@ -3347,7 +3347,7 @@ Result KPageTableBase::WriteIoMemoryImpl(KPhysicalAddress phys_addr, KProcessAdd const size_t map_size = map_end - map_start; // Get the memory reference to read from. - auto& src_memory = GetCurrentMemory(m_kernel); + auto& src_memory = GetCurrentMemory(m_system.Kernel()); // We're going to perform an update, so create a helper. KScopedPageTableUpdater updater(this); @@ -3379,7 +3379,7 @@ Result KPageTableBase::ReadDebugIoMemory(KProcessAddress dst_address, KProcessAd // We need to lock both this table, and the current process's table, so set up some aliases. KPageTableBase& src_page_table = *this; - KPageTableBase& dst_page_table = GetCurrentProcess(m_kernel).GetPageTable().GetBasePageTable(); + KPageTableBase& dst_page_table = GetCurrentProcess(m_system.Kernel()).GetPageTable().GetBasePageTable(); // Acquire the table locks. KScopedLightLockPair lk(src_page_table.m_general_lock, dst_page_table.m_general_lock); @@ -3421,7 +3421,7 @@ Result KPageTableBase::WriteDebugIoMemory(KProcessAddress dst_address, KProcessA // We need to lock both this table, and the current process's table, so set up some aliases. KPageTableBase& src_page_table = *this; - KPageTableBase& dst_page_table = GetCurrentProcess(m_kernel).GetPageTable().GetBasePageTable(); + KPageTableBase& dst_page_table = GetCurrentProcess(m_system.Kernel()).GetPageTable().GetBasePageTable(); // Acquire the table locks. KScopedLightLockPair lk(src_page_table.m_general_lock, dst_page_table.m_general_lock); @@ -3606,7 +3606,7 @@ Result KPageTableBase::OpenMemoryRangeForMapDeviceAddressSpace(KPageTableBase::M KMemoryAttribute::IpcLocked | KMemoryAttribute::Locked, KMemoryAttribute::None)); // We got the range, so open it. - out->Open(); + out->Open(m_system.Kernel()); R_SUCCEED(); } @@ -3624,7 +3624,7 @@ Result KPageTableBase::OpenMemoryRangeForUnmapDeviceAddressSpace(MemoryRange* ou KMemoryAttribute::DeviceShared | KMemoryAttribute::Locked, KMemoryAttribute::DeviceShared)); // We got the range, so open it. - out->Open(); + out->Open(m_system.Kernel()); R_SUCCEED(); } @@ -3697,7 +3697,7 @@ Result KPageTableBase::OpenMemoryRangeForProcessCacheOperation(MemoryRange* out, KMemoryAttribute::None)); // We got the range, so open it. - out->Open(); + out->Open(m_system.Kernel()); R_SUCCEED(); } @@ -3710,7 +3710,7 @@ Result KPageTableBase::CopyMemoryFromLinearToUser( R_UNLESS(this->Contains(src_addr, size), ResultInvalidCurrentMemory); // Get the destination memory reference. - auto& dst_memory = GetCurrentMemory(m_kernel); + auto& dst_memory = GetCurrentMemory(m_system.Kernel()); // Copy the memory. { @@ -3745,7 +3745,7 @@ Result KPageTableBase::CopyMemoryFromLinearToUser( if (cur_size >= sizeof(u32)) { const size_t copy_size = Common::AlignDown(cur_size, sizeof(u32)); R_UNLESS(dst_memory.WriteBlock(dst_addr, - GetLinearMappedVirtualPointer(m_kernel, cur_addr), + GetLinearMappedVirtualPointer(m_system.Kernel(), cur_addr), copy_size), ResultInvalidCurrentMemory); @@ -3757,7 +3757,7 @@ Result KPageTableBase::CopyMemoryFromLinearToUser( // Copy remaining data. if (cur_size > 0) { R_UNLESS(dst_memory.WriteBlock( - dst_addr, GetLinearMappedVirtualPointer(m_kernel, cur_addr), cur_size), + dst_addr, GetLinearMappedVirtualPointer(m_system.Kernel(), cur_addr), cur_size), ResultInvalidCurrentMemory); } @@ -3836,7 +3836,7 @@ Result KPageTableBase::CopyMemoryFromLinearToKernel( R_UNLESS(IsLinearMappedPhysicalAddress(cur_addr), ResultInvalidCurrentMemory); // Copy the data. - std::memcpy(buffer, GetLinearMappedVirtualPointer(m_kernel, cur_addr), cur_size); + std::memcpy(buffer, GetLinearMappedVirtualPointer(m_system.Kernel(), cur_addr), cur_size); R_SUCCEED(); }; @@ -3884,7 +3884,7 @@ Result KPageTableBase::CopyMemoryFromUserToLinear( R_UNLESS(this->Contains(dst_addr, size), ResultInvalidCurrentMemory); // Get the source memory reference. - auto& src_memory = GetCurrentMemory(m_kernel); + auto& src_memory = GetCurrentMemory(m_system.Kernel()); // Copy the memory. { @@ -3919,7 +3919,7 @@ Result KPageTableBase::CopyMemoryFromUserToLinear( if (cur_size >= sizeof(u32)) { const size_t copy_size = Common::AlignDown(cur_size, sizeof(u32)); R_UNLESS(src_memory.ReadBlock(src_addr, - GetLinearMappedVirtualPointer(m_kernel, cur_addr), + GetLinearMappedVirtualPointer(m_system.Kernel(), cur_addr), copy_size), ResultInvalidCurrentMemory); src_addr += copy_size; @@ -3930,7 +3930,7 @@ Result KPageTableBase::CopyMemoryFromUserToLinear( // Copy remaining data. if (cur_size > 0) { R_UNLESS(src_memory.ReadBlock( - src_addr, GetLinearMappedVirtualPointer(m_kernel, cur_addr), cur_size), + src_addr, GetLinearMappedVirtualPointer(m_system.Kernel(), cur_addr), cur_size), ResultInvalidCurrentMemory); } @@ -4011,7 +4011,7 @@ Result KPageTableBase::CopyMemoryFromKernelToLinear(KProcessAddress dst_addr, si R_UNLESS(IsLinearMappedPhysicalAddress(cur_addr), ResultInvalidCurrentMemory); // Copy the data. - std::memcpy(GetLinearMappedVirtualPointer(m_kernel, cur_addr), buffer, cur_size); + std::memcpy(GetLinearMappedVirtualPointer(m_system.Kernel(), cur_addr), buffer, cur_size); R_SUCCEED(); }; @@ -4162,8 +4162,8 @@ Result KPageTableBase::CopyMemoryFromHeapToHeap( R_UNLESS(IsHeapPhysicalAddress(cur_dst_addr), ResultInvalidCurrentMemory); // Copy the data. - std::memcpy(GetHeapVirtualPointer(m_kernel, cur_dst_addr), - GetHeapVirtualPointer(m_kernel, cur_src_addr), cur_copy_size); + std::memcpy(GetHeapVirtualPointer(m_system.Kernel(), cur_dst_addr), + GetHeapVirtualPointer(m_system.Kernel(), cur_src_addr), cur_copy_size); // Update. cur_src_block_addr = src_next_entry.phys_addr; @@ -4296,8 +4296,8 @@ Result KPageTableBase::CopyMemoryFromHeapToHeapWithoutCheckDestination( R_UNLESS(IsHeapPhysicalAddress(cur_dst_addr), ResultInvalidCurrentMemory); // Copy the data. - std::memcpy(GetHeapVirtualPointer(m_kernel, cur_dst_addr), - GetHeapVirtualPointer(m_kernel, cur_src_addr), cur_copy_size); + std::memcpy(GetHeapVirtualPointer(m_system.Kernel(), cur_dst_addr), + GetHeapVirtualPointer(m_system.Kernel(), cur_src_addr), cur_copy_size); // Update. cur_src_block_addr = src_next_entry.phys_addr; @@ -4493,7 +4493,7 @@ Result KPageTableBase::SetupForIpcServer(KProcessAddress* out_addr, size_t size, // Reserve space for any partial pages we allocate. const size_t unmapped_size = aligned_src_size - mapping_src_size; - KScopedResourceReservation memory_reservation( + KScopedResourceReservation memory_reservation(m_system.Kernel(), m_resource_limit, Svc::LimitableResource::PhysicalMemoryMax, unmapped_size); R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); @@ -4506,10 +4506,10 @@ Result KPageTableBase::SetupForIpcServer(KProcessAddress* out_addr, size_t size, // free on scope exit. SCOPE_EXIT { if (start_partial_page != 0) { - m_kernel.MemoryManager().Close(start_partial_page, 1); + m_system.Kernel().MemoryManager().Close(start_partial_page, 1); } if (end_partial_page != 0) { - m_kernel.MemoryManager().Close(end_partial_page, 1); + m_system.Kernel().MemoryManager().Close(end_partial_page, 1); } }; @@ -4526,7 +4526,7 @@ Result KPageTableBase::SetupForIpcServer(KProcessAddress* out_addr, size_t size, // Allocate the start page as needed. if (aligned_src_start < mapping_src_start) { start_partial_page = - m_kernel.MemoryManager().AllocateAndOpenContinuous(1, 1, m_allocate_option); + m_system.Kernel().MemoryManager().AllocateAndOpenContinuous(1, 1, m_allocate_option); R_UNLESS(start_partial_page != 0, ResultOutOfMemory); } @@ -4534,7 +4534,7 @@ Result KPageTableBase::SetupForIpcServer(KProcessAddress* out_addr, size_t size, if (mapping_src_end < aligned_src_end && (aligned_src_start < mapping_src_end || aligned_src_start == mapping_src_start)) { end_partial_page = - m_kernel.MemoryManager().AllocateAndOpenContinuous(1, 1, m_allocate_option); + m_system.Kernel().MemoryManager().AllocateAndOpenContinuous(1, 1, m_allocate_option); R_UNLESS(end_partial_page != 0, ResultOutOfMemory); } @@ -4560,7 +4560,7 @@ Result KPageTableBase::SetupForIpcServer(KProcessAddress* out_addr, size_t size, // Map the start page, if we have one. if (start_partial_page != 0) { // Ensure the page holds correct data. - u8* const start_partial_virt = GetHeapVirtualPointer(m_kernel, start_partial_page); + u8* const start_partial_virt = GetHeapVirtualPointer(m_system.Kernel(), start_partial_page); if (send) { const size_t partial_offset = src_start - aligned_src_start; size_t copy_size, clear_size; @@ -4574,7 +4574,7 @@ Result KPageTableBase::SetupForIpcServer(KProcessAddress* out_addr, size_t size, std::memset(start_partial_virt, fill_val, partial_offset); std::memcpy(start_partial_virt + partial_offset, - GetHeapVirtualPointer(m_kernel, cur_block_addr) + partial_offset, + GetHeapVirtualPointer(m_system.Kernel(), cur_block_addr) + partial_offset, copy_size); if (clear_size > 0) { std::memset(start_partial_virt + partial_offset + copy_size, fill_val, clear_size); @@ -4663,10 +4663,10 @@ Result KPageTableBase::SetupForIpcServer(KProcessAddress* out_addr, size_t size, // Map the end page, if we have one. if (end_partial_page != 0) { // Ensure the page holds correct data. - u8* const end_partial_virt = GetHeapVirtualPointer(m_kernel, end_partial_page); + u8* const end_partial_virt = GetHeapVirtualPointer(m_system.Kernel(), end_partial_page); if (send) { const size_t copy_size = src_end - mapping_src_end; - std::memcpy(end_partial_virt, GetHeapVirtualPointer(m_kernel, cur_block_addr), + std::memcpy(end_partial_virt, GetHeapVirtualPointer(m_system.Kernel(), cur_block_addr), copy_size); std::memset(end_partial_virt + copy_size, fill_val, PageSize - copy_size); } else { @@ -4799,7 +4799,7 @@ Result KPageTableBase::CleanupForIpcServer(KProcessAddress address, size_t size, const KProcessAddress mapping_start = Common::AlignUp(GetInteger(address), PageSize); const KProcessAddress mapping_end = Common::AlignDown(GetInteger(address) + size, PageSize); const size_t mapping_size = (mapping_start < mapping_end) ? mapping_end - mapping_start : 0; - m_resource_limit->Release(Svc::LimitableResource::PhysicalMemoryMax, + m_resource_limit->Release(m_system.Kernel(), Svc::LimitableResource::PhysicalMemoryMax, aligned_size - mapping_size); R_SUCCEED(); @@ -5168,20 +5168,20 @@ Result KPageTableBase::MapPhysicalMemory(KProcessAddress address, size_t size) { // Allocate and map the memory. { // Reserve the memory from the process resource limit. - KScopedResourceReservation memory_reservation( + KScopedResourceReservation memory_reservation(m_system.Kernel(), m_resource_limit, Svc::LimitableResource::PhysicalMemoryMax, size - mapped_size); R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); // Allocate pages for the new memory. - KPageGroup pg(m_kernel, m_block_info_manager); - R_TRY(m_kernel.MemoryManager().AllocateForProcess( + KPageGroup pg(m_system.Kernel(), m_block_info_manager); + R_TRY(m_system.Kernel().MemoryManager().AllocateForProcess( std::addressof(pg), (size - mapped_size) / PageSize, m_allocate_option, - GetCurrentProcess(m_kernel).GetId(), m_heap_fill_value)); + GetCurrentProcess(m_system.Kernel()).GetId(), m_heap_fill_value)); // If we fail in the next bit (or retry), we need to cleanup the pages. auto pg_guard = SCOPE_GUARD { - pg.OpenFirst(); - pg.Close(); + pg.OpenFirst(m_system.Kernel()); + pg.Close(m_system.Kernel()); }; // Map the memory. @@ -5304,12 +5304,12 @@ Result KPageTableBase::MapPhysicalMemory(KProcessAddress address, size_t size) { } // Release any remaining unmapped memory. - m_kernel.MemoryManager().OpenFirst(pg_phys_addr, pg_pages); - m_kernel.MemoryManager().Close(pg_phys_addr, pg_pages); + m_system.Kernel().MemoryManager().OpenFirst(pg_phys_addr, pg_pages); + m_system.Kernel().MemoryManager().Close(pg_phys_addr, pg_pages); for (++pg_it; pg_it != pg.end(); ++pg_it) { - m_kernel.MemoryManager().OpenFirst(pg_it->GetAddress(), + m_system.Kernel().MemoryManager().OpenFirst(pg_it->GetAddress(), pg_it->GetNumPages()); - m_kernel.MemoryManager().Close(pg_it->GetAddress(), pg_it->GetNumPages()); + m_system.Kernel().MemoryManager().Close(pg_it->GetAddress(), pg_it->GetNumPages()); } }; @@ -5337,11 +5337,11 @@ Result KPageTableBase::MapPhysicalMemory(KProcessAddress address, size_t size) { // While we have pages to map, map them. { // Create a page group for the current mapping range. - KPageGroup cur_pg(m_kernel, m_block_info_manager); + KPageGroup cur_pg(m_system.Kernel(), m_block_info_manager); { ON_RESULT_FAILURE_2 { - cur_pg.OpenFirst(); - cur_pg.Close(); + cur_pg.OpenFirst(m_system.Kernel()); + cur_pg.Close(m_system.Kernel()); }; size_t remain_pages = map_pages; @@ -5541,7 +5541,7 @@ Result KPageTableBase::UnmapPhysicalMemory(KProcessAddress address, size_t size) // Release the memory resource. m_mapped_physical_memory_size -= mapped_size; - m_resource_limit->Release(Svc::LimitableResource::PhysicalMemoryMax, mapped_size); + m_resource_limit->Release(m_system.Kernel(), Svc::LimitableResource::PhysicalMemoryMax, mapped_size); // Update memory blocks. m_memory_block_manager.Update(std::addressof(allocator), address, size / PageSize, @@ -5706,9 +5706,9 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a const bool separate_heap = operation == OperationType::UnmapPhysical; // Ensure that any pages we track are closed on exit. - KPageGroup pages_to_close(m_kernel, this->GetBlockInfoManager()); + KPageGroup pages_to_close(m_system.Kernel(), this->GetBlockInfoManager()); SCOPE_EXIT { - pages_to_close.CloseAndReset(); + pages_to_close.CloseAndReset(m_system.Kernel()); }; // Make a page group representing the region to unmap. @@ -5727,7 +5727,7 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a // Open references to pages, if we should. if (this->IsHeapPhysicalAddress(phys_addr)) { - m_kernel.MemoryManager().Open(phys_addr, num_pages); + m_system.Kernel().MemoryManager().Open(phys_addr, num_pages); } R_SUCCEED(); @@ -5767,7 +5767,7 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a const bool separate_heap = operation == OperationType::MapFirstGroupPhysical; // We want to maintain a new reference to every page in the group. - KScopedPageGroup spg(page_group, operation == OperationType::MapGroup); + KScopedPageGroup spg(m_system.Kernel(), page_group, operation == OperationType::MapGroup); for (const auto& node : page_group) { const size_t size{node.GetNumPages() * PageSize}; diff --git a/src/core/hle/kernel/k_page_table_base.h b/src/core/hle/kernel/k_page_table_base.h index 726fad989f..160e722985 100644 --- a/src/core/hle/kernel/k_page_table_base.h +++ b/src/core/hle/kernel/k_page_table_base.h @@ -61,14 +61,12 @@ public: class MemoryRange { private: - KernelCore& m_kernel; - KPhysicalAddress m_address; - size_t m_size; - bool m_heap; + KPhysicalAddress m_address = 0; + size_t m_size = 0; + bool m_heap = false; public: - explicit MemoryRange(KernelCore& kernel) - : m_kernel(kernel), m_address(0), m_size(0), m_heap(false) {} + explicit MemoryRange() : m_address(0), m_size(0), m_heap(false) {} void Set(KPhysicalAddress address, size_t size, bool heap) { m_address = address; @@ -86,8 +84,8 @@ public: return m_heap; } - void Open(); - void Close(); + void Open(KernelCore& kernel); + void Close(KernelCore& kernel); }; protected: @@ -189,7 +187,6 @@ private: }; private: - KernelCore& m_kernel; Core::System& m_system; KProcessAddress m_address_space_start{}; KProcessAddress m_address_space_end{}; @@ -329,35 +326,35 @@ protected: bool IsLinearMappedPhysicalAddress(KPhysicalAddress phys_addr) { ASSERT(this->IsLockedByCurrentThread()); - return m_kernel.MemoryLayout().IsLinearMappedPhysicalAddress( + return m_system.Kernel().MemoryLayout().IsLinearMappedPhysicalAddress( m_cached_physical_linear_region, phys_addr); } bool IsLinearMappedPhysicalAddress(KPhysicalAddress phys_addr, size_t size) { ASSERT(this->IsLockedByCurrentThread()); - return m_kernel.MemoryLayout().IsLinearMappedPhysicalAddress( + return m_system.Kernel().MemoryLayout().IsLinearMappedPhysicalAddress( m_cached_physical_linear_region, phys_addr, size); } bool IsHeapPhysicalAddress(KPhysicalAddress phys_addr) { ASSERT(this->IsLockedByCurrentThread()); - return m_kernel.MemoryLayout().IsHeapPhysicalAddress(m_cached_physical_heap_region, + return m_system.Kernel().MemoryLayout().IsHeapPhysicalAddress(m_cached_physical_heap_region, phys_addr); } bool IsHeapPhysicalAddress(KPhysicalAddress phys_addr, size_t size) { ASSERT(this->IsLockedByCurrentThread()); - return m_kernel.MemoryLayout().IsHeapPhysicalAddress(m_cached_physical_heap_region, + return m_system.Kernel().MemoryLayout().IsHeapPhysicalAddress(m_cached_physical_heap_region, phys_addr, size); } bool IsHeapPhysicalAddressForFinalize(KPhysicalAddress phys_addr) { ASSERT(!this->IsLockedByCurrentThread()); - return m_kernel.MemoryLayout().IsHeapPhysicalAddress(m_cached_physical_heap_region, + return m_system.Kernel().MemoryLayout().IsHeapPhysicalAddress(m_cached_physical_heap_region, phys_addr); } @@ -744,15 +741,15 @@ public: // Member heap u8* GetHeapVirtualPointer(KPhysicalAddress addr) { - return GetHeapVirtualPointer(m_kernel, addr); + return GetHeapVirtualPointer(m_system.Kernel(), addr); } KPhysicalAddress GetHeapPhysicalAddress(KVirtualAddress addr) { - return GetHeapPhysicalAddress(m_kernel, addr); + return GetHeapPhysicalAddress(m_system.Kernel(), addr); } KVirtualAddress GetHeapVirtualAddress(KPhysicalAddress addr) { - return GetHeapVirtualAddress(m_kernel, addr); + return GetHeapVirtualAddress(m_system.Kernel(), addr); } // TODO: GetPageTableVirtualAddress diff --git a/src/core/hle/kernel/k_port.cpp b/src/core/hle/kernel/k_port.cpp index e5f5d8028c..f64d983e0c 100644 --- a/src/core/hle/kernel/k_port.cpp +++ b/src/core/hle/kernel/k_port.cpp @@ -1,8 +1,12 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/kernel/k_port.h" #include "core/hle/kernel/k_scheduler.h" +#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/svc_results.h" namespace Kernel { @@ -12,15 +16,15 @@ KPort::KPort(KernelCore& kernel) KPort::~KPort() = default; -void KPort::Initialize(s32 max_sessions, bool is_light, uintptr_t name) { +void KPort::Initialize(KernelCore& kernel, s32 max_sessions, bool is_light, uintptr_t name) { // Open a new reference count to the initialized port. - this->Open(); + this->Open(kernel); // Create and initialize our server/client pair. KAutoObject::Create(std::addressof(m_server)); KAutoObject::Create(std::addressof(m_client)); - m_server.Initialize(this); - m_client.Initialize(this, max_sessions); + m_server.Initialize(kernel, this); + m_client.Initialize(kernel, this, max_sessions); // Set our member variables. m_is_light = is_light; @@ -28,42 +32,38 @@ void KPort::Initialize(s32 max_sessions, bool is_light, uintptr_t name) { m_state = State::Normal; } -void KPort::OnClientClosed() { - KScopedSchedulerLock sl{m_kernel}; +void KPort::OnClientClosed(KernelCore& kernel) { + KScopedSchedulerLock sl{kernel}; if (m_state == State::Normal) { m_state = State::ClientClosed; } } -void KPort::OnServerClosed() { - KScopedSchedulerLock sl{m_kernel}; +void KPort::OnServerClosed(KernelCore& kernel) { + KScopedSchedulerLock sl{kernel}; if (m_state == State::Normal) { m_state = State::ServerClosed; } } -bool KPort::IsServerClosed() const { - KScopedSchedulerLock sl{m_kernel}; +bool KPort::IsServerClosed(KernelCore& kernel) const { + KScopedSchedulerLock sl{kernel}; return m_state == State::ServerClosed; } -Result KPort::EnqueueSession(KServerSession* session) { - KScopedSchedulerLock sl{m_kernel}; - +Result KPort::EnqueueSession(KernelCore& kernel, KServerSession* session) { + KScopedSchedulerLock sl{kernel}; R_UNLESS(m_state == State::Normal, ResultPortClosed); - - m_server.EnqueueSession(session); + m_server.EnqueueSession(kernel, session); R_SUCCEED(); } -Result KPort::EnqueueSession(KLightServerSession* session) { - KScopedSchedulerLock sl{m_kernel}; - +Result KPort::EnqueueSession(KernelCore& kernel, KLightServerSession* session) { + KScopedSchedulerLock sl{kernel}; R_UNLESS(m_state == State::Normal, ResultPortClosed); - - m_server.EnqueueSession(session); + m_server.EnqueueSession(kernel, session); R_SUCCEED(); } diff --git a/src/core/hle/kernel/k_port.h b/src/core/hle/kernel/k_port.h index 26f5f14eff..698f794538 100644 --- a/src/core/hle/kernel/k_port.h +++ b/src/core/hle/kernel/k_port.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -23,11 +26,11 @@ public: explicit KPort(KernelCore& kernel); ~KPort() override; - static void PostDestroy(uintptr_t arg) {} + static void PostDestroy(KernelCore& kernel, uintptr_t arg) {} - void Initialize(s32 max_sessions, bool is_light, uintptr_t name); - void OnClientClosed(); - void OnServerClosed(); + void Initialize(KernelCore& kernel, s32 max_sessions, bool is_light, uintptr_t name); + void OnClientClosed(KernelCore& kernel); + void OnServerClosed(KernelCore& kernel); uintptr_t GetName() const { return m_name; @@ -36,10 +39,10 @@ public: return m_is_light; } - bool IsServerClosed() const; + bool IsServerClosed(KernelCore& kernel) const; - Result EnqueueSession(KServerSession* session); - Result EnqueueSession(KLightServerSession* session); + Result EnqueueSession(KernelCore& kernel, KServerSession* session); + Result EnqueueSession(KernelCore& kernel, KLightServerSession* session); KClientPort& GetClientPort() { return m_client; diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp index 70e578f22a..7308b0ff87 100644 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp @@ -28,8 +28,7 @@ namespace Kernel { namespace { -Result TerminateChildren(KernelCore& kernel, KProcess* process, - const KThread* thread_to_not_terminate) { +Result TerminateChildren(KernelCore& kernel, KProcess* process, const KThread* thread_to_not_terminate) { // Request that all children threads terminate. { KScopedLightLock proc_lk(process->GetListLock()); @@ -41,14 +40,14 @@ Result TerminateChildren(KernelCore& kernel, KProcess* process, // This is valid because the only caller which uses non-nullptr as argument uses // GetCurrentThreadPointer(), but it's still notable because it seems incorrect at // first glance. - process->UnpinCurrentThread(); + process->UnpinCurrentThread(kernel); } auto& thread_list = process->GetThreadList(); for (auto it = thread_list.begin(); it != thread_list.end(); ++it) { if (KThread* thread = std::addressof(*it); thread != thread_to_not_terminate) { if (thread->GetState() != ThreadState::Terminated) { - thread->RequestTerminate(); + thread->RequestTerminate(kernel); } } } @@ -65,7 +64,7 @@ Result TerminateChildren(KernelCore& kernel, KProcess* process, for (auto it = thread_list.begin(); it != thread_list.end(); ++it) { if (KThread* thread = std::addressof(*it); thread != thread_to_not_terminate) { if (thread->GetState() != ThreadState::Terminated) { - if (thread->Open()) { + if (thread->Open(kernel)) { cur_child = thread; break; } @@ -81,11 +80,10 @@ Result TerminateChildren(KernelCore& kernel, KProcess* process, // Terminate and close the thread. SCOPE_EXIT { - cur_child->Close(); + cur_child->Close(kernel); }; - if (const Result terminate_result = cur_child->Terminate(); - ResultTerminationRequested == terminate_result) { + if (const Result terminate_result = cur_child->Terminate(kernel); ResultTerminationRequested == terminate_result) { R_THROW(terminate_result); } } @@ -101,21 +99,21 @@ public: explicit ThreadQueueImplForKProcessEnterUserException(KernelCore& kernel, KThread** t) : KThreadQueue(kernel), m_exception_thread(t) {} - virtual void EndWait(KThread* waiting_thread, Result wait_result) override { + virtual void EndWait(KernelCore& kernel, KThread* waiting_thread, Result wait_result) override { // Set the exception thread. *m_exception_thread = waiting_thread; // Invoke the base end wait handler. - KThreadQueue::EndWait(waiting_thread, wait_result); + KThreadQueue::EndWait(kernel, waiting_thread, wait_result); } - virtual void CancelWait(KThread* waiting_thread, Result wait_result, + virtual void CancelWait(KernelCore& kernel, KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override { // Remove the thread as a waiter on its mutex owner. - waiting_thread->GetLockOwner()->RemoveWaiter(waiting_thread); + waiting_thread->GetLockOwner(kernel)->RemoveWaiter(kernel, waiting_thread); // Invoke the base cancel wait handler. - KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task); + KThreadQueue::CancelWait(kernel, waiting_thread, wait_result, cancel_timer_task); } }; @@ -128,12 +126,12 @@ void GenerateRandom(std::span out_random) { } // namespace -void KProcess::Finalize() { +void KProcess::Finalize(KernelCore& kernel) { // Delete the process local region. - this->DeleteThreadLocalRegion(m_plr_address); + this->DeleteThreadLocalRegion(kernel, m_plr_address); // Get the used memory size. - const size_t used_memory_size = this->GetUsedNonSystemUserPhysicalMemorySize(); + const size_t used_memory_size = this->GetUsedNonSystemUserPhysicalMemorySize(kernel); // Finalize the page table. m_page_table.Finalize(); @@ -142,10 +140,10 @@ void KProcess::Finalize() { if (m_system_resource) { if (m_system_resource->IsSecureResource()) { // Finalize optimized memory. If memory wasn't optimized, this is a no-op. - m_kernel.MemoryManager().FinalizeOptimizedMemory(this->GetId(), m_memory_pool); + kernel.MemoryManager().FinalizeOptimizedMemory(this->GetId(), m_memory_pool); } - m_system_resource->Close(); + m_system_resource->Close(kernel); m_system_resource = nullptr; } @@ -157,12 +155,12 @@ void KProcess::Finalize() { KSharedMemory* shmem = info->GetSharedMemory(); while (!info->Close()) { - shmem->Close(); + shmem->Close(kernel); } - shmem->Close(); + shmem->Close(kernel); it = m_shared_memory_list.erase(it); - KSharedMemoryInfo::Free(m_kernel, info); + KSharedMemoryInfo::Free(kernel, info); } } @@ -173,9 +171,8 @@ void KProcess::Finalize() { // Release memory to the resource limit. if (m_resource_limit != nullptr) { ASSERT(used_memory_size >= m_memory_release_hint); - m_resource_limit->Release(Svc::LimitableResource::PhysicalMemoryMax, used_memory_size, - used_memory_size - m_memory_release_hint); - m_resource_limit->Close(); + m_resource_limit->Release(kernel, Svc::LimitableResource::PhysicalMemoryMax, used_memory_size, used_memory_size - m_memory_release_hint); + m_resource_limit->Close(kernel); } // Clear expensive resources, as the destructor is not called for guest objects. @@ -185,15 +182,14 @@ void KProcess::Finalize() { m_exclusive_monitor.reset(); // Perform inherited finalization. - KSynchronizationObject::Finalize(); + KSynchronizationObject::Finalize(kernel); } -Result KProcess::Initialize(const Svc::CreateProcessParameter& params, KResourceLimit* res_limit, - bool is_real) { +Result KProcess::Initialize(KernelCore& kernel, const Svc::CreateProcessParameter& params, KResourceLimit* res_limit, bool is_real) { // TODO: remove this special case if (is_real) { // Create and clear the process local region. - R_TRY(this->CreateThreadLocalRegion(std::addressof(m_plr_address))); + R_TRY(this->CreateThreadLocalRegion(kernel, std::addressof(m_plr_address))); this->GetMemory().ZeroBlock(m_plr_address, Svc::ThreadLocalRegionSize); } @@ -211,6 +207,9 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params, KResource m_version = params.version; m_program_id = params.program_id; m_code_address = params.code_address; + m_arg_pointer = 0; + m_arg_return_address = 0; + m_main_thread_handle_addr = 0; m_code_size = params.code_num_pages * PageSize; m_is_application = True(params.flags & Svc::CreateProcessFlag::IsApplication); @@ -256,7 +255,7 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params, KResource // Open a reference to our resource limit. m_resource_limit = res_limit; - m_resource_limit->Open(); + m_resource_limit->Open(kernel); // We're initialized! m_is_initialized = true; @@ -264,7 +263,7 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params, KResource R_SUCCEED(); } -Result KProcess::Initialize(const Svc::CreateProcessParameter& params, const KPageGroup& pg, +Result KProcess::Initialize(KernelCore& kernel, const Svc::CreateProcessParameter& params, const KPageGroup& pg, std::span caps, KResourceLimit* res_limit, KMemoryManager::Pool pool, bool immortal) { ASSERT(res_limit != nullptr); @@ -280,34 +279,33 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params, const KPa if (const size_t system_resource_num_pages = params.system_resource_num_pages; system_resource_num_pages != 0) { // Create a secure system resource. - KSecureSystemResource* secure_resource = KSecureSystemResource::Create(m_kernel); + KSecureSystemResource* secure_resource = KSecureSystemResource::Create(kernel); R_UNLESS(secure_resource != nullptr, ResultOutOfResource); ON_RESULT_FAILURE { - secure_resource->Close(); + secure_resource->Close(kernel); }; // Initialize the secure resource. - R_TRY(secure_resource->Initialize(system_resource_num_pages * PageSize, res_limit, - m_memory_pool)); + R_TRY(secure_resource->Initialize(kernel, system_resource_num_pages * PageSize, res_limit, m_memory_pool)); // Set our system resource. m_system_resource = secure_resource; } else { // Use the system-wide system resource. const bool is_app = True(params.flags & Svc::CreateProcessFlag::IsApplication); - m_system_resource = std::addressof(is_app ? m_kernel.GetAppSystemResource() - : m_kernel.GetSystemSystemResource()); + m_system_resource = std::addressof(is_app ? kernel.GetAppSystemResource() + : kernel.GetSystemSystemResource()); m_is_default_application_system_resource = is_app; // Open reference to the system resource. - m_system_resource->Open(); + m_system_resource->Open(kernel); } // Ensure we clean up our secure resource, if we fail. ON_RESULT_FAILURE { - m_system_resource->Close(); + m_system_resource->Close(kernel); m_system_resource = nullptr; }; @@ -327,7 +325,7 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params, const KPa // Ensure our memory is initialized. m_memory.SetCurrentPageTable(*this); - m_memory.SetGPUDirtyManagers(m_kernel.System().GetGPUDirtyMemoryManager()); + m_memory.SetGPUDirtyManagers(kernel.System().GetGPUDirtyMemoryManager()); // Ensure we can insert the code region. R_UNLESS(m_page_table.CanContain(params.code_address, params.code_num_pages * PageSize, @@ -347,18 +345,18 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params, const KPa } // Initialize the process id. - m_process_id = m_kernel.CreateNewUserProcessID(); + m_process_id = kernel.CreateNewUserProcessID(); ASSERT(InitialProcessIdMin <= m_process_id); ASSERT(m_process_id <= InitialProcessIdMax); // Initialize the rest of the process. - R_TRY(this->Initialize(params, res_limit, true)); + R_TRY(this->Initialize(kernel, params, res_limit, true)); // We succeeded! R_SUCCEED(); } -Result KProcess::Initialize(const Svc::CreateProcessParameter& params, +Result KProcess::Initialize(KernelCore& kernel, const Svc::CreateProcessParameter& params, std::span user_caps, KResourceLimit* res_limit, KMemoryManager::Pool pool, KProcessAddress aslr_space_start) { ASSERT(res_limit != nullptr); @@ -375,22 +373,22 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params, const size_t system_resource_size = system_resource_num_pages * PageSize; // Reserve memory for our code resource. - KScopedResourceReservation memory_reservation( + KScopedResourceReservation memory_reservation(kernel, res_limit, Svc::LimitableResource::PhysicalMemoryMax, code_size); R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); // Setup our system resource. if (system_resource_num_pages != 0) { // Create a secure system resource. - KSecureSystemResource* secure_resource = KSecureSystemResource::Create(m_kernel); + KSecureSystemResource* secure_resource = KSecureSystemResource::Create(kernel); R_UNLESS(secure_resource != nullptr, ResultOutOfResource); ON_RESULT_FAILURE { - secure_resource->Close(); + secure_resource->Close(kernel); }; // Initialize the secure resource. - R_TRY(secure_resource->Initialize(system_resource_size, res_limit, m_memory_pool)); + R_TRY(secure_resource->Initialize(kernel, system_resource_size, res_limit, m_memory_pool)); // Set our system resource. m_system_resource = secure_resource; @@ -398,18 +396,18 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params, } else { // Use the system-wide system resource. const bool is_app = True(params.flags & Svc::CreateProcessFlag::IsApplication); - m_system_resource = std::addressof(is_app ? m_kernel.GetAppSystemResource() - : m_kernel.GetSystemSystemResource()); + m_system_resource = std::addressof(is_app ? kernel.GetAppSystemResource() + : kernel.GetSystemSystemResource()); m_is_default_application_system_resource = is_app; // Open reference to the system resource. - m_system_resource->Open(); + m_system_resource->Open(kernel); } // Ensure we clean up our secure resource, if we fail. ON_RESULT_FAILURE { - m_system_resource->Close(); + m_system_resource->Close(kernel); m_system_resource = nullptr; }; @@ -429,7 +427,7 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params, // Ensure our memory is initialized. m_memory.SetCurrentPageTable(*this); - m_memory.SetGPUDirtyManagers(m_kernel.System().GetGPUDirtyMemoryManager()); + m_memory.SetGPUDirtyManagers(kernel.System().GetGPUDirtyMemoryManager()); // Ensure we can insert the code region. R_UNLESS(m_page_table.CanContain(params.code_address, code_size, KMemoryState::Code), @@ -448,101 +446,99 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params, } // Initialize the process id. - m_process_id = m_kernel.CreateNewUserProcessID(); + m_process_id = kernel.CreateNewUserProcessID(); ASSERT(ProcessIdMin <= m_process_id); ASSERT(m_process_id <= ProcessIdMax); // If we should optimize memory allocations, do so. if (m_system_resource->IsSecureResource() && True(params.flags & Svc::CreateProcessFlag::OptimizeMemoryAllocation)) { - R_TRY(m_kernel.MemoryManager().InitializeOptimizedMemory(m_process_id, pool)); + R_TRY(kernel.MemoryManager().InitializeOptimizedMemory(m_process_id, pool)); } // Initialize the rest of the process. - R_TRY(this->Initialize(params, res_limit, true)); + R_TRY(this->Initialize(kernel, params, res_limit, true)); // We succeeded, so commit our memory reservation. memory_reservation.Commit(); R_SUCCEED(); } -void KProcess::DoWorkerTaskImpl() { +void KProcess::DoWorkerTaskImpl(KernelCore& kernel) { // Terminate child threads. - TerminateChildren(m_kernel, this, nullptr); + TerminateChildren(kernel, this, nullptr); // Finalize the handle table, if we're not immortal. if (!m_is_immortal && m_is_handle_table_initialized) { - this->FinalizeHandleTable(); + this->FinalizeHandleTable(kernel); } // Finish termination. - this->FinishTermination(); + this->FinishTermination(kernel); } -Result KProcess::StartTermination() { +Result KProcess::StartTermination(KernelCore& kernel) { // Finalize the handle table when we're done, if the process isn't immortal. SCOPE_EXIT { if (!m_is_immortal) { - this->FinalizeHandleTable(); + this->FinalizeHandleTable(kernel); } }; // Terminate child threads other than the current one. - R_RETURN(TerminateChildren(m_kernel, this, GetCurrentThreadPointer(m_kernel))); + R_RETURN(TerminateChildren(kernel, this, GetCurrentThreadPointer(kernel))); } -void KProcess::FinishTermination() { +void KProcess::FinishTermination(KernelCore& kernel) { // Only allow termination to occur if the process isn't immortal. if (!m_is_immortal) { // Release resource limit hint. if (m_resource_limit != nullptr) { - m_memory_release_hint = this->GetUsedNonSystemUserPhysicalMemorySize(); - m_resource_limit->Release(Svc::LimitableResource::PhysicalMemoryMax, 0, - m_memory_release_hint); + m_memory_release_hint = this->GetUsedNonSystemUserPhysicalMemorySize(kernel); + m_resource_limit->Release(kernel, Svc::LimitableResource::PhysicalMemoryMax, 0, m_memory_release_hint); } // Change state. { - KScopedSchedulerLock sl(m_kernel); - this->ChangeState(State::Terminated); + KScopedSchedulerLock sl(kernel); + this->ChangeState(kernel, State::Terminated); } // Close. - this->Close(); + this->Close(kernel); } } -void KProcess::Exit() { +void KProcess::Exit(KernelCore& kernel) { // Determine whether we need to start terminating bool needs_terminate = false; { KScopedLightLock lk(m_state_lock); - KScopedSchedulerLock sl(m_kernel); + KScopedSchedulerLock sl(kernel); ASSERT(m_state != State::Created); ASSERT(m_state != State::CreatedAttached); ASSERT(m_state != State::Crashed); ASSERT(m_state != State::Terminated); - if (m_state == State::Running || m_state == State::RunningAttached || - m_state == State::DebugBreak) { - this->ChangeState(State::Terminating); + if (m_state == State::Running || m_state == State::RunningAttached || m_state == State::DebugBreak) { + this->ChangeState(kernel, State::Terminating); needs_terminate = true; } } // If we need to start termination, do so. if (needs_terminate) { - this->StartTermination(); + this->StartTermination(kernel); // Register the process as a work task. - m_kernel.WorkerTaskManager().AddTask(m_kernel, KWorkerTaskManager::WorkerType::Exit, this); + kernel.WorkerTaskManager().AddTask(kernel, KWorkerTaskManager::WorkerType::Exit, this); } // Exit the current thread. - GetCurrentThread(m_kernel).Exit(); + GetCurrentThread(kernel).Exit(kernel); } -Result KProcess::Terminate() { +Result KProcess::Terminate(KernelCore& kernel) { // Determine whether we need to start terminating. bool needs_terminate = false; { @@ -552,11 +548,11 @@ Result KProcess::Terminate() { R_UNLESS(m_state != State::Created, ResultInvalidState); R_UNLESS(m_state != State::CreatedAttached, ResultInvalidState); - KScopedSchedulerLock sl(m_kernel); + KScopedSchedulerLock sl(kernel); if (m_state == State::Running || m_state == State::RunningAttached || m_state == State::Crashed || m_state == State::DebugBreak) { - this->ChangeState(State::Terminating); + this->ChangeState(kernel, State::Terminating); needs_terminate = true; } } @@ -564,20 +560,19 @@ Result KProcess::Terminate() { // If we need to terminate, do so. if (needs_terminate) { // Start termination. - if (R_SUCCEEDED(this->StartTermination())) { + if (R_SUCCEEDED(this->StartTermination(kernel))) { // Finish termination. - this->FinishTermination(); + this->FinishTermination(kernel); } else { // Register the process as a work task. - m_kernel.WorkerTaskManager().AddTask(m_kernel, KWorkerTaskManager::WorkerType::Exit, - this); + kernel.WorkerTaskManager().AddTask(kernel, KWorkerTaskManager::WorkerType::Exit, this); } } R_SUCCEED(); } -Result KProcess::AddSharedMemory(KSharedMemory* shmem, KProcessAddress address, size_t size) { +Result KProcess::AddSharedMemory(KernelCore& kernel, KSharedMemory* shmem, KProcessAddress address, size_t size) { // Lock ourselves, to prevent concurrent access. KScopedLightLock lk(m_state_lock); @@ -593,7 +588,7 @@ Result KProcess::AddSharedMemory(KSharedMemory* shmem, KProcessAddress address, // If we didn't find an info, create one. if (info == nullptr) { // Allocate a new info. - info = KSharedMemoryInfo::Allocate(m_kernel); + info = KSharedMemoryInfo::Allocate(kernel); R_UNLESS(info != nullptr, ResultOutOfResource); // Initialize the info and add it to our list. @@ -602,13 +597,13 @@ Result KProcess::AddSharedMemory(KSharedMemory* shmem, KProcessAddress address, } // Open a reference to the shared memory and its info. - shmem->Open(); + shmem->Open(kernel); info->Open(); R_SUCCEED(); } -void KProcess::RemoveSharedMemory(KSharedMemory* shmem, KProcessAddress address, size_t size) { +void KProcess::RemoveSharedMemory(KernelCore& kernel, KSharedMemory* shmem, KProcessAddress address, size_t size) { // Lock ourselves, to prevent concurrent access. KScopedLightLock lk(m_state_lock); @@ -626,19 +621,19 @@ void KProcess::RemoveSharedMemory(KSharedMemory* shmem, KProcessAddress address, // Close a reference to the info and its memory. if (info->Close()) { m_shared_memory_list.erase(it); - KSharedMemoryInfo::Free(m_kernel, info); + KSharedMemoryInfo::Free(kernel, info); } - shmem->Close(); + shmem->Close(kernel); } -Result KProcess::CreateThreadLocalRegion(KProcessAddress* out) { +Result KProcess::CreateThreadLocalRegion(KernelCore& kernel, KProcessAddress* out) { KThreadLocalPage* tlp = nullptr; KProcessAddress tlr = 0; // See if we can get a region from a partially used TLP. { - KScopedSchedulerLock sl(m_kernel); + KScopedSchedulerLock sl(kernel); if (auto it = m_partially_used_tlp_tree.begin(); it != m_partially_used_tlp_tree.end()) { tlr = it->Reserve(); @@ -656,14 +651,14 @@ Result KProcess::CreateThreadLocalRegion(KProcessAddress* out) { } // Allocate a new page. - tlp = KThreadLocalPage::Allocate(m_kernel); + tlp = KThreadLocalPage::Allocate(kernel); R_UNLESS(tlp != nullptr, ResultOutOfMemory); ON_RESULT_FAILURE { - KThreadLocalPage::Free(m_kernel, tlp); + KThreadLocalPage::Free(kernel, tlp); }; // Initialize the new page. - R_TRY(tlp->Initialize(m_kernel, this)); + R_TRY(tlp->Initialize(kernel, this)); // Reserve a TLR. tlr = tlp->Reserve(); @@ -671,7 +666,7 @@ Result KProcess::CreateThreadLocalRegion(KProcessAddress* out) { // Insert into our tree. { - KScopedSchedulerLock sl(m_kernel); + KScopedSchedulerLock sl(kernel); if (tlp->IsAllUsed()) { m_fully_used_tlp_tree.insert(*tlp); } else { @@ -684,12 +679,12 @@ Result KProcess::CreateThreadLocalRegion(KProcessAddress* out) { R_SUCCEED(); } -Result KProcess::DeleteThreadLocalRegion(KProcessAddress addr) { +Result KProcess::DeleteThreadLocalRegion(KernelCore& kernel, KProcessAddress addr) { KThreadLocalPage* page_to_free = nullptr; // Release the region. { - KScopedSchedulerLock sl(m_kernel); + KScopedSchedulerLock sl(kernel); // Try to find the page in the partially used list. auto it = m_partially_used_tlp_tree.find_key(Common::AlignDown(GetInteger(addr), PageSize)); @@ -726,57 +721,56 @@ Result KProcess::DeleteThreadLocalRegion(KProcessAddress addr) { if (page_to_free != nullptr) { page_to_free->Finalize(); - KThreadLocalPage::Free(m_kernel, page_to_free); + KThreadLocalPage::Free(kernel, page_to_free); } R_SUCCEED(); } -bool KProcess::ReserveResource(Svc::LimitableResource which, s64 value) { +bool KProcess::ReserveResource(KernelCore& kernel, Svc::LimitableResource which, s64 value) { if (KResourceLimit* rl = this->GetResourceLimit(); rl != nullptr) { - return rl->Reserve(which, value); + return rl->Reserve(kernel, which, value); } else { return true; } } -bool KProcess::ReserveResource(Svc::LimitableResource which, s64 value, s64 timeout) { +bool KProcess::ReserveResource(KernelCore& kernel, Svc::LimitableResource which, s64 value, s64 timeout) { if (KResourceLimit* rl = this->GetResourceLimit(); rl != nullptr) { - return rl->Reserve(which, value, timeout); + return rl->Reserve(kernel, which, value, timeout); } else { return true; } } -void KProcess::ReleaseResource(Svc::LimitableResource which, s64 value) { +void KProcess::ReleaseResource(KernelCore& kernel, Svc::LimitableResource which, s64 value) { if (KResourceLimit* rl = this->GetResourceLimit(); rl != nullptr) { - rl->Release(which, value); + rl->Release(kernel, which, value); } } -void KProcess::ReleaseResource(Svc::LimitableResource which, s64 value, s64 hint) { +void KProcess::ReleaseResource(KernelCore& kernel, Svc::LimitableResource which, s64 value, s64 hint) { if (KResourceLimit* rl = this->GetResourceLimit(); rl != nullptr) { - rl->Release(which, value, hint); + rl->Release(kernel, which, value, hint); } } -void KProcess::IncrementRunningThreadCount() { +void KProcess::IncrementRunningThreadCount(KernelCore& kernel) { ASSERT(m_num_running_threads.load() >= 0); ++m_num_running_threads; } -void KProcess::DecrementRunningThreadCount() { +void KProcess::DecrementRunningThreadCount(KernelCore& kernel) { ASSERT(m_num_running_threads.load() > 0); - if (const auto prev = m_num_running_threads--; prev == 1) { - this->Terminate(); + this->Terminate(kernel); } } -bool KProcess::EnterUserException() { +bool KProcess::EnterUserException(KernelCore& kernel) { // Get the current thread. - KThread* cur_thread = GetCurrentThreadPointer(m_kernel); + KThread* cur_thread = GetCurrentThreadPointer(kernel); ASSERT(this == cur_thread->GetOwnerProcess()); // Check that we haven't already claimed the exception thread. @@ -785,13 +779,13 @@ bool KProcess::EnterUserException() { } // Create the wait queue we'll be using. - ThreadQueueImplForKProcessEnterUserException wait_queue(m_kernel, + ThreadQueueImplForKProcessEnterUserException wait_queue(kernel, std::addressof(m_exception_thread)); // Claim the exception thread. { // Lock the scheduler. - KScopedSchedulerLock sl(m_kernel); + KScopedSchedulerLock sl(kernel); // Check that we're not terminating. if (cur_thread->IsTerminationRequested()) { @@ -801,45 +795,44 @@ bool KProcess::EnterUserException() { // If we don't have an exception thread, we can just claim it directly. if (m_exception_thread == nullptr) { m_exception_thread = cur_thread; - KScheduler::SetSchedulerUpdateNeeded(m_kernel); + KScheduler::SetSchedulerUpdateNeeded(kernel); return true; } // Otherwise, we need to wait until we don't have an exception thread. // Add the current thread as a waiter on the current exception thread. - cur_thread->SetKernelAddressKey( - reinterpret_cast(std::addressof(m_exception_thread)) | 1); - m_exception_thread->AddWaiter(cur_thread); + cur_thread->SetKernelAddressKey(uintptr_t(std::addressof(m_exception_thread)) | 1); + m_exception_thread->AddWaiter(kernel, cur_thread); // Wait to claim the exception thread. - cur_thread->BeginWait(std::addressof(wait_queue)); + cur_thread->BeginWait(kernel, std::addressof(wait_queue)); } // If our wait didn't end due to thread termination, we succeeded. return ResultTerminationRequested != cur_thread->GetWaitResult(); } -bool KProcess::LeaveUserException() { - return this->ReleaseUserException(GetCurrentThreadPointer(m_kernel)); +bool KProcess::LeaveUserException(KernelCore& kernel) { + return this->ReleaseUserException(kernel, GetCurrentThreadPointer(kernel)); } -bool KProcess::ReleaseUserException(KThread* thread) { - KScopedSchedulerLock sl(m_kernel); +bool KProcess::ReleaseUserException(KernelCore& kernel, KThread* thread) { + KScopedSchedulerLock sl(kernel); if (m_exception_thread == thread) { m_exception_thread = nullptr; // Remove waiter thread. bool has_waiters; - if (KThread* next = thread->RemoveKernelWaiterByKey( + if (KThread* next = thread->RemoveKernelWaiterByKey(kernel, std::addressof(has_waiters), reinterpret_cast(std::addressof(m_exception_thread)) | 1); next != nullptr) { - next->EndWait(ResultSuccess); + next->EndWait(kernel, ResultSuccess); } - KScheduler::SetSchedulerUpdateNeeded(m_kernel); + KScheduler::SetSchedulerUpdateNeeded(kernel); return true; } else { @@ -847,19 +840,19 @@ bool KProcess::ReleaseUserException(KThread* thread) { } } -void KProcess::RegisterThread(KThread* thread) { +void KProcess::RegisterThread(KernelCore& kernel, KThread* thread) { KScopedLightLock lk(m_list_lock); m_thread_list.push_back(*thread); } -void KProcess::UnregisterThread(KThread* thread) { +void KProcess::UnregisterThread(KernelCore& kernel, KThread* thread) { KScopedLightLock lk(m_list_lock); m_thread_list.erase(m_thread_list.iterator_to(*thread)); } -size_t KProcess::GetUsedUserPhysicalMemorySize() const { +size_t KProcess::GetUsedUserPhysicalMemorySize(KernelCore& kernel) const { const size_t norm_size = m_page_table.GetNormalMemorySize(); const size_t other_size = m_code_size + m_main_thread_stack_size; const size_t sec_size = this->GetRequiredSecureMemorySizeNonDefault(); @@ -867,7 +860,7 @@ size_t KProcess::GetUsedUserPhysicalMemorySize() const { return norm_size + other_size + sec_size; } -size_t KProcess::GetTotalUserPhysicalMemorySize() const { +size_t KProcess::GetTotalUserPhysicalMemorySize(KernelCore& kernel) const { // Get the amount of free and used size. const size_t free_size = m_resource_limit->GetFreeValue(Svc::LimitableResource::PhysicalMemoryMax); @@ -886,18 +879,18 @@ size_t KProcess::GetTotalUserPhysicalMemorySize() const { if (used_size + free_size > max_size) { return max_size; } else { - return free_size + this->GetUsedUserPhysicalMemorySize(); + return free_size + this->GetUsedUserPhysicalMemorySize(kernel); } } -size_t KProcess::GetUsedNonSystemUserPhysicalMemorySize() const { +size_t KProcess::GetUsedNonSystemUserPhysicalMemorySize(KernelCore& kernel) const { const size_t norm_size = m_page_table.GetNormalMemorySize(); const size_t other_size = m_code_size + m_main_thread_stack_size; return norm_size + other_size; } -size_t KProcess::GetTotalNonSystemUserPhysicalMemorySize() const { +size_t KProcess::GetTotalNonSystemUserPhysicalMemorySize(KernelCore& kernel) const { // Get the amount of free and used size. const size_t free_size = m_resource_limit->GetFreeValue(Svc::LimitableResource::PhysicalMemoryMax); @@ -916,11 +909,11 @@ size_t KProcess::GetTotalNonSystemUserPhysicalMemorySize() const { if (used_size + free_size > max_size) { return max_size - this->GetRequiredSecureMemorySizeNonDefault(); } else { - return free_size + this->GetUsedNonSystemUserPhysicalMemorySize(); + return free_size + this->GetUsedNonSystemUserPhysicalMemorySize(kernel); } } -Result KProcess::Run(s32 priority, size_t stack_size) { +Result KProcess::Run(KernelCore& kernel, s32 priority, size_t stack_size) { // Lock ourselves, to prevent concurrent access. KScopedLightLock lk(m_state_lock); @@ -929,7 +922,7 @@ Result KProcess::Run(s32 priority, size_t stack_size) { R_UNLESS(state == State::Created || state == State::CreatedAttached, ResultInvalidState); // Place a tentative reservation of a thread for this process. - KScopedResourceReservation thread_reservation(this, Svc::LimitableResource::ThreadCountMax); + KScopedResourceReservation thread_reservation(kernel, this, Svc::LimitableResource::ThreadCountMax); R_UNLESS(thread_reservation.Succeeded(), ResultLimitReached); // Ensure that we haven't already allocated stack. @@ -941,7 +934,7 @@ Result KProcess::Run(s32 priority, size_t stack_size) { R_UNLESS(stack_size + m_code_size >= m_code_size, ResultOutOfMemory); // Place a tentative reservation of memory for our new stack. - KScopedResourceReservation mem_reservation(this, Svc::LimitableResource::PhysicalMemoryMax, + KScopedResourceReservation mem_reservation(kernel, this, Svc::LimitableResource::PhysicalMemoryMax, stack_size); R_UNLESS(mem_reservation.Succeeded(), ResultLimitReached); @@ -971,53 +964,71 @@ Result KProcess::Run(s32 priority, size_t stack_size) { (m_main_thread_stack_size + m_code_size))); // Initialize our handle table. - R_TRY(this->InitializeHandleTable(m_capabilities.GetHandleTableSize())); + R_TRY(this->InitializeHandleTable(kernel, m_capabilities.GetHandleTableSize())); ON_RESULT_FAILURE_2 { - this->FinalizeHandleTable(); + this->FinalizeHandleTable(kernel); }; // Create a new thread for the process. - KThread* main_thread = KThread::Create(m_kernel); + KThread* main_thread = KThread::Create(kernel); R_UNLESS(main_thread != nullptr, ResultOutOfResource); SCOPE_EXIT { - main_thread->Close(); + main_thread->Close(kernel); }; // Initialize the thread. - R_TRY(KThread::InitializeUserThread(m_kernel.System(), main_thread, this->GetEntryPoint(), 0, + R_TRY(KThread::InitializeUserThread(kernel.System(), main_thread, this->GetEntryPoint(), 0, stack_top, priority, m_ideal_core_id, this)); // Register the thread, and commit our reservation. - KThread::Register(m_kernel, main_thread); + KThread::Register(kernel, main_thread); thread_reservation.Commit(); // Add the thread to our handle table. Handle thread_handle; - R_TRY(m_handle_table.Add(std::addressof(thread_handle), main_thread)); + R_TRY(m_handle_table.Add(kernel, std::addressof(thread_handle), main_thread)); - // Set the thread arguments. - main_thread->GetContext().r[0] = 0; - main_thread->GetContext().r[1] = thread_handle; + // Set the thread arguments. Two distinct entry conventions: + // * Kernel/NSO entry (no homebrew ABI): x0 = 0, x1 = thread_handle + // * Homebrew/NRO ABI (loader set arg ptr): x0 = ConfigEntry ptr, x1 = -1ULL + // libnx's switch_crt0.s tests `x0==0 || x1==0xFFFFFFFFFFFFFFFF` to take + // its normal init path; any other combination is interpreted as a user + // exception handler entry. + if (GetInteger(m_arg_pointer) != 0) { + main_thread->GetContext().r[0] = GetInteger(m_arg_pointer); + main_thread->GetContext().r[1] = UINT64_MAX; + main_thread->GetContext().lr = GetInteger(m_arg_return_address); + // Patch the MainThreadHandle entry in the ConfigEntry table now that + // the actual handle exists. libnx stores this verbatim and uses it + // for thread-control SVCs later; a pseudo-handle wouldn't survive + // svcCloseHandle on exit. + if (GetInteger(m_main_thread_handle_addr) != 0) { + this->GetMemory().Write32(m_main_thread_handle_addr, thread_handle); + } + } else { + main_thread->GetContext().r[0] = 0; + main_thread->GetContext().r[1] = thread_handle; + } // Pass the thread handle to the thread local region. this->GetMemory().Write32(GetInteger(main_thread->GetTlsAddress()) + 0x110, thread_handle); // Update our state. - this->ChangeState((state == State::Created) ? State::Running : State::RunningAttached); + this->ChangeState(kernel, (state == State::Created) ? State::Running : State::RunningAttached); ON_RESULT_FAILURE_2 { - this->ChangeState(state); + this->ChangeState(kernel, state); }; // Suspend for debug, if we should. - if (m_kernel.System().DebuggerEnabled()) { - main_thread->RequestSuspend(SuspendType::Debug); + if (kernel.System().DebuggerEnabled()) { + main_thread->RequestSuspend(kernel, SuspendType::Debug); } // Run our thread. - R_TRY(main_thread->Run()); + R_TRY(main_thread->Run(kernel)); // Open a reference to represent that we're running. - this->Open(); + this->Open(kernel); // We succeeded! Commit our memory reservation. mem_reservation.Commit(); @@ -1025,10 +1036,10 @@ Result KProcess::Run(s32 priority, size_t stack_size) { R_SUCCEED(); } -Result KProcess::Reset() { +Result KProcess::Reset(KernelCore& kernel) { // Lock the process and the scheduler. KScopedLightLock lk(m_state_lock); - KScopedSchedulerLock sl(m_kernel); + KScopedSchedulerLock sl(kernel); // Validate that we're in a state that we can reset. R_UNLESS(m_state != State::Terminated, ResultInvalidState); @@ -1039,11 +1050,11 @@ Result KProcess::Reset() { R_SUCCEED(); } -Result KProcess::SetActivity(Svc::ProcessActivity activity) { +Result KProcess::SetActivity(KernelCore& kernel, Svc::ProcessActivity activity) { // Lock ourselves and the scheduler. KScopedLightLock lk(m_state_lock); KScopedLightLock list_lk(m_list_lock); - KScopedSchedulerLock sl(m_kernel); + KScopedSchedulerLock sl(kernel); // Validate our state. R_UNLESS(m_state != State::Terminating, ResultInvalidState); @@ -1057,7 +1068,7 @@ Result KProcess::SetActivity(Svc::ProcessActivity activity) { // Suspend all threads. auto end = this->GetThreadList().end(); for (auto it = this->GetThreadList().begin(); it != end; ++it) { - it->RequestSuspend(SuspendType::Process); + it->RequestSuspend(kernel, SuspendType::Process); } // Set ourselves as suspended. @@ -1071,7 +1082,7 @@ Result KProcess::SetActivity(Svc::ProcessActivity activity) { // Resume all threads. auto end = this->GetThreadList().end(); for (auto it = this->GetThreadList().begin(); it != end; ++it) { - it->Resume(SuspendType::Process); + it->Resume(kernel, SuspendType::Process); } // Set ourselves as resumed. @@ -1081,54 +1092,54 @@ Result KProcess::SetActivity(Svc::ProcessActivity activity) { R_SUCCEED(); } -void KProcess::PinCurrentThread() { - ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); +void KProcess::PinCurrentThread(KernelCore& kernel) { + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel)); // Get the current thread. - const s32 core_id = GetCurrentCoreId(m_kernel); - KThread* cur_thread = GetCurrentThreadPointer(m_kernel); + const s32 core_id = GetCurrentCoreId(kernel); + KThread* cur_thread = GetCurrentThreadPointer(kernel); // If the thread isn't terminated, pin it. if (!cur_thread->IsTerminationRequested()) { // Pin it. this->PinThread(core_id, cur_thread); - cur_thread->Pin(core_id); + cur_thread->Pin(kernel, core_id); // An update is needed. - KScheduler::SetSchedulerUpdateNeeded(m_kernel); + KScheduler::SetSchedulerUpdateNeeded(kernel); } } -void KProcess::UnpinCurrentThread() { - ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); +void KProcess::UnpinCurrentThread(KernelCore& kernel) { + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel)); // Get the current thread. - const s32 core_id = GetCurrentCoreId(m_kernel); - KThread* cur_thread = GetCurrentThreadPointer(m_kernel); + const s32 core_id = GetCurrentCoreId(kernel); + KThread* cur_thread = GetCurrentThreadPointer(kernel); // Unpin it. - cur_thread->Unpin(); + cur_thread->Unpin(kernel); this->UnpinThread(core_id, cur_thread); // An update is needed. - KScheduler::SetSchedulerUpdateNeeded(m_kernel); + KScheduler::SetSchedulerUpdateNeeded(kernel); } -void KProcess::UnpinThread(KThread* thread) { - ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); +void KProcess::UnpinThread(KernelCore& kernel, KThread* thread) { + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel)); // Get the thread's core id. const auto core_id = thread->GetActiveCore(); // Unpin it. this->UnpinThread(core_id, thread); - thread->Unpin(); + thread->Unpin(kernel); // An update is needed. - KScheduler::SetSchedulerUpdateNeeded(m_kernel); + KScheduler::SetSchedulerUpdateNeeded(kernel); } -Result KProcess::GetThreadList(s32* out_num_threads, KProcessAddress out_thread_ids, +Result KProcess::GetThreadList(KernelCore& kernel, s32* out_num_threads, KProcessAddress out_thread_ids, s32 max_out_count) { auto& memory = this->GetMemory(); @@ -1158,7 +1169,7 @@ Result KProcess::GetThreadList(s32* out_num_threads, KProcessAddress out_thread_ R_SUCCEED(); } -void KProcess::Switch(KProcess* cur_process, KProcess* next_process) {} +void KProcess::Switch(KernelCore& kernel, KProcess* cur_process, KProcess* next_process) {} KProcess::KProcess(KernelCore& kernel) : KAutoObjectWithSlabHeapAndContainer(kernel) @@ -1174,17 +1185,16 @@ KProcess::KProcess(KernelCore& kernel) KProcess::~KProcess() = default; -Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size, - KProcessAddress aslr_space_start, size_t aslr_space_offset, bool is_hbl) { +Result KProcess::LoadFromMetadata(KernelCore& kernel, const FileSys::ProgramMetadata& metadata, std::size_t code_size, KProcessAddress aslr_space_start, size_t aslr_space_offset) { // Create a resource limit for the process. const auto pool = static_cast(metadata.GetPoolPartition()); - const auto physical_memory_size = m_kernel.MemoryManager().GetSize(pool); + const auto physical_memory_size = kernel.MemoryManager().GetSize(pool); auto* res_limit = - Kernel::CreateResourceLimitForProcess(m_kernel.System(), physical_memory_size); + Kernel::CreateResourceLimitForProcess(kernel.System(), physical_memory_size); // Ensure we maintain a clean state on exit. SCOPE_EXIT { - res_limit->Close(); + res_limit->Close(kernel); }; // Declare flags and code address. @@ -1243,23 +1253,20 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std: std::memcpy(params.name.data(), name.data(), sizeof(params.name)); // Initialize for application process. - R_TRY(this->Initialize(params, metadata.GetKernelCapabilities(), res_limit, pool, - aslr_space_start)); + R_TRY(this->Initialize(kernel, params, metadata.GetKernelCapabilities(), res_limit, pool, aslr_space_start)); // Assign remaining properties. - m_is_hbl = is_hbl; m_ideal_core_id = metadata.GetMainThreadCore(); // Set up emulation context. - this->InitializeInterfaces(); + this->InitializeInterfaces(kernel); // We succeeded. R_SUCCEED(); } -void KProcess::LoadModule(CodeSet code_set, KProcessAddress base_addr) { - const auto ReprotectSegment = [&](const CodeSet::Segment& segment, - Svc::MemoryPermission permission) { +void KProcess::LoadModule(KernelCore& kernel, CodeSet code_set, KProcessAddress base_addr) { + const auto ReprotectSegment = [&](const CodeSet::Segment& segment, Svc::MemoryPermission permission) { m_page_table.SetProcessMemoryPermission(segment.addr + base_addr, segment.size, permission); }; @@ -1273,7 +1280,7 @@ void KProcess::LoadModule(CodeSet code_set, KProcessAddress base_addr) { const auto& patch = code_set.PatchSegment(); const auto& post_patch = code_set.PostPatchSegment(); if (this->IsApplication() && Settings::IsNceEnabled() && patch.size != 0) { - auto& buffer = m_kernel.System().DeviceMemory().buffer; + auto& buffer = kernel.System().DeviceMemory().buffer; const auto& code = code_set.CodeSegment(); buffer.Protect(GetInteger(base_addr + code.addr), code.size, Common::MemoryPermission::Read | Common::MemoryPermission::Execute); @@ -1292,32 +1299,32 @@ void KProcess::LoadModule(CodeSet code_set, KProcessAddress base_addr) { #endif } -void KProcess::InitializeInterfaces() { +void KProcess::InitializeInterfaces(KernelCore& kernel) { m_exclusive_monitor = Core::MakeExclusiveMonitor(this->GetMemory(), Core::Hardware::NUM_CPU_CORES); #ifdef HAS_NCE if (this->IsApplication() && Settings::IsNceEnabled()) { for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) - m_arm_interfaces[i] = std::make_unique(m_kernel.System(), true, i); + m_arm_interfaces[i] = std::make_unique(kernel.System(), true, i); } else #endif if (this->Is64Bit()) { for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { m_arm_interfaces[i] = std::make_unique( - m_kernel.System(), m_kernel.IsMulticore(), this, + kernel.System(), kernel.IsMulticore(), this, static_cast(*m_exclusive_monitor), i); } } else { for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { m_arm_interfaces[i] = std::make_unique( - m_kernel.System(), m_kernel.IsMulticore(), this, + kernel.System(), kernel.IsMulticore(), this, static_cast(*m_exclusive_monitor), i); } } } -bool KProcess::InsertWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointType type) { +bool KProcess::InsertWatchpoint(KernelCore& kernel, KProcessAddress addr, u64 size, DebugWatchpointType type) { const auto watch{std::find_if(m_watchpoints.begin(), m_watchpoints.end(), [&](const auto& wp) { return wp.type == DebugWatchpointType::None; })}; @@ -1339,7 +1346,7 @@ bool KProcess::InsertWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointT return true; } -bool KProcess::RemoveWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointType type) { +bool KProcess::RemoveWatchpoint(KernelCore& kernel, KProcessAddress addr, u64 size, DebugWatchpointType type) { const auto watch{std::find_if(m_watchpoints.begin(), m_watchpoints.end(), [&](const auto& wp) { return wp.start_address == addr && wp.end_address == addr + size && wp.type == type; })}; diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h index 975448f0dd..c861a9ea51 100644 --- a/src/core/hle/kernel/k_process.h +++ b/src/core/hle/kernel/k_process.h @@ -84,6 +84,9 @@ private: Core::Memory::Memory m_memory; KCapabilities m_capabilities{}; KProcessAddress m_code_address{}; + KProcessAddress m_arg_pointer{}; + KProcessAddress m_arg_return_address{}; + KProcessAddress m_main_thread_handle_addr{}; KHandleTable m_handle_table; KProcessAddress m_plr_address{}; ThreadList m_thread_list{}; @@ -133,14 +136,13 @@ private: bool m_is_initialized : 1 = false; bool m_is_application : 1 = false; bool m_is_default_application_system_resource : 1 = false; - bool m_is_hbl : 1 = false; bool m_is_suspended : 1 = false; bool m_is_immortal : 1 = false; bool m_is_handle_table_initialized : 1 = false; private: - Result StartTermination(); - void FinishTermination(); + Result StartTermination(KernelCore& kernel); + void FinishTermination(KernelCore& kernel); void PinThread(s32 core_id, KThread* thread) { ASSERT(0 <= core_id && core_id < static_cast(Core::Hardware::NUM_CPU_CORES)); @@ -160,16 +162,16 @@ public: explicit KProcess(KernelCore& kernel); ~KProcess() override; - Result Initialize(const Svc::CreateProcessParameter& params, KResourceLimit* res_limit, + Result Initialize(KernelCore& kernel, const Svc::CreateProcessParameter& params, KResourceLimit* res_limit, bool is_real); - Result Initialize(const Svc::CreateProcessParameter& params, const KPageGroup& pg, + Result Initialize(KernelCore& kernel, const Svc::CreateProcessParameter& params, const KPageGroup& pg, std::span caps, KResourceLimit* res_limit, KMemoryManager::Pool pool, bool immortal); - Result Initialize(const Svc::CreateProcessParameter& params, std::span user_caps, + Result Initialize(KernelCore& kernel, const Svc::CreateProcessParameter& params, std::span user_caps, KResourceLimit* res_limit, KMemoryManager::Pool pool, KProcessAddress aslr_space_start); - void Exit(); + void Exit(KernelCore& kernel); const char* GetName() const { return m_name.data(); @@ -220,6 +222,16 @@ public: return m_code_address; } + void SetArgPointer(KProcessAddress addr) { + m_arg_pointer = addr; + } + void SetArgReturnAddress(KProcessAddress addr) { + m_arg_return_address = addr; + } + void SetMainThreadHandleAddr(KProcessAddress addr) { + m_main_thread_handle_addr = addr; + } + size_t GetMainStackSize() const { return m_main_thread_stack_size; } @@ -255,7 +267,7 @@ public: m_pointer_buffer_size = size; } - Result Terminate(); + Result Terminate(KernelCore& kernel); bool IsTerminated() const { return m_state == State::Terminated; @@ -277,10 +289,6 @@ public: return m_capabilities.CanForceDebug(); } - bool IsHbl() const { - return m_is_hbl; - } - u32 GetAllocateOption() const { return m_page_table.GetAllocateOption(); } @@ -292,9 +300,9 @@ public: return m_thread_list; } - bool EnterUserException(); - bool LeaveUserException(); - bool ReleaseUserException(KThread* thread); + bool EnterUserException(KernelCore& kernel); + bool LeaveUserException(KernelCore& kernel); + bool ReleaseUserException(KernelCore& kernel, KThread* thread); KThread* GetPinnedThread(s32 core_id) const { ASSERT(0 <= core_id && core_id < static_cast(Core::Hardware::NUM_CPU_CORES)); @@ -309,10 +317,10 @@ public: return m_resource_limit; } - bool ReserveResource(Svc::LimitableResource which, s64 value); - bool ReserveResource(Svc::LimitableResource which, s64 value, s64 timeout); - void ReleaseResource(Svc::LimitableResource which, s64 value); - void ReleaseResource(Svc::LimitableResource which, s64 value, s64 hint); + bool ReserveResource(KernelCore& kernel, Svc::LimitableResource which, s64 value); + bool ReserveResource(KernelCore& kernel, Svc::LimitableResource which, s64 value, s64 timeout); + void ReleaseResource(KernelCore& kernel, Svc::LimitableResource which, s64 value); + void ReleaseResource(KernelCore& kernel, Svc::LimitableResource which, s64 value, s64 hint); KLightLock& GetStateLock() { return m_state_lock; @@ -335,16 +343,16 @@ public: return m_handle_table; } - size_t GetUsedUserPhysicalMemorySize() const; - size_t GetTotalUserPhysicalMemorySize() const; - size_t GetUsedNonSystemUserPhysicalMemorySize() const; - size_t GetTotalNonSystemUserPhysicalMemorySize() const; + size_t GetUsedUserPhysicalMemorySize(KernelCore& kernel) const; + size_t GetTotalUserPhysicalMemorySize(KernelCore& kernel) const; + size_t GetUsedNonSystemUserPhysicalMemorySize(KernelCore& kernel) const; + size_t GetTotalNonSystemUserPhysicalMemorySize(KernelCore& kernel) const; - Result AddSharedMemory(KSharedMemory* shmem, KProcessAddress address, size_t size); - void RemoveSharedMemory(KSharedMemory* shmem, KProcessAddress address, size_t size); + Result AddSharedMemory(KernelCore& kernel, KSharedMemory* shmem, KProcessAddress address, size_t size); + void RemoveSharedMemory(KernelCore& kernel, KSharedMemory* shmem, KProcessAddress address, size_t size); - Result CreateThreadLocalRegion(KProcessAddress* out); - Result DeleteThreadLocalRegion(KProcessAddress addr); + Result CreateThreadLocalRegion(KernelCore& kernel, KProcessAddress* out); + Result DeleteThreadLocalRegion(KernelCore& kernel, KProcessAddress addr); KProcessAddress GetProcessLocalRegionAddress() const { return m_plr_address; @@ -368,8 +376,8 @@ public: ++m_schedule_count; } - void IncrementRunningThreadCount(); - void DecrementRunningThreadCount(); + void IncrementRunningThreadCount(KernelCore& kernel); + void DecrementRunningThreadCount(KernelCore& kernel); size_t GetRequiredSecureMemorySizeNonDefault() const { if (!this->IsDefaultApplicationSystemResource() && m_system_resource->IsSecureResource()) { @@ -414,11 +422,9 @@ public: } void ClearRunningThread(KThread* thread) { - for (size_t i = 0; i < m_running_threads.size(); ++i) { - if (m_running_threads[i] == thread) { + for (size_t i = 0; i < m_running_threads.size(); ++i) + if (m_running_threads[i] == thread) m_running_threads[i] = nullptr; - } - } } const KSystemResource& GetSystemResource() const { @@ -445,30 +451,27 @@ public: return m_running_thread_switch_counts[core]; } - void RegisterThread(KThread* thread); - void UnregisterThread(KThread* thread); + void RegisterThread(KernelCore& kernel, KThread* thread); + void UnregisterThread(KernelCore& kernel, KThread* thread); + Result Run(KernelCore& kernel, s32 priority, size_t stack_size); + Result Reset(KernelCore& kernel); - Result Run(s32 priority, size_t stack_size); - - Result Reset(); - - void SetDebugBreak() { + void SetDebugBreak(KernelCore& kernel) { if (m_state == State::RunningAttached) { - this->ChangeState(State::DebugBreak); + this->ChangeState(kernel, State::DebugBreak); } } - void SetAttached() { + void SetAttached(KernelCore& kernel) { if (m_state == State::DebugBreak) { - this->ChangeState(State::RunningAttached); + this->ChangeState(kernel, State::RunningAttached); } } - Result SetActivity(Svc::ProcessActivity activity); - - void PinCurrentThread(); - void UnpinCurrentThread(); - void UnpinThread(KThread* thread); + Result SetActivity(KernelCore& kernel, Svc::ProcessActivity activity); + void PinCurrentThread(KernelCore& kernel); + void UnpinCurrentThread(KernelCore& kernel); + void UnpinThread(KernelCore& kernel, KThread* thread); void SignalConditionVariable(uintptr_t cv_key, int32_t count) { return m_cond_var.Signal(cv_key, count); @@ -478,19 +481,17 @@ public: R_RETURN(m_cond_var.Wait(address, cv_key, tag, ns)); } - Result SignalAddressArbiter(uintptr_t address, Svc::SignalType signal_type, s32 value, - s32 count) { + Result SignalAddressArbiter(uintptr_t address, Svc::SignalType signal_type, s32 value, s32 count) { R_RETURN(m_address_arbiter.SignalToAddress(address, signal_type, value, count)); } - Result WaitAddressArbiter(uintptr_t address, Svc::ArbitrationType arb_type, s32 value, - s64 timeout) { + Result WaitAddressArbiter(uintptr_t address, Svc::ArbitrationType arb_type, s32 value, s64 timeout) { R_RETURN(m_address_arbiter.WaitForAddress(address, arb_type, value, timeout)); } - Result GetThreadList(s32* out_num_threads, KProcessAddress out_thread_ids, s32 max_out_count); + Result GetThreadList(KernelCore& kernel, s32* out_num_threads, KProcessAddress out_thread_ids, s32 max_out_count); - static void Switch(KProcess* cur_process, KProcess* next_process); + static void Switch(KernelCore& kernel, KProcess* cur_process, KProcess* next_process); #ifdef HAS_NCE ankerl::unordered_dense::map& GetPostHandlers() noexcept { @@ -504,22 +505,21 @@ public: public: // Attempts to insert a watchpoint into a free slot. Returns false if none are available. - bool InsertWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointType type); + bool InsertWatchpoint(KernelCore& kernel, KProcessAddress addr, u64 size, DebugWatchpointType type); // Attempts to remove the watchpoint specified by the given parameters. - bool RemoveWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointType type); + bool RemoveWatchpoint(KernelCore& kernel, KProcessAddress addr, u64 size, DebugWatchpointType type); const std::array& GetWatchpoints() const { return m_watchpoints; } public: - Result LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size, - KProcessAddress aslr_space_start, size_t aslr_space_offset, bool is_hbl); + Result LoadFromMetadata(KernelCore& kernel, const FileSys::ProgramMetadata& metadata, std::size_t code_size, KProcessAddress aslr_space_start, size_t aslr_space_offset); - void LoadModule(CodeSet code_set, KProcessAddress base_addr); + void LoadModule(KernelCore& kernel, CodeSet code_set, KProcessAddress base_addr); - void InitializeInterfaces(); + void InitializeInterfaces(KernelCore& kernel); Core::Memory::Memory& GetMemory() { return m_memory; @@ -535,9 +535,9 @@ public: return m_is_initialized; } - static void PostDestroy(uintptr_t arg) {} + static void PostDestroy(KernelCore& kernel, uintptr_t arg) {} - void Finalize() override; + void Finalize(KernelCore& kernel) override; u64 GetIdImpl() const { return this->GetProcessId(); @@ -546,34 +546,34 @@ public: return this->GetIdImpl(); } - virtual bool IsSignaled() const override { - ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); + virtual bool IsSignaled(KernelCore& kernel) const override { + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel)); return m_is_signaled; } - void DoWorkerTaskImpl(); + void DoWorkerTaskImpl(KernelCore& kernel); private: - void ChangeState(State new_state) { + void ChangeState(KernelCore& kernel, State new_state) { if (m_state != new_state) { m_state = new_state; m_is_signaled = true; - this->NotifyAvailable(); + this->NotifyAvailable(kernel); } } - Result InitializeHandleTable(s32 size) { + Result InitializeHandleTable(KernelCore& kernel, s32 size) { // Try to initialize the handle table. - R_TRY(m_handle_table.Initialize(size)); + R_TRY(m_handle_table.Initialize(kernel, size)); // We succeeded, so note that we did. m_is_handle_table_initialized = true; R_SUCCEED(); } - void FinalizeHandleTable() { + void FinalizeHandleTable(KernelCore& kernel) { // Finalize the table. - m_handle_table.Finalize(); + m_handle_table.Finalize(kernel); // Note that the table is finalized. m_is_handle_table_initialized = false; diff --git a/src/core/hle/kernel/k_readable_event.cpp b/src/core/hle/kernel/k_readable_event.cpp index c30662666b..a00941bc1e 100644 --- a/src/core/hle/kernel/k_readable_event.cpp +++ b/src/core/hle/kernel/k_readable_event.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -15,50 +18,45 @@ KReadableEvent::KReadableEvent(KernelCore& kernel) : KSynchronizationObject{kern KReadableEvent::~KReadableEvent() = default; -void KReadableEvent::Initialize(KEvent* parent) { +void KReadableEvent::Initialize(KernelCore& kernel, KEvent* parent) { m_is_signaled = false; m_parent = parent; - if (m_parent != nullptr) { - m_parent->Open(); + m_parent->Open(kernel); } } -bool KReadableEvent::IsSignaled() const { - ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); - +bool KReadableEvent::IsSignaled(KernelCore& kernel) const { + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel)); return m_is_signaled; } -void KReadableEvent::Destroy() { +void KReadableEvent::Destroy(KernelCore& kernel) { if (m_parent) { { - KScopedSchedulerLock sl{m_kernel}; + KScopedSchedulerLock sl{kernel}; m_parent->OnReadableEventDestroyed(); } - m_parent->Close(); + m_parent->Close(kernel); } } -Result KReadableEvent::Signal() { - KScopedSchedulerLock lk{m_kernel}; - +Result KReadableEvent::Signal(KernelCore& kernel) { + KScopedSchedulerLock lk{kernel}; if (!m_is_signaled) { m_is_signaled = true; - this->NotifyAvailable(); + this->NotifyAvailable(kernel); } - R_SUCCEED(); } -Result KReadableEvent::Clear() { - this->Reset(); - +Result KReadableEvent::Clear(KernelCore& kernel) { + this->Reset(kernel); R_SUCCEED(); } -Result KReadableEvent::Reset() { - KScopedSchedulerLock lk{m_kernel}; +Result KReadableEvent::Reset(KernelCore& kernel) { + KScopedSchedulerLock lk{kernel}; R_UNLESS(m_is_signaled, ResultInvalidState); diff --git a/src/core/hle/kernel/k_readable_event.h b/src/core/hle/kernel/k_readable_event.h index d2ec363233..63a6e8de60 100644 --- a/src/core/hle/kernel/k_readable_event.h +++ b/src/core/hle/kernel/k_readable_event.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -20,19 +23,17 @@ public: explicit KReadableEvent(KernelCore& kernel); ~KReadableEvent() override; - void Initialize(KEvent* parent); + void Initialize(KernelCore& kernel, KEvent* parent); KEvent* GetParent() const { return m_parent; } - Result Signal(); - Result Clear(); - - bool IsSignaled() const override; - void Destroy() override; - - Result Reset(); + Result Signal(KernelCore& kernel); + Result Clear(KernelCore& kernel); + bool IsSignaled(KernelCore& kernel) const override; + void Destroy(KernelCore& kernel) override; + Result Reset(KernelCore& kernel); private: bool m_is_signaled{}; diff --git a/src/core/hle/kernel/k_resource_limit.cpp b/src/core/hle/kernel/k_resource_limit.cpp index e886f97ebd..1a2c92a665 100644 --- a/src/core/hle/kernel/k_resource_limit.cpp +++ b/src/core/hle/kernel/k_resource_limit.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project @@ -16,12 +16,15 @@ namespace Kernel { constexpr s64 DefaultTimeout = 10000000000; // 10 seconds KResourceLimit::KResourceLimit(KernelCore& kernel) - : KAutoObjectWithSlabHeapAndContainer{kernel}, m_lock{m_kernel}, m_cond_var{m_kernel} {} + : KAutoObjectWithSlabHeapAndContainer{kernel} + , m_lock{kernel} + , m_cond_var{kernel} +{} KResourceLimit::~KResourceLimit() = default; void KResourceLimit::Initialize() {} -void KResourceLimit::Finalize() {} +void KResourceLimit::Finalize(KernelCore& kernel) {} s64 KResourceLimit::GetLimitValue(LimitableResource which) const { const auto index = static_cast(which); @@ -87,11 +90,11 @@ Result KResourceLimit::SetLimitValue(LimitableResource which, s64 value) { R_SUCCEED(); } -bool KResourceLimit::Reserve(LimitableResource which, s64 value) { - return Reserve(which, value, m_kernel.HardwareTimer().GetTick() + DefaultTimeout); +bool KResourceLimit::Reserve(KernelCore& kernel, LimitableResource which, s64 value) { + return Reserve(kernel, which, value, kernel.HardwareTimer().GetTick() + DefaultTimeout); } -bool KResourceLimit::Reserve(LimitableResource which, s64 value, s64 timeout) { +bool KResourceLimit::Reserve(KernelCore& kernel, LimitableResource which, s64 value, s64 timeout) { ASSERT(value >= 0); const auto index = static_cast(which); KScopedLightLock lk(m_lock); @@ -119,7 +122,7 @@ bool KResourceLimit::Reserve(LimitableResource which, s64 value, s64 timeout) { } if (m_current_hints[index] + value <= m_limit_values[index] && - (timeout < 0 || m_kernel.HardwareTimer().GetTick() < timeout)) { + (timeout < 0 || kernel.HardwareTimer().GetTick() < timeout)) { m_waiter_count++; m_cond_var.Wait(std::addressof(m_lock), timeout, false); m_waiter_count--; @@ -131,11 +134,11 @@ bool KResourceLimit::Reserve(LimitableResource which, s64 value, s64 timeout) { return false; } -void KResourceLimit::Release(LimitableResource which, s64 value) { - Release(which, value, value); +void KResourceLimit::Release(KernelCore& kernel, LimitableResource which, s64 value) { + Release(kernel, which, value, value); } -void KResourceLimit::Release(LimitableResource which, s64 value, s64 hint) { +void KResourceLimit::Release(KernelCore& kernel, LimitableResource which, s64 value, s64 hint) { ASSERT(value >= 0); ASSERT(hint >= 0); diff --git a/src/core/hle/kernel/k_resource_limit.h b/src/core/hle/kernel/k_resource_limit.h index b733ec8f81..eb45d23bab 100644 --- a/src/core/hle/kernel/k_resource_limit.h +++ b/src/core/hle/kernel/k_resource_limit.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -32,7 +35,7 @@ public: ~KResourceLimit() override; void Initialize(); - void Finalize() override; + void Finalize(KernelCore& kernel) override; s64 GetLimitValue(LimitableResource which) const; s64 GetCurrentValue(LimitableResource which) const; @@ -41,12 +44,12 @@ public: Result SetLimitValue(LimitableResource which, s64 value); - bool Reserve(LimitableResource which, s64 value); - bool Reserve(LimitableResource which, s64 value, s64 timeout); - void Release(LimitableResource which, s64 value); - void Release(LimitableResource which, s64 value, s64 hint); + bool Reserve(KernelCore& kernel, LimitableResource which, s64 value); + bool Reserve(KernelCore& kernel, LimitableResource which, s64 value, s64 timeout); + void Release(KernelCore& kernel, LimitableResource which, s64 value); + void Release(KernelCore& kernel, LimitableResource which, s64 value, s64 hint); - static void PostDestroy(uintptr_t arg) {} + static void PostDestroy(KernelCore& kernel, uintptr_t arg) {} private: using ResourceArray = std::array(LimitableResource::Count)>; diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp index 4b76e18950..8e629f6456 100644 --- a/src/core/hle/kernel/k_scheduler.cpp +++ b/src/core/hle/kernel/k_scheduler.cpp @@ -30,34 +30,32 @@ static void IncrementScheduledCount(Kernel::KThread* thread) { } } -KScheduler::KScheduler(KernelCore& kernel) : m_kernel{kernel} { - m_switch_fiber = std::make_shared([this] { +KScheduler::KScheduler(KernelCore& kernel) { + m_switch_fiber = std::make_shared([this, &kernel] { while (true) { - ScheduleImplFiber(); + ScheduleImplFiber(kernel); } }); - m_state.needs_scheduling = true; } KScheduler::~KScheduler() = default; -void KScheduler::SetInterruptTaskRunnable() { +void KScheduler::SetInterruptTaskRunnable(KernelCore& kernel) { m_state.interrupt_task_runnable = true; m_state.needs_scheduling = true; } -void KScheduler::RequestScheduleOnInterrupt() { +void KScheduler::RequestScheduleOnInterrupt(KernelCore& kernel) { m_state.needs_scheduling = true; - - if (CanSchedule(m_kernel)) { - ScheduleOnInterrupt(); + if (CanSchedule(kernel)) { + ScheduleOnInterrupt(kernel); } } void KScheduler::DisableScheduling(KernelCore& kernel) { ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() >= 0); - GetCurrentThread(kernel).DisableDispatch(); + GetCurrentThread(kernel).DisableDispatch(kernel); } void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling) { @@ -71,12 +69,12 @@ void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduli return; } - scheduler->RescheduleOtherCores(cores_needing_scheduling); + scheduler->RescheduleOtherCores(kernel, cores_needing_scheduling); if (GetCurrentThread(kernel).GetDisableDispatchCount() > 1) { - GetCurrentThread(kernel).EnableDispatch(); + GetCurrentThread(kernel).EnableDispatch(kernel); } else { - scheduler->RescheduleCurrentCore(); + scheduler->RescheduleCurrentCore(kernel); } } @@ -85,10 +83,10 @@ void KScheduler::RescheduleCurrentHLEThread(KernelCore& kernel) { ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() == 1); // Ensure dummy threads that are waiting block. - GetCurrentThread(kernel).DummyThreadBeginWait(); + GetCurrentThread(kernel).DummyThreadBeginWait(kernel); ASSERT(GetCurrentThread(kernel).GetState() != ThreadState::Waiting); - GetCurrentThread(kernel).EnableDispatch(); + GetCurrentThread(kernel).EnableDispatch(kernel); } u64 KScheduler::UpdateHighestPriorityThreads(KernelCore& kernel) { @@ -99,55 +97,55 @@ u64 KScheduler::UpdateHighestPriorityThreads(KernelCore& kernel) { } } -void KScheduler::Schedule() { - ASSERT(GetCurrentThread(m_kernel).GetDisableDispatchCount() == 1); - ASSERT(m_core_id == GetCurrentCoreId(m_kernel)); +void KScheduler::Schedule(KernelCore& kernel) { + ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() == 1); + ASSERT(m_core_id == GetCurrentCoreId(kernel)); - ScheduleImpl(); + ScheduleImpl(kernel); } -void KScheduler::ScheduleOnInterrupt() { - GetCurrentThread(m_kernel).DisableDispatch(); - Schedule(); - GetCurrentThread(m_kernel).EnableDispatch(); +void KScheduler::ScheduleOnInterrupt(KernelCore& kernel) { + GetCurrentThread(kernel).DisableDispatch(kernel); + Schedule(kernel); + GetCurrentThread(kernel).EnableDispatch(kernel); } -void KScheduler::PreemptSingleCore() { - GetCurrentThread(m_kernel).DisableDispatch(); +void KScheduler::PreemptSingleCore(KernelCore& kernel) { + GetCurrentThread(kernel).DisableDispatch(kernel); - auto* thread = GetCurrentThreadPointer(m_kernel); - auto& previous_scheduler = m_kernel.Scheduler(thread->GetCurrentCore()); - previous_scheduler.Unload(thread); + auto* thread = GetCurrentThreadPointer(kernel); + auto& previous_scheduler = kernel.Scheduler(thread->GetCurrentCore()); + previous_scheduler.Unload(kernel, thread); Common::Fiber::YieldTo(thread->GetHostContext(), *m_switch_fiber); - GetCurrentThread(m_kernel).EnableDispatch(); + GetCurrentThread(kernel).EnableDispatch(kernel); } -void KScheduler::RescheduleCurrentCore() { - ASSERT(!m_kernel.IsPhantomModeForSingleCore()); - ASSERT(GetCurrentThread(m_kernel).GetDisableDispatchCount() == 1); +void KScheduler::RescheduleCurrentCore(KernelCore& kernel) { + ASSERT(!kernel.IsPhantomModeForSingleCore()); + ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() == 1); - GetCurrentThread(m_kernel).EnableDispatch(); + GetCurrentThread(kernel).EnableDispatch(kernel); if (m_state.needs_scheduling.load()) { // Disable interrupts, and then check again if rescheduling is needed. // KScopedInterruptDisable intr_disable; - m_kernel.CurrentScheduler()->RescheduleCurrentCoreImpl(); + kernel.CurrentScheduler()->RescheduleCurrentCoreImpl(kernel); } } -void KScheduler::RescheduleCurrentCoreImpl() { +void KScheduler::RescheduleCurrentCoreImpl(KernelCore& kernel) { // Check that scheduling is needed. if (m_state.needs_scheduling.load()) [[likely]] { - GetCurrentThread(m_kernel).DisableDispatch(); - Schedule(); - GetCurrentThread(m_kernel).EnableDispatch(); + GetCurrentThread(kernel).DisableDispatch(kernel); + Schedule(kernel); + GetCurrentThread(kernel).EnableDispatch(kernel); } } -void KScheduler::Initialize(KThread* main_thread, KThread* idle_thread, s32 core_id) { +void KScheduler::Initialize(KernelCore& kernel, KThread* main_thread, KThread* idle_thread, s32 core_id) { // Set core ID/idle thread/interrupt task manager. m_core_id = core_id; m_idle_thread = idle_thread; @@ -156,39 +154,39 @@ void KScheduler::Initialize(KThread* main_thread, KThread* idle_thread, s32 core // Insert the main thread into the priority queue. // { - // KScopedSchedulerLock lk{m_kernel}; - // GetPriorityQueue(m_kernel).PushBack(GetCurrentThreadPointer(m_kernel)); - // SetSchedulerUpdateNeeded(m_kernel); + // KScopedSchedulerLock lk{kernel}; + // GetPriorityQueue(kernel).PushBack(GetCurrentThreadPointer(kernel)); + // SetSchedulerUpdateNeeded(kernel); // } // Bind interrupt handler. // kernel.GetInterruptManager().BindHandler( - // GetSchedulerInterruptHandler(m_kernel), KInterruptName::Scheduler, m_core_id, + // GetSchedulerInterruptHandler(kernel), KInterruptName::Scheduler, m_core_id, // KInterruptController::PriorityLevel::Scheduler, false, false); // Set the current thread. m_current_thread = main_thread; } -void KScheduler::Activate() { - ASSERT(GetCurrentThread(m_kernel).GetDisableDispatchCount() == 1); +void KScheduler::Activate(KernelCore& kernel) { + ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() == 1); // m_state.should_count_idle = KTargetSystem::IsDebugMode(); m_is_active = true; - RescheduleCurrentCore(); + RescheduleCurrentCore(kernel); } -void KScheduler::OnThreadStart() { - GetCurrentThread(m_kernel).EnableDispatch(); +void KScheduler::OnThreadStart(KernelCore& kernel) { + GetCurrentThread(kernel).EnableDispatch(kernel); } -u64 KScheduler::UpdateHighestPriorityThread(KThread* highest_thread) { +u64 KScheduler::UpdateHighestPriorityThread(KernelCore& kernel, KThread* highest_thread) { if (KThread* prev_highest_thread = m_state.highest_priority_thread; prev_highest_thread != highest_thread) [[likely]] { if (prev_highest_thread != nullptr) [[likely]] { IncrementScheduledCount(prev_highest_thread); prev_highest_thread->SetLastScheduledTick( - m_kernel.System().CoreTiming().GetClockTicks()); + kernel.System().CoreTiming().GetClockTicks()); } if (m_state.should_count_idle) { if (highest_thread != nullptr) [[likely]] { @@ -245,8 +243,7 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) { } top_threads[core_id] = top_thread; - cores_needing_scheduling |= - kernel.Scheduler(core_id).UpdateHighestPriorityThread(top_threads[core_id]); + cores_needing_scheduling |= kernel.Scheduler(core_id).UpdateHighestPriorityThread(kernel, top_threads[core_id]); } // Idle cores are bad. We're going to try to migrate threads to each idle core in turn. @@ -274,8 +271,7 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) { suggested->SetActiveCore(core_id); priority_queue.ChangeCore(suggested_core, suggested); top_threads[core_id] = suggested; - cores_needing_scheduling |= - kernel.Scheduler(core_id).UpdateHighestPriorityThread(top_threads[core_id]); + cores_needing_scheduling |= kernel.Scheduler(core_id).UpdateHighestPriorityThread(kernel, top_threads[core_id]); break; } @@ -298,17 +294,13 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) { // The candidate core can run some other thread! We'll migrate its current // top thread to us. top_threads[candidate_core] = next_on_candidate_core; - cores_needing_scheduling |= - kernel.Scheduler(candidate_core) - .UpdateHighestPriorityThread(top_threads[candidate_core]); + cores_needing_scheduling |= kernel.Scheduler(candidate_core).UpdateHighestPriorityThread(kernel, top_threads[candidate_core]); // Perform the migration. suggested->SetActiveCore(core_id); priority_queue.ChangeCore(candidate_core, suggested); top_threads[core_id] = suggested; - cores_needing_scheduling |= - kernel.Scheduler(core_id).UpdateHighestPriorityThread( - top_threads[core_id]); + cores_needing_scheduling |= kernel.Scheduler(core_id).UpdateHighestPriorityThread(kernel, top_threads[core_id]); break; } } @@ -319,21 +311,21 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) { } // HACK: any waiting dummy threads can wake up now. - kernel.GlobalSchedulerContext().WakeupWaitingDummyThreads(); + kernel.GlobalSchedulerContext().WakeupWaitingDummyThreads(kernel); // HACK: if we are a dummy thread, and we need to go sleep, indicate // that for when the lock is released. KThread* const cur_thread = GetCurrentThreadPointer(kernel); if (cur_thread->IsDummyThread() && cur_thread->GetState() != ThreadState::Runnable) { - cur_thread->RequestDummyThreadWait(); + cur_thread->RequestDummyThreadWait(kernel); } return cores_needing_scheduling; } -void KScheduler::SwitchThread(KThread* next_thread) { - KProcess* const cur_process = GetCurrentProcessPointer(m_kernel); - KThread* const cur_thread = GetCurrentThreadPointer(m_kernel); +void KScheduler::SwitchThread(KernelCore& kernel, KThread* next_thread) { + KProcess* const cur_process = GetCurrentProcessPointer(kernel); + KThread* const cur_thread = GetCurrentThreadPointer(kernel); // We never want to schedule a null thread, so use the idle thread if we don't have a next. if (next_thread == nullptr) { @@ -355,7 +347,7 @@ void KScheduler::SwitchThread(KThread* next_thread) { // Update the CPU time tracking variables. const s64 prev_tick = m_last_context_switch_time; - const s64 cur_tick = m_kernel.System().CoreTiming().GetClockTicks(); + const s64 cur_tick = kernel.System().CoreTiming().GetClockTicks(); const s64 tick_diff = cur_tick - prev_tick; cur_thread->AddCpuTime(m_core_id, tick_diff); if (cur_process != nullptr) { @@ -379,23 +371,23 @@ void KScheduler::SwitchThread(KThread* next_thread) { // } // Set the new thread. - SetCurrentThread(m_kernel, next_thread); + SetCurrentThread(kernel, next_thread); m_current_thread = next_thread; // Set the new Thread Local region. // cpu::SwitchThreadLocalRegion(GetInteger(next_thread->GetThreadLocalRegionAddress())); // Update the thread's cpu time differential in TLS, if relevant. - next_thread->UpdateTlsThreadCpuTime(cur_tick); + next_thread->UpdateTlsThreadCpuTime(kernel, cur_tick); } -void KScheduler::ScheduleImpl() { +void KScheduler::ScheduleImpl(KernelCore& kernel) { // First, clear the needs scheduling bool. m_state.needs_scheduling.store(false, std::memory_order_relaxed); std::atomic_thread_fence(std::memory_order_seq_cst); // Load the appropriate thread pointers for scheduling. - KThread* const cur_thread{GetCurrentThreadPointer(m_kernel)}; + KThread* const cur_thread{GetCurrentThreadPointer(kernel)}; KThread* highest_priority_thread{m_state.highest_priority_thread}; // Check whether there are runnable interrupt tasks. @@ -423,7 +415,7 @@ void KScheduler::ScheduleImpl() { // Returning from ScheduleImpl occurs after this thread has been scheduled again. } -void KScheduler::ScheduleImplFiber() { +void KScheduler::ScheduleImplFiber(KernelCore& kernel) { KThread* const cur_thread{m_switch_cur_thread}; KThread* highest_priority_thread{m_switch_highest_priority_thread}; @@ -437,7 +429,7 @@ void KScheduler::ScheduleImplFiber() { m_switch_from_schedule = false; // Save the original thread context. - Unload(cur_thread); + Unload(kernel, cur_thread); // The current thread's context has been entirely taken care of. // Now we want to loop until we successfully switch the thread context. @@ -468,7 +460,7 @@ void KScheduler::ScheduleImplFiber() { // It's time to switch the thread. // Switch to the highest priority thread. - SwitchThread(highest_priority_thread); + SwitchThread(kernel, highest_priority_thread); // Check if we need scheduling. If we do, then we can't complete the switch and should // retry. @@ -493,14 +485,14 @@ void KScheduler::ScheduleImplFiber() { } // Reload the guest thread context. - Reload(highest_priority_thread); + Reload(kernel, highest_priority_thread); // Reload the host thread. Common::Fiber::YieldTo(m_switch_fiber, *highest_priority_thread->m_host_context); } -void KScheduler::Unload(KThread* thread) { - m_kernel.PhysicalCore(m_core_id).SaveContext(thread); +void KScheduler::Unload(KernelCore& kernel, KThread* thread) { + kernel.PhysicalCore(m_core_id).SaveContext(thread); // Check if the thread is terminated by checking the DPC flags. if ((thread->GetStackParameters().dpc_flags & static_cast(DpcFlag::Terminated)) == 0) { @@ -509,8 +501,8 @@ void KScheduler::Unload(KThread* thread) { } } -void KScheduler::Reload(KThread* thread) { - m_kernel.PhysicalCore(m_core_id).LoadContext(thread); +void KScheduler::Reload(KernelCore& kernel, KThread* thread) { + kernel.PhysicalCore(m_core_id).LoadContext(thread); } void KScheduler::ClearPreviousThread(KernelCore& kernel, KThread* thread) { @@ -877,9 +869,9 @@ void KScheduler::YieldToAnyThread(KernelCore& kernel) { } } -void KScheduler::RescheduleOtherCores(u64 cores_needing_scheduling) { +void KScheduler::RescheduleOtherCores(KernelCore& kernel, u64 cores_needing_scheduling) { if (const u64 core_mask = cores_needing_scheduling & ~(1ULL << m_core_id); core_mask != 0) { - RescheduleCores(m_kernel, core_mask); + RescheduleCores(kernel, core_mask); } } diff --git a/src/core/hle/kernel/k_scheduler.h b/src/core/hle/kernel/k_scheduler.h index d85a0c0408..5a896fd899 100644 --- a/src/core/hle/kernel/k_scheduler.h +++ b/src/core/hle/kernel/k_scheduler.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -41,15 +44,15 @@ public: explicit KScheduler(KernelCore& kernel); ~KScheduler(); - void Initialize(KThread* main_thread, KThread* idle_thread, s32 core_id); - void Activate(); - void OnThreadStart(); - void Unload(KThread* thread); - void Reload(KThread* thread); + void Initialize(KernelCore& kernel, KThread* main_thread, KThread* idle_thread, s32 core_id); + void Activate(KernelCore& kernel); + void OnThreadStart(KernelCore& kernel); + void Unload(KernelCore& kernel, KThread* thread); + void Reload(KernelCore& kernel, KThread* thread); - void SetInterruptTaskRunnable(); - void RequestScheduleOnInterrupt(); - void PreemptSingleCore(); + void SetInterruptTaskRunnable(KernelCore& kernel); + void RequestScheduleOnInterrupt(KernelCore& kernel); + void PreemptSingleCore(KernelCore& kernel); u64 GetIdleCount() { return m_state.idle_count; @@ -122,18 +125,18 @@ private: static void RescheduleCurrentHLEThread(KernelCore& kernel); // Instanced private API. - void ScheduleImpl(); - void ScheduleImplFiber(); - void SwitchThread(KThread* next_thread); + void ScheduleImpl(KernelCore& kernel); + void ScheduleImplFiber(KernelCore& kernel); + void SwitchThread(KernelCore& kernel, KThread* next_thread); - void Schedule(); - void ScheduleOnInterrupt(); + void Schedule(KernelCore& kernel); + void ScheduleOnInterrupt(KernelCore& kernel); - void RescheduleOtherCores(u64 cores_needing_scheduling); - void RescheduleCurrentCore(); - void RescheduleCurrentCoreImpl(); + void RescheduleOtherCores(KernelCore& kernel, u64 cores_needing_scheduling); + void RescheduleCurrentCore(KernelCore& kernel); + void RescheduleCurrentCoreImpl(KernelCore& kernel); - u64 UpdateHighestPriorityThread(KThread* thread); + u64 UpdateHighestPriorityThread(KernelCore& kernel, KThread* thread); private: friend class KScopedDisableDispatch; @@ -149,14 +152,12 @@ private: KInterruptTaskManager* interrupt_task_manager{nullptr}; }; - KernelCore& m_kernel; SchedulingState m_state; bool m_is_active{false}; s32 m_core_id{0}; s64 m_last_context_switch_time{0}; KThread* m_idle_thread{nullptr}; std::atomic m_current_thread{nullptr}; - std::shared_ptr m_switch_fiber{}; KThread* m_switch_cur_thread{}; KThread* m_switch_highest_priority_thread{}; diff --git a/src/core/hle/kernel/k_scoped_resource_reservation.h b/src/core/hle/kernel/k_scoped_resource_reservation.h index 2cc464612a..4fc66af725 100644 --- a/src/core/hle/kernel/k_scoped_resource_reservation.h +++ b/src/core/hle/kernel/k_scoped_resource_reservation.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -11,34 +14,42 @@ namespace Kernel { class KScopedResourceReservation { public: - explicit KScopedResourceReservation(KResourceLimit* l, LimitableResource r, s64 v, s64 timeout) - : m_limit(l), m_value(v), m_resource(r) { + explicit KScopedResourceReservation(KernelCore& kernel, KResourceLimit* l, LimitableResource r, s64 v, s64 timeout) + : m_kernel{kernel} + , m_limit(l) + , m_value(v) + , m_resource(r) + { if (m_limit && m_value) { - m_succeeded = m_limit->Reserve(m_resource, m_value, timeout); + m_succeeded = m_limit->Reserve(kernel, m_resource, m_value, timeout); } else { m_succeeded = true; } } - explicit KScopedResourceReservation(KResourceLimit* l, LimitableResource r, s64 v = 1) - : m_limit(l), m_value(v), m_resource(r) { + explicit KScopedResourceReservation(KernelCore& kernel, KResourceLimit* l, LimitableResource r, s64 v = 1) + : m_kernel{kernel} + , m_limit(l) + , m_value(v) + , m_resource(r) + { if (m_limit && m_value) { - m_succeeded = m_limit->Reserve(m_resource, m_value); + m_succeeded = m_limit->Reserve(kernel, m_resource, m_value); } else { m_succeeded = true; } } - explicit KScopedResourceReservation(const KProcess* p, LimitableResource r, s64 v, s64 t) - : KScopedResourceReservation(p->GetResourceLimit(), r, v, t) {} + explicit KScopedResourceReservation(KernelCore& kernel, const KProcess* p, LimitableResource r, s64 v, s64 t) + : KScopedResourceReservation(kernel, p->GetResourceLimit(), r, v, t) {} - explicit KScopedResourceReservation(const KProcess* p, LimitableResource r, s64 v = 1) - : KScopedResourceReservation(p->GetResourceLimit(), r, v) {} + explicit KScopedResourceReservation(KernelCore& kernel, const KProcess* p, LimitableResource r, s64 v = 1) + : KScopedResourceReservation(kernel, p->GetResourceLimit(), r, v) {} ~KScopedResourceReservation() noexcept { if (m_limit && m_value && m_succeeded) { // Resource was not committed, release the reservation. - m_limit->Release(m_resource, m_value); + m_limit->Release(m_kernel, m_resource, m_value); } } @@ -52,6 +63,7 @@ public: } private: + KernelCore& m_kernel; KResourceLimit* m_limit{}; s64 m_value{}; LimitableResource m_resource{}; diff --git a/src/core/hle/kernel/k_server_port.cpp b/src/core/hle/kernel/k_server_port.cpp index bb6632f58e..dad6ef0678 100644 --- a/src/core/hle/kernel/k_server_port.cpp +++ b/src/core/hle/kernel/k_server_port.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -15,18 +18,18 @@ namespace Kernel { KServerPort::KServerPort(KernelCore& kernel) : KSynchronizationObject{kernel} {} KServerPort::~KServerPort() = default; -void KServerPort::Initialize(KPort* parent) { +void KServerPort::Initialize(KernelCore& kernel, KPort* parent) { // Set member variables. m_parent = parent; } -bool KServerPort::IsLight() const { +bool KServerPort::IsLight(KernelCore& kernel) const { return this->GetParent()->IsLight(); } -void KServerPort::CleanupSessions() { +void KServerPort::CleanupSessions(KernelCore& kernel) { // Ensure our preconditions are met. - if (this->IsLight()) { + if (this->IsLight(kernel)) { ASSERT(m_session_list.empty()); } else { ASSERT(m_light_session_list.empty()); @@ -37,7 +40,7 @@ void KServerPort::CleanupSessions() { // Get the last session in the list. KServerSession* session = nullptr; { - KScopedSchedulerLock sl{m_kernel}; + KScopedSchedulerLock sl{kernel}; if (!m_session_list.empty()) { session = std::addressof(m_session_list.front()); m_session_list.pop_front(); @@ -46,7 +49,7 @@ void KServerPort::CleanupSessions() { // Close the session. if (session != nullptr) { - session->Close(); + session->Close(kernel); } else { break; } @@ -57,7 +60,7 @@ void KServerPort::CleanupSessions() { // Get the last session in the list. KLightServerSession* session = nullptr; { - KScopedSchedulerLock sl{m_kernel}; + KScopedSchedulerLock sl{kernel}; if (!m_light_session_list.empty()) { session = std::addressof(m_light_session_list.front()); m_light_session_list.pop_front(); @@ -66,60 +69,60 @@ void KServerPort::CleanupSessions() { // Close the session. if (session != nullptr) { - session->Close(); + session->Close(kernel); } else { break; } } } -void KServerPort::Destroy() { +void KServerPort::Destroy(KernelCore& kernel) { // Note with our parent that we're closed. - m_parent->OnServerClosed(); + m_parent->OnServerClosed(kernel); // Perform necessary cleanup of our session lists. - this->CleanupSessions(); + this->CleanupSessions(kernel); // Close our reference to our parent. - m_parent->Close(); + m_parent->Close(kernel); } -bool KServerPort::IsSignaled() const { - if (this->IsLight()) { +bool KServerPort::IsSignaled(KernelCore& kernel) const { + if (this->IsLight(kernel)) { return !m_light_session_list.empty(); } else { return !m_session_list.empty(); } } -void KServerPort::EnqueueSession(KServerSession* session) { - ASSERT(!this->IsLight()); +void KServerPort::EnqueueSession(KernelCore& kernel, KServerSession* session) { + ASSERT(!this->IsLight(kernel)); - KScopedSchedulerLock sl{m_kernel}; + KScopedSchedulerLock sl{kernel}; // Add the session to our queue. m_session_list.push_back(*session); if (m_session_list.size() == 1) { - this->NotifyAvailable(); + this->NotifyAvailable(kernel); } } -void KServerPort::EnqueueSession(KLightServerSession* session) { - ASSERT(this->IsLight()); +void KServerPort::EnqueueSession(KernelCore& kernel, KLightServerSession* session) { + ASSERT(this->IsLight(kernel)); - KScopedSchedulerLock sl{m_kernel}; + KScopedSchedulerLock sl{kernel}; // Add the session to our queue. m_light_session_list.push_back(*session); if (m_light_session_list.size() == 1) { - this->NotifyAvailable(); + this->NotifyAvailable(kernel); } } -KServerSession* KServerPort::AcceptSession() { - ASSERT(!this->IsLight()); +KServerSession* KServerPort::AcceptSession(KernelCore& kernel) { + ASSERT(!this->IsLight(kernel)); - KScopedSchedulerLock sl{m_kernel}; + KScopedSchedulerLock sl{kernel}; // Return the first session in the list. if (m_session_list.empty()) { @@ -131,10 +134,10 @@ KServerSession* KServerPort::AcceptSession() { return session; } -KLightServerSession* KServerPort::AcceptLightSession() { - ASSERT(this->IsLight()); +KLightServerSession* KServerPort::AcceptLightSession(KernelCore& kernel) { + ASSERT(this->IsLight(kernel)); - KScopedSchedulerLock sl{m_kernel}; + KScopedSchedulerLock sl{kernel}; // Return the first session in the list. if (m_light_session_list.empty()) { diff --git a/src/core/hle/kernel/k_server_port.h b/src/core/hle/kernel/k_server_port.h index 72fdb6734a..5996a3a3ee 100644 --- a/src/core/hle/kernel/k_server_port.h +++ b/src/core/hle/kernel/k_server_port.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -26,29 +29,29 @@ public: explicit KServerPort(KernelCore& kernel); ~KServerPort() override; - void Initialize(KPort* parent); + void Initialize(KernelCore& kernel, KPort* parent); - void EnqueueSession(KServerSession* session); - void EnqueueSession(KLightServerSession* session); + void EnqueueSession(KernelCore& kernel, KServerSession* session); + void EnqueueSession(KernelCore& kernel, KLightServerSession* session); - KServerSession* AcceptSession(); - KLightServerSession* AcceptLightSession(); + KServerSession* AcceptSession(KernelCore& kernel); + KLightServerSession* AcceptLightSession(KernelCore& kernel); const KPort* GetParent() const { return m_parent; } - bool IsLight() const; + bool IsLight(KernelCore& kernel) const; // Overridden virtual functions. - void Destroy() override; - bool IsSignaled() const override; + void Destroy(KernelCore& kernel) override; + bool IsSignaled(KernelCore& kernel) const override; private: using SessionList = Common::IntrusiveListBaseTraits::ListType; using LightSessionList = Common::IntrusiveListBaseTraits::ListType; - void CleanupSessions(); + void CleanupSessions(KernelCore& kernel); SessionList m_session_list{}; LightSessionList m_light_session_list{}; diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp index 4c91235ac9..e3cfe6b6c2 100644 --- a/src/core/hle/kernel/k_server_session.cpp +++ b/src/core/hle/kernel/k_server_session.cpp @@ -159,7 +159,7 @@ private: }; template -Result ProcessMessageSpecialData(s32& offset, KProcess& dst_process, KProcess& src_process, +Result ProcessMessageSpecialData(KernelCore& kernel, s32& offset, KProcess& dst_process, KProcess& src_process, KThread& src_thread, const MessageBuffer& dst_msg, const MessageBuffer& src_msg, const MessageBuffer::SpecialHeader& src_special_header) { @@ -185,10 +185,9 @@ Result ProcessMessageSpecialData(s32& offset, KProcess& dst_process, KProcess& s // If we're in a success state, try to move the handle to the new table. if (R_SUCCEEDED(result) && src_handle != Svc::InvalidHandle) { KScopedAutoObject obj = - src_handle_table.GetObjectForIpc(src_handle, std::addressof(src_thread)); + src_handle_table.GetObjectForIpc(kernel, src_handle, std::addressof(src_thread)); if (obj.IsNotNull()) { - Result add_result = - dst_handle_table.Add(std::addressof(dst_handle), obj.GetPointerUnsafe()); + Result add_result = dst_handle_table.Add(kernel, std::addressof(dst_handle), obj.GetPointerUnsafe()); if (R_FAILED(add_result)) { result = add_result; dst_handle = Svc::InvalidHandle; @@ -213,12 +212,10 @@ Result ProcessMessageSpecialData(s32& offset, KProcess& dst_process, KProcess& s if (src_handle != Svc::InvalidHandle) { if (R_SUCCEEDED(result)) { KScopedAutoObject obj = - src_handle_table.GetObjectForIpcWithoutPseudoHandle(src_handle); + src_handle_table.GetObjectForIpcWithoutPseudoHandle(kernel, src_handle); if (obj.IsNotNull()) { - Result add_result = dst_handle_table.Add(std::addressof(dst_handle), - obj.GetPointerUnsafe()); - - src_handle_table.Remove(src_handle); + Result add_result = dst_handle_table.Add(kernel, std::addressof(dst_handle), obj.GetPointerUnsafe()); + src_handle_table.Remove(kernel, src_handle); if (R_FAILED(add_result)) { result = add_result; @@ -228,7 +225,7 @@ Result ProcessMessageSpecialData(s32& offset, KProcess& dst_process, KProcess& s result = ResultInvalidHandle; } } else { - src_handle_table.Remove(src_handle); + src_handle_table.Remove(kernel, src_handle); } } @@ -336,7 +333,7 @@ constexpr Result GetMapAliasTestStateAndAttributeMask(KMemoryState& out_state, R_SUCCEED(); } -void CleanupSpecialData(KProcess& dst_process, u32* dst_msg_ptr, size_t dst_buffer_size) { +void CleanupSpecialData(KernelCore& kernel, KProcess& dst_process, u32* dst_msg_ptr, size_t dst_buffer_size) { // Parse the message. const MessageBuffer dst_msg(dst_msg_ptr, dst_buffer_size); const MessageBuffer::MessageHeader dst_header(dst_msg); @@ -363,15 +360,14 @@ void CleanupSpecialData(KProcess& dst_process, u32* dst_msg_ptr, size_t dst_buff const Handle handle = dst_msg.GetHandle(offset); if (handle != Svc::InvalidHandle) { - dst_handle_table.Remove(handle); + dst_handle_table.Remove(kernel, handle); } offset = dst_msg.SetHandle(offset, Svc::InvalidHandle); } } -Result CleanupServerHandles(KernelCore& kernel, uint64_t message, size_t buffer_size, - KPhysicalAddress message_paddr) { +Result CleanupServerHandles(KernelCore& kernel, uint64_t message, size_t buffer_size, KPhysicalAddress message_paddr) { // Server is assumed to be current thread. KThread& thread = GetCurrentThread(kernel); @@ -410,7 +406,7 @@ Result CleanupServerHandles(KernelCore& kernel, uint64_t message, size_t buffer_ // Close the handles. for (auto i = 0; i < special_header.GetMoveHandleCount(); ++i) { - handle_table.Remove(msg.GetHandle(offset)); + handle_table.Remove(kernel, msg.GetHandle(offset)); offset += static_cast(sizeof(Svc::Handle) / sizeof(u32)); } } @@ -558,7 +554,7 @@ Result ReceiveMessage(KernelCore& kernel, bool& recv_list_broken, uint64_t dst_m recv_list_broken = false; // Set the server process for the request. - request->SetServerProcess(std::addressof(dst_process)); + request->SetServerProcess(kernel, std::addressof(dst_process)); // Determine the message buffers. u32 *dst_msg_ptr, *src_msg_ptr; @@ -639,7 +635,7 @@ Result ReceiveMessage(KernelCore& kernel, bool& recv_list_broken, uint64_t dst_m // Cleanup special data. if (src_header.GetHasSpecialHeader()) { - CleanupSpecialData(dst_process, dst_msg_ptr, dst_buffer_size); + CleanupSpecialData(kernel, dst_process, dst_msg_ptr, dst_buffer_size); } // Cleanup the header if the receive list isn't broken. @@ -661,7 +657,7 @@ Result ReceiveMessage(KernelCore& kernel, bool& recv_list_broken, uint64_t dst_m }; // Process special data. - R_TRY(ProcessMessageSpecialData(offset, dst_process, src_process, src_thread, + R_TRY(ProcessMessageSpecialData(kernel, offset, dst_process, src_process, src_thread, dst_msg, src_msg, src_special_header)); } @@ -922,7 +918,7 @@ Result SendMessage(KernelCore& kernel, uint64_t src_message_buffer, size_t src_b // Cleanup special data. if (processed_special_data) { if (src_header.GetHasSpecialHeader()) { - CleanupSpecialData(dst_process, dst_msg_ptr, dst_buffer_size); + CleanupSpecialData(kernel, dst_process, dst_msg_ptr, dst_buffer_size); } } else { CleanupServerHandles(kernel, src_user ? src_message_buffer : 0, src_buffer_size, @@ -987,7 +983,7 @@ Result SendMessage(KernelCore& kernel, uint64_t src_message_buffer, size_t src_b ASSERT(GetCurrentThreadPointer(kernel) == std::addressof(src_thread)); processed_special_data = true; if (src_header.GetHasSpecialHeader()) { - R_TRY(ProcessMessageSpecialData(offset, dst_process, src_process, src_thread, + R_TRY(ProcessMessageSpecialData(kernel, offset, dst_process, src_process, src_thread, dst_msg, src_msg, src_special_header)); } @@ -1080,19 +1076,17 @@ void ReplyAsyncError(KProcess* to_process, uint64_t to_msg_buf, size_t to_msg_bu } // namespace KServerSession::KServerSession(KernelCore& kernel) - : KSynchronizationObject{kernel}, m_lock{m_kernel} {} + : KSynchronizationObject{kernel}, m_lock{kernel} {} KServerSession::~KServerSession() = default; -void KServerSession::Destroy() { - m_parent->OnServerClosed(); - - this->CleanupRequests(); - - m_parent->Close(); +void KServerSession::Destroy(KernelCore& kernel) { + m_parent->OnServerClosed(kernel); + this->CleanupRequests(kernel); + m_parent->Close(kernel); } -Result KServerSession::ReceiveRequest(uintptr_t server_message, uintptr_t server_buffer_size, +Result KServerSession::ReceiveRequest(KernelCore& kernel, uintptr_t server_message, uintptr_t server_buffer_size, KPhysicalAddress server_message_paddr, std::shared_ptr* out_context, std::weak_ptr manager) { @@ -1104,7 +1098,7 @@ Result KServerSession::ReceiveRequest(uintptr_t server_message, uintptr_t server KThread* client_thread; { - KScopedSchedulerLock sl{m_kernel}; + KScopedSchedulerLock sl{kernel}; // Ensure that we can service the request. R_UNLESS(!m_parent->IsClientClosed(), ResultSessionClosed); @@ -1124,11 +1118,11 @@ Result KServerSession::ReceiveRequest(uintptr_t server_message, uintptr_t server R_UNLESS(client_thread != nullptr, ResultSessionClosed); // Open the client thread. - client_thread->Open(); + client_thread->Open(kernel); } SCOPE_EXIT { - client_thread->Close(); + client_thread->Close(kernel); }; // Set the request as our current. @@ -1150,13 +1144,13 @@ Result KServerSession::ReceiveRequest(uintptr_t server_message, uintptr_t server Core::Memory::Memory& memory{client_thread->GetOwnerProcess()->GetMemory()}; u32* cmd_buf{reinterpret_cast(memory.GetPointer(client_message))}; *out_context = - std::make_shared(m_kernel, memory, this, client_thread); + std::make_shared(kernel, memory, this, client_thread); (*out_context)->SetSessionRequestManager(manager); (*out_context)->PopulateFromIncomingCommandBuffer(cmd_buf); // We succeeded. R_SUCCEED(); } else { - result = ReceiveMessage(m_kernel, recv_list_broken, server_message, server_buffer_size, + result = ReceiveMessage(kernel, recv_list_broken, server_message, server_buffer_size, server_message_paddr, *client_thread, client_message, client_buffer_size, this, request); } @@ -1168,11 +1162,11 @@ Result KServerSession::ReceiveRequest(uintptr_t server_message, uintptr_t server // Clear the current request. { - KScopedSchedulerLock sl(m_kernel); + KScopedSchedulerLock sl(kernel); ASSERT(m_current_request == request); m_current_request = nullptr; if (!m_request_list.empty()) { - this->NotifyAvailable(); + this->NotifyAvailable(kernel); } } @@ -1180,7 +1174,7 @@ Result KServerSession::ReceiveRequest(uintptr_t server_message, uintptr_t server { // After we reply, close our reference to the request. SCOPE_EXIT { - request->Close(); + request->Close(kernel); }; // Get the event to check whether the request is async. @@ -1199,13 +1193,13 @@ Result KServerSession::ReceiveRequest(uintptr_t server_message, uintptr_t server client_pt.UnlockForIpcUserBuffer(client_message, client_buffer_size); // Signal the event. - event->Signal(); + event->Signal(kernel); } else { // End the client thread's wait. - KScopedSchedulerLock sl(m_kernel); + KScopedSchedulerLock sl(kernel); if (!client_thread->IsTerminationRequested()) { - client_thread->EndWait(result_for_client); + client_thread->EndWait(kernel, result_for_client); } } } @@ -1221,7 +1215,7 @@ Result KServerSession::ReceiveRequest(uintptr_t server_message, uintptr_t server R_RETURN(result); } -Result KServerSession::SendReply(uintptr_t server_message, uintptr_t server_buffer_size, +Result KServerSession::SendReply(KernelCore& kernel, uintptr_t server_message, uintptr_t server_buffer_size, KPhysicalAddress server_message_paddr, bool is_hle) { // Lock the session. KScopedLightLock lk{m_lock}; @@ -1229,7 +1223,7 @@ Result KServerSession::SendReply(uintptr_t server_message, uintptr_t server_buff // Get the request. KSessionRequest* request; { - KScopedSchedulerLock sl{m_kernel}; + KScopedSchedulerLock sl{kernel}; // Get the current request. request = m_current_request; @@ -1238,13 +1232,13 @@ Result KServerSession::SendReply(uintptr_t server_message, uintptr_t server_buff // Clear the current request, since we're processing it. m_current_request = nullptr; if (!m_request_list.empty()) { - this->NotifyAvailable(); + this->NotifyAvailable(kernel); } } // Close reference to the request once we're done processing it. SCOPE_EXIT { - request->Close(); + request->Close(kernel); }; // Extract relevant information from the request. @@ -1263,7 +1257,7 @@ Result KServerSession::SendReply(uintptr_t server_message, uintptr_t server_buff // HLE servers write directly to a pointer to the thread command buffer. Therefore // the reply has already been written in this case. } else { - result = SendMessage(m_kernel, server_message, server_buffer_size, server_message_paddr, + result = SendMessage(kernel, server_message, server_buffer_size, server_message_paddr, *client_thread, client_message, client_buffer_size, this, request); } } else if (!is_hle) { @@ -1275,7 +1269,7 @@ Result KServerSession::SendReply(uintptr_t server_message, uintptr_t server_buff (client_process != nullptr) ? std::addressof(client_process->GetPageTable()) : nullptr; // Cleanup server handles. - result = CleanupServerHandles(m_kernel, server_message, server_buffer_size, + result = CleanupServerHandles(kernel, server_message, server_buffer_size, server_message_paddr); // Cleanup mappings. @@ -1313,13 +1307,13 @@ Result KServerSession::SendReply(uintptr_t server_message, uintptr_t server_buff client_page_table->UnlockForIpcUserBuffer(client_message, client_buffer_size); // Signal the event. - event->Signal(); + event->Signal(kernel); } else { // End the client thread's wait. - KScopedSchedulerLock sl{m_kernel}; + KScopedSchedulerLock sl{kernel}; if (!client_thread->IsTerminationRequested()) { - client_thread->EndWait(client_result); + client_thread->EndWait(kernel, client_result); } } } @@ -1327,45 +1321,45 @@ Result KServerSession::SendReply(uintptr_t server_message, uintptr_t server_buff R_RETURN(result); } -Result KServerSession::OnRequest(KSessionRequest* request) { +Result KServerSession::OnRequest(KernelCore& kernel, KSessionRequest* request) { // Create the wait queue. - ThreadQueueImplForKServerSessionRequest wait_queue{m_kernel}; + ThreadQueueImplForKServerSessionRequest wait_queue{kernel}; { // Lock the scheduler. - KScopedSchedulerLock sl{m_kernel}; + KScopedSchedulerLock sl{kernel}; // Ensure that we can handle new requests. R_UNLESS(!m_parent->IsServerClosed(), ResultSessionClosed); // Check that we're not terminating. - R_UNLESS(!GetCurrentThread(m_kernel).IsTerminationRequested(), ResultTerminationRequested); + R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(), ResultTerminationRequested); // Get whether we're empty. const bool was_empty = m_request_list.empty(); // Add the request to the list. - request->Open(); + request->Open(kernel); m_request_list.push_back(*request); // If we were empty, signal. if (was_empty) { - this->NotifyAvailable(); + this->NotifyAvailable(kernel); } // If we have a request event, this is asynchronous, and we don't need to wait. R_SUCCEED_IF(request->GetEvent() != nullptr); // This is a synchronous request, so we should wait for our request to complete. - GetCurrentThread(m_kernel).SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC); - GetCurrentThread(m_kernel).BeginWait(std::addressof(wait_queue)); + GetCurrentThread(kernel).SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC); + GetCurrentThread(kernel).BeginWait(kernel, std::addressof(wait_queue)); } - return GetCurrentThread(m_kernel).GetWaitResult(); + return GetCurrentThread(kernel).GetWaitResult(); } -bool KServerSession::IsSignaled() const { - ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); +bool KServerSession::IsSignaled(KernelCore& kernel) const { + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel)); // If the client is closed, we're always signaled. if (m_parent->IsClientClosed()) { @@ -1376,7 +1370,7 @@ bool KServerSession::IsSignaled() const { return !m_request_list.empty() && m_current_request == nullptr; } -void KServerSession::CleanupRequests() { +void KServerSession::CleanupRequests(KernelCore& kernel) { KScopedLightLock lk(m_lock); // Clean up any pending requests. @@ -1384,7 +1378,7 @@ void KServerSession::CleanupRequests() { // Get the next request. KSessionRequest* request = nullptr; { - KScopedSchedulerLock sl{m_kernel}; + KScopedSchedulerLock sl{kernel}; if (m_current_request) { // Choose the current request if we have one. @@ -1404,7 +1398,7 @@ void KServerSession::CleanupRequests() { // Close a reference to the request once it's cleaned up. SCOPE_EXIT { - request->Close(); + request->Close(kernel); }; // Extract relevant information from the request. @@ -1434,20 +1428,20 @@ void KServerSession::CleanupRequests() { client_page_table->UnlockForIpcUserBuffer(client_message, client_buffer_size); // Signal the event. - event->Signal(); + event->Signal(kernel); } else { // End the client thread's wait. - KScopedSchedulerLock sl{m_kernel}; + KScopedSchedulerLock sl{kernel}; if (!client_thread->IsTerminationRequested()) { - client_thread->EndWait(ResultSessionClosed); + client_thread->EndWait(kernel, ResultSessionClosed); } } } } } -void KServerSession::OnClientClosed() { +void KServerSession::OnClientClosed(KernelCore& kernel) { KScopedLightLock lk{m_lock}; // Handle any pending requests. @@ -1462,12 +1456,12 @@ void KServerSession::OnClientClosed() { // Get the next request. { - KScopedSchedulerLock sl{m_kernel}; + KScopedSchedulerLock sl{kernel}; if (m_current_request != nullptr && m_current_request != prev_request) { // Set the request, open a reference as we process it. request = m_current_request; - request->Open(); + request->Open(kernel); cur_request = true; // Get thread and event for the request. @@ -1503,14 +1497,14 @@ void KServerSession::OnClientClosed() { // Ensure that we close the request when done. SCOPE_EXIT { - request->Close(); + request->Close(kernel); }; // If we're terminating, close a reference to the thread and event. if (terminate) { - thread->Close(); + thread->Close(kernel); if (event != nullptr) { - event->Close(); + event->Close(kernel); } } @@ -1534,12 +1528,12 @@ void KServerSession::OnClientClosed() { client_pt.UnlockForIpcUserBuffer(request->GetAddress(), request->GetSize()); // Signal the event. - event->Signal(); + event->Signal(kernel); } } // Notify. - this->NotifyAvailable(ResultSessionClosed); + this->NotifyAvailable(kernel, ResultSessionClosed); } } // namespace Kernel diff --git a/src/core/hle/kernel/k_server_session.h b/src/core/hle/kernel/k_server_session.h index 2876c231b2..217e7dc7be 100644 --- a/src/core/hle/kernel/k_server_session.h +++ b/src/core/hle/kernel/k_server_session.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -36,7 +39,7 @@ public: explicit KServerSession(KernelCore& kernel); ~KServerSession() override; - void Destroy() override; + void Destroy(KernelCore& kernel) override; void Initialize(KSession* p) { m_parent = p; @@ -46,29 +49,29 @@ public: return m_parent; } - bool IsSignaled() const override; - void OnClientClosed(); + bool IsSignaled(KernelCore& kernel) const override; + void OnClientClosed(KernelCore& kernel); - Result OnRequest(KSessionRequest* request); - Result SendReply(uintptr_t server_message, uintptr_t server_buffer_size, + Result OnRequest(KernelCore& kernel, KSessionRequest* request); + Result SendReply(KernelCore& kernel, uintptr_t server_message, uintptr_t server_buffer_size, KPhysicalAddress server_message_paddr, bool is_hle = false); - Result ReceiveRequest(uintptr_t server_message, uintptr_t server_buffer_size, + Result ReceiveRequest(KernelCore& kernel, uintptr_t server_message, uintptr_t server_buffer_size, KPhysicalAddress server_message_paddr, std::shared_ptr* out_context = nullptr, std::weak_ptr manager = {}); - Result SendReplyHLE() { - R_RETURN(this->SendReply(0, 0, 0, true)); + Result SendReplyHLE(KernelCore& kernel) { + R_RETURN(this->SendReply(kernel, 0, 0, 0, true)); } - Result ReceiveRequestHLE(std::shared_ptr* out_context, + Result ReceiveRequestHLE(KernelCore& kernel, std::shared_ptr* out_context, std::weak_ptr manager) { - R_RETURN(this->ReceiveRequest(0, 0, 0, out_context, manager)); + R_RETURN(this->ReceiveRequest(kernel, 0, 0, 0, out_context, manager)); } private: /// Frees up waiting client sessions when this server session is about to die - void CleanupRequests(); + void CleanupRequests(KernelCore& kernel); /// KSession that owns this KServerSession KSession* m_parent{}; diff --git a/src/core/hle/kernel/k_session.cpp b/src/core/hle/kernel/k_session.cpp index 4a1f6027ee..58b0d5de67 100644 --- a/src/core/hle/kernel/k_session.cpp +++ b/src/core/hle/kernel/k_session.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -13,12 +16,12 @@ KSession::KSession(KernelCore& kernel) : KAutoObjectWithSlabHeapAndContainer{kernel}, m_server{kernel}, m_client{kernel} {} KSession::~KSession() = default; -void KSession::Initialize(KClientPort* client_port, uintptr_t name) { +void KSession::Initialize(KernelCore& kernel, KClientPort* client_port, uintptr_t name) { // Increment reference count. // Because reference count is one on creation, this will result // in a reference count of two. Thus, when both server and client are closed // this object will be destroyed. - this->Open(); + this->Open(kernel); // Create our sub sessions. KAutoObject::Create(std::addressof(m_server)); @@ -33,45 +36,45 @@ void KSession::Initialize(KClientPort* client_port, uintptr_t name) { m_name = name; // Set our owner process. - m_process = GetCurrentProcessPointer(m_kernel); - m_process->Open(); + m_process = GetCurrentProcessPointer(kernel); + m_process->Open(kernel); // Set our port. m_port = client_port; if (m_port != nullptr) { - m_port->Open(); + m_port->Open(kernel); } // Mark initialized. m_initialized = true; } -void KSession::Finalize() { +void KSession::Finalize(KernelCore& kernel) { if (m_port != nullptr) { - m_port->OnSessionFinalized(); - m_port->Close(); + m_port->OnSessionFinalized(kernel); + m_port->Close(kernel); } } -void KSession::OnServerClosed() { +void KSession::OnServerClosed(KernelCore& kernel) { if (this->GetState() == State::Normal) { this->SetState(State::ServerClosed); m_client.OnServerClosed(); } } -void KSession::OnClientClosed() { +void KSession::OnClientClosed(KernelCore& kernel) { if (this->GetState() == State::Normal) { - SetState(State::ClientClosed); - m_server.OnClientClosed(); + this->SetState(State::ClientClosed); + m_server.OnClientClosed(kernel); } } -void KSession::PostDestroy(uintptr_t arg) { +void KSession::PostDestroy(KernelCore& kernel, uintptr_t arg) { // Release the session count resource the owner process holds. KProcess* owner = reinterpret_cast(arg); - owner->GetResourceLimit()->Release(LimitableResource::SessionCountMax, 1); - owner->Close(); + owner->GetResourceLimit()->Release(kernel, LimitableResource::SessionCountMax, 1); + owner->Close(kernel); } } // namespace Kernel diff --git a/src/core/hle/kernel/k_session.h b/src/core/hle/kernel/k_session.h index 3f4dd5989f..0d77bc1fd4 100644 --- a/src/core/hle/kernel/k_session.h +++ b/src/core/hle/kernel/k_session.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -21,8 +24,8 @@ public: explicit KSession(KernelCore& kernel); ~KSession() override; - void Initialize(KClientPort* port, uintptr_t name); - void Finalize() override; + void Initialize(KernelCore& kernel, KClientPort* port, uintptr_t name); + void Finalize(KernelCore& kernel) override; bool IsInitialized() const override { return m_initialized; @@ -32,11 +35,9 @@ public: return reinterpret_cast(m_process); } - static void PostDestroy(uintptr_t arg); - - void OnServerClosed(); - - void OnClientClosed(); + static void PostDestroy(KernelCore& kernel, uintptr_t arg); + void OnServerClosed(KernelCore& kernel); + void OnClientClosed(KernelCore& kernel); bool IsServerClosed() const { return this->GetState() != State::Normal; @@ -46,8 +47,8 @@ public: return this->GetState() != State::Normal; } - Result OnRequest(KSessionRequest* request) { - R_RETURN(m_server.OnRequest(request)); + Result OnRequest(KernelCore& kernel, KSessionRequest* request) { + R_RETURN(m_server.OnRequest(kernel, request)); } KClientSession& GetClientSession() { diff --git a/src/core/hle/kernel/k_session_request.cpp b/src/core/hle/kernel/k_session_request.cpp index 9a69b4ffc1..25451a3922 100644 --- a/src/core/hle/kernel/k_session_request.cpp +++ b/src/core/hle/kernel/k_session_request.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -18,7 +21,7 @@ Result KSessionRequest::SessionMappings::PushMap(KProcessAddress client, KProces } else { // Allocate a page for the extra mappings. if (m_mappings == nullptr) { - KPageBuffer* page_buffer = KPageBuffer::Allocate(m_kernel); + KPageBuffer* page_buffer = KPageBuffer::Allocate(kernel); R_UNLESS(page_buffer != nullptr, ResultOutOfMemory); m_mappings = reinterpret_cast(page_buffer); @@ -54,7 +57,7 @@ Result KSessionRequest::SessionMappings::PushExchange(KProcessAddress client, void KSessionRequest::SessionMappings::Finalize() { if (m_mappings) { - KPageBuffer::Free(m_kernel, reinterpret_cast(m_mappings)); + KPageBuffer::Free(kernel, reinterpret_cast(m_mappings)); m_mappings = nullptr; } } diff --git a/src/core/hle/kernel/k_session_request.h b/src/core/hle/kernel/k_session_request.h index 283669e0ac..6665eca56f 100644 --- a/src/core/hle/kernel/k_session_request.h +++ b/src/core/hle/kernel/k_session_request.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -56,7 +59,7 @@ public: }; public: - explicit SessionMappings(KernelCore& kernel) : m_kernel(kernel) {} + explicit SessionMappings(KernelCore& kernel_) : kernel(kernel_) {} void Initialize() {} void Finalize(); @@ -155,7 +158,7 @@ public: } private: - KernelCore& m_kernel; + KernelCore& kernel; std::array m_static_mappings{}; Mapping* m_mappings{}; u8 m_num_send{}; @@ -174,26 +177,26 @@ public: return req; } - void Destroy() override { - this->Finalize(); - KSessionRequest::Free(m_kernel, this); + void Destroy(KernelCore& kernel) override { + this->Finalize(kernel); + KSessionRequest::Free(kernel, this); } - void Initialize(KEvent* event, uintptr_t address, size_t size) { + void Initialize(KernelCore& kernel, KEvent* event, uintptr_t address, size_t size) { m_mappings.Initialize(); - m_thread = GetCurrentThreadPointer(m_kernel); + m_thread = GetCurrentThreadPointer(kernel); m_event = event; m_address = address; m_size = size; - m_thread->Open(); + m_thread->Open(kernel); if (m_event != nullptr) { - m_event->Open(); + m_event->Open(kernel); } } - static void PostDestroy(uintptr_t arg) {} + static void PostDestroy(KernelCore& kernel, uintptr_t arg) {} KThread* GetThread() const { return m_thread; @@ -211,9 +214,9 @@ public: return m_server; } - void SetServerProcess(KProcess* process) { + void SetServerProcess(KernelCore& kernel, KProcess* process) { m_server = process; - m_server->Open(); + m_server->Open(kernel); } void ClearThread() { @@ -289,17 +292,16 @@ public: private: // NOTE: This is public and virtual in Nintendo's kernel. - void Finalize() override { + void Finalize(KernelCore& kernel) override { m_mappings.Finalize(); - if (m_thread) { - m_thread->Close(); + m_thread->Close(kernel); } if (m_event) { - m_event->Close(); + m_event->Close(kernel); } if (m_server) { - m_server->Close(); + m_server->Close(kernel); } } diff --git a/src/core/hle/kernel/k_shared_memory.cpp b/src/core/hle/kernel/k_shared_memory.cpp index f713968f68..61f2872560 100644 --- a/src/core/hle/kernel/k_shared_memory.cpp +++ b/src/core/hle/kernel/k_shared_memory.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2014 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -15,7 +18,7 @@ namespace Kernel { KSharedMemory::KSharedMemory(KernelCore& kernel) : KAutoObjectWithSlabHeapAndContainer{kernel} {} KSharedMemory::~KSharedMemory() = default; -Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory, KProcess* owner_process, +Result KSharedMemory::Initialize(KernelCore& kernel, Core::DeviceMemory& device_memory, KProcess* owner_process, Svc::MemoryPermission owner_permission, Svc::MemoryPermission user_permission, std::size_t size) { // Set members. @@ -28,11 +31,10 @@ Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory, KProcess* ow const size_t num_pages = Common::DivideUp(size, PageSize); // Get the resource limit. - KResourceLimit* reslimit = m_kernel.GetSystemResourceLimit(); + KResourceLimit* reslimit = kernel.GetSystemResourceLimit(); // Reserve memory for ourselves. - KScopedResourceReservation memory_reservation(reslimit, LimitableResource::PhysicalMemoryMax, - size); + KScopedResourceReservation memory_reservation(kernel, reslimit, LimitableResource::PhysicalMemoryMax, size); R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); // Allocate the memory. @@ -40,12 +42,11 @@ Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory, KProcess* ow //! HACK: Open continuous mapping from sysmodule pool. auto option = KMemoryManager::EncodeOption(KMemoryManager::Pool::Secure, KMemoryManager::Direction::FromBack); - m_physical_address = m_kernel.MemoryManager().AllocateAndOpenContinuous(num_pages, 1, option); + m_physical_address = kernel.MemoryManager().AllocateAndOpenContinuous(num_pages, 1, option); R_UNLESS(m_physical_address != 0, ResultOutOfMemory); //! Insert the result into our page group. - m_page_group.emplace(m_kernel, - std::addressof(m_kernel.GetSystemSystemResource().GetBlockInfoManager())); + m_page_group.emplace(kernel, std::addressof(kernel.GetSystemSystemResource().GetBlockInfoManager())); m_page_group->AddBlock(m_physical_address, num_pages); // Commit our reservation. @@ -53,7 +54,7 @@ Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory, KProcess* ow // Set our resource limit. m_resource_limit = reslimit; - m_resource_limit->Open(); + m_resource_limit->Open(kernel); // Mark initialized. m_is_initialized = true; @@ -66,14 +67,14 @@ Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory, KProcess* ow R_SUCCEED(); } -void KSharedMemory::Finalize() { +void KSharedMemory::Finalize(KernelCore& kernel) { // Close and finalize the page group. - m_page_group->Close(); + m_page_group->Close(kernel); m_page_group->Finalize(); // Release the memory reservation. - m_resource_limit->Release(LimitableResource::PhysicalMemoryMax, m_size); - m_resource_limit->Close(); + m_resource_limit->Release(kernel, LimitableResource::PhysicalMemoryMax, m_size); + m_resource_limit->Close(kernel); } Result KSharedMemory::Map(KProcess& target_process, KProcessAddress address, std::size_t map_size, diff --git a/src/core/hle/kernel/k_shared_memory.h b/src/core/hle/kernel/k_shared_memory.h index 54b23d7ac2..3aede457c9 100644 --- a/src/core/hle/kernel/k_shared_memory.h +++ b/src/core/hle/kernel/k_shared_memory.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2014 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -26,7 +29,7 @@ public: explicit KSharedMemory(KernelCore& kernel); ~KSharedMemory() override; - Result Initialize(Core::DeviceMemory& device_memory_, KProcess* owner_process_, + Result Initialize(KernelCore& kernel, Core::DeviceMemory& device_memory_, KProcess* owner_process_, Svc::MemoryPermission owner_permission_, Svc::MemoryPermission user_permission_, std::size_t size_); @@ -66,12 +69,12 @@ public: return m_device_memory->GetPointer(m_physical_address + offset); } - void Finalize() override; + void Finalize(KernelCore& kernel) override; bool IsInitialized() const override { return m_is_initialized; } - static void PostDestroy(uintptr_t arg) {} + static void PostDestroy(KernelCore& kernel, uintptr_t arg) {} private: Core::DeviceMemory* m_device_memory{}; diff --git a/src/core/hle/kernel/k_synchronization_object.cpp b/src/core/hle/kernel/k_synchronization_object.cpp index 3e5b735b1e..221cab655f 100644 --- a/src/core/hle/kernel/k_synchronization_object.cpp +++ b/src/core/hle/kernel/k_synchronization_object.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -18,12 +21,10 @@ namespace { class ThreadQueueImplForKSynchronizationObjectWait final : public KThreadQueueWithoutEndWait { public: - ThreadQueueImplForKSynchronizationObjectWait(KernelCore& kernel, KSynchronizationObject** o, - KSynchronizationObject::ThreadListNode* n, s32 c) + ThreadQueueImplForKSynchronizationObjectWait(KernelCore& kernel, KSynchronizationObject** o, KSynchronizationObject::ThreadListNode* n, s32 c) : KThreadQueueWithoutEndWait(kernel), m_objects(o), m_nodes(n), m_count(c) {} - void NotifyAvailable(KThread* waiting_thread, KSynchronizationObject* signaled_object, - Result wait_result) override { + void NotifyAvailable(KernelCore& kernel, KThread* waiting_thread, KSynchronizationObject* signaled_object, Result wait_result) override { // Determine the sync index, and unlink all nodes. s32 sync_index = -1; for (auto i = 0; i < m_count; ++i) { @@ -43,10 +44,10 @@ public: waiting_thread->ClearCancellable(); // Invoke the base end wait handler. - KThreadQueue::EndWait(waiting_thread, wait_result); + KThreadQueue::EndWait(kernel, waiting_thread, wait_result); } - void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override { + void CancelWait(KernelCore& kernel, KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override { // Remove all nodes from our list. for (auto i = 0; i < m_count; ++i) { m_objects[i]->UnlinkNode(std::addressof(m_nodes[i])); @@ -56,7 +57,7 @@ public: waiting_thread->ClearCancellable(); // Invoke the base cancel wait handler. - KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task); + KThreadQueue::CancelWait(kernel, waiting_thread, wait_result, cancel_timer_task); } private: @@ -67,9 +68,9 @@ private: } // namespace -void KSynchronizationObject::Finalize() { +void KSynchronizationObject::Finalize(KernelCore& kernel) { this->OnFinalizeSynchronizationObject(); - KAutoObject::Finalize(); + KAutoObject::Finalize(kernel); } Result KSynchronizationObject::Wait(KernelCore& kernel, s32* out_index, @@ -98,7 +99,7 @@ Result KSynchronizationObject::Wait(KernelCore& kernel, s32* out_index, for (auto i = 0; i < num_objects; ++i) { ASSERT(objects[i] != nullptr); - if (objects[i]->IsSignaled()) { + if (objects[i]->IsSignaled(kernel)) { *out_index = i; slp.CancelSleep(); R_THROW(ResultSuccess); @@ -134,7 +135,7 @@ Result KSynchronizationObject::Wait(KernelCore& kernel, s32* out_index, // Wait for an object to be signaled. wait_queue.SetHardwareTimer(timer); - thread->BeginWait(std::addressof(wait_queue)); + thread->BeginWait(kernel, std::addressof(wait_queue)); thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Synchronization); } @@ -149,26 +150,26 @@ KSynchronizationObject::KSynchronizationObject(KernelCore& kernel) : KAutoObject KSynchronizationObject::~KSynchronizationObject() = default; -void KSynchronizationObject::NotifyAvailable(Result result) { - KScopedSchedulerLock sl(m_kernel); +void KSynchronizationObject::NotifyAvailable(KernelCore& kernel, Result result) { + KScopedSchedulerLock sl(kernel); // If we're not signaled, we've nothing to notify. - if (!this->IsSignaled()) { + if (!this->IsSignaled(kernel)) { return; } // Iterate over each thread. for (auto* cur_node = m_thread_list_head; cur_node != nullptr; cur_node = cur_node->next) { - cur_node->thread->NotifyAvailable(this, result); + cur_node->thread->NotifyAvailable(kernel, this, result); } } -std::vector KSynchronizationObject::GetWaitingThreadsForDebugging() const { +std::vector KSynchronizationObject::GetWaitingThreadsForDebugging(KernelCore& kernel) const { std::vector threads; // If debugging, dump the list of waiters. { - KScopedSchedulerLock lock(m_kernel); + KScopedSchedulerLock lock(kernel); for (auto* cur_node = m_thread_list_head; cur_node != nullptr; cur_node = cur_node->next) { threads.emplace_back(cur_node->thread); } diff --git a/src/core/hle/kernel/k_synchronization_object.h b/src/core/hle/kernel/k_synchronization_object.h index d55a2673df..4c54a4d2cb 100644 --- a/src/core/hle/kernel/k_synchronization_object.h +++ b/src/core/hle/kernel/k_synchronization_object.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -24,14 +27,13 @@ public: KThread* thread{}; }; - static Result Wait(KernelCore& kernel, s32* out_index, KSynchronizationObject** objects, - const s32 num_objects, s64 timeout); + static Result Wait(KernelCore& kernel, s32* out_index, KSynchronizationObject** objects, const s32 num_objects, s64 timeout); - void Finalize() override; + void Finalize(KernelCore& kernel) override; - virtual bool IsSignaled() const = 0; + virtual bool IsSignaled(KernelCore& kernel) const = 0; - std::vector GetWaitingThreadsForDebugging() const; + std::vector GetWaitingThreadsForDebugging(KernelCore& kernel) const; void LinkNode(ThreadListNode* node_) { // Link the node to the list. @@ -71,9 +73,9 @@ protected: virtual void OnFinalizeSynchronizationObject() {} - void NotifyAvailable(Result result); - void NotifyAvailable() { - return this->NotifyAvailable(ResultSuccess); + void NotifyAvailable(KernelCore& kernel, Result result); + void NotifyAvailable(KernelCore& kernel) { + return this->NotifyAvailable(kernel, ResultSuccess); } private: diff --git a/src/core/hle/kernel/k_system_resource.cpp b/src/core/hle/kernel/k_system_resource.cpp index b51941faf4..fb664eb57a 100644 --- a/src/core/hle/kernel/k_system_resource.cpp +++ b/src/core/hle/kernel/k_system_resource.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -7,8 +10,7 @@ namespace Kernel { -Result KSecureSystemResource::Initialize(size_t size, KResourceLimit* resource_limit, - KMemoryManager::Pool pool) { +Result KSecureSystemResource::Initialize(KernelCore& kernel, size_t size, KResourceLimit* resource_limit, KMemoryManager::Pool pool) { // Set members. m_resource_limit = resource_limit; m_resource_size = size; @@ -18,18 +20,17 @@ Result KSecureSystemResource::Initialize(size_t size, KResourceLimit* resource_l const size_t secure_size = this->CalculateRequiredSecureMemorySize(); // Reserve memory for our secure resource. - KScopedResourceReservation memory_reservation( - m_resource_limit, Svc::LimitableResource::PhysicalMemoryMax, secure_size); + KScopedResourceReservation memory_reservation(kernel, m_resource_limit, Svc::LimitableResource::PhysicalMemoryMax, secure_size); R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); // Allocate secure memory. - R_TRY(KSystemControl::AllocateSecureMemory(m_kernel, std::addressof(m_resource_address), + R_TRY(KSystemControl::AllocateSecureMemory(kernel, std::addressof(m_resource_address), m_resource_size, static_cast(m_resource_pool))); ASSERT(m_resource_address != 0); // Ensure we clean up the secure memory, if we fail past this point. ON_RESULT_FAILURE { - KSystemControl::FreeSecureMemory(m_kernel, m_resource_address, m_resource_size, + KSystemControl::FreeSecureMemory(kernel, m_resource_address, m_resource_size, static_cast(m_resource_pool)); }; @@ -40,9 +41,9 @@ Result KSecureSystemResource::Initialize(size_t size, KResourceLimit* resource_l // Get resource pointer. KPhysicalAddress resource_paddr = - KPageTable::GetHeapPhysicalAddress(m_kernel, m_resource_address); + KPageTable::GetHeapPhysicalAddress(kernel, m_resource_address); auto* resource = - m_kernel.System().DeviceMemory().GetPointer(resource_paddr); + kernel.System().DeviceMemory().GetPointer(resource_paddr); // Initialize slab heaps. m_dynamic_page_manager.Initialize(m_resource_address + rc_size, m_resource_size - rc_size, @@ -66,7 +67,7 @@ Result KSecureSystemResource::Initialize(size_t size, KResourceLimit* resource_l memory_reservation.Commit(); // Open reference to our resource limit. - m_resource_limit->Open(); + m_resource_limit->Open(kernel); // Set ourselves as initialized. m_is_initialized = true; @@ -74,26 +75,25 @@ Result KSecureSystemResource::Initialize(size_t size, KResourceLimit* resource_l R_SUCCEED(); } -void KSecureSystemResource::Finalize() { +void KSecureSystemResource::Finalize(KernelCore& kernel) { // Check that we have no outstanding allocations. ASSERT(m_memory_block_slab_manager.GetUsed() == 0); ASSERT(m_block_info_manager.GetUsed() == 0); ASSERT(m_page_table_manager.GetUsed() == 0); // Free our secure memory. - KSystemControl::FreeSecureMemory(m_kernel, m_resource_address, m_resource_size, + KSystemControl::FreeSecureMemory(kernel, m_resource_address, m_resource_size, static_cast(m_resource_pool)); // Release the memory reservation. - m_resource_limit->Release(Svc::LimitableResource::PhysicalMemoryMax, + m_resource_limit->Release(kernel, Svc::LimitableResource::PhysicalMemoryMax, this->CalculateRequiredSecureMemorySize()); // Close reference to our resource limit. - m_resource_limit->Close(); + m_resource_limit->Close(kernel); } -size_t KSecureSystemResource::CalculateRequiredSecureMemorySize(size_t size, - KMemoryManager::Pool pool) { +size_t KSecureSystemResource::CalculateRequiredSecureMemorySize(size_t size, KMemoryManager::Pool pool) { return KSystemControl::CalculateRequiredSecureMemorySize(size, static_cast(pool)); } diff --git a/src/core/hle/kernel/k_system_resource.h b/src/core/hle/kernel/k_system_resource.h index 6ea4821858..c0362497f9 100644 --- a/src/core/hle/kernel/k_system_resource.h +++ b/src/core/hle/kernel/k_system_resource.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -29,7 +32,7 @@ protected: } public: - virtual void Destroy() override { + virtual void Destroy(KernelCore& kernel) override { UNREACHABLE_MSG("KSystemResource::Destroy() was called"); } @@ -93,13 +96,13 @@ public: this->SetSecureResource(); } - Result Initialize(size_t size, KResourceLimit* resource_limit, KMemoryManager::Pool pool); - void Finalize(); + Result Initialize(KernelCore& kernel, size_t size, KResourceLimit* resource_limit, KMemoryManager::Pool pool); + void Finalize(KernelCore& kernel); bool IsInitialized() const { return m_is_initialized; } - static void PostDestroy(uintptr_t arg) {} + static void PostDestroy(KernelCore& kernel, uintptr_t arg) {} size_t CalculateRequiredSecureMemorySize() const { return CalculateRequiredSecureMemorySize(m_resource_size, m_resource_pool); diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp index 0af315d726..b499bd4073 100644 --- a/src/core/hle/kernel/k_thread.cpp +++ b/src/core/hle/kernel/k_thread.cpp @@ -95,12 +95,12 @@ namespace Kernel { explicit ThreadQueueImplForKThreadSetProperty(KernelCore& kernel, KThread::WaiterList* wl) : KThreadQueue(kernel), m_wait_list(wl) {} - void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override { + void CancelWait(KernelCore& kernel, KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override { // Remove the thread from the wait list. m_wait_list->erase(m_wait_list->iterator_to(*waiting_thread)); // Invoke the base cancel wait handler. - KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task); + KThreadQueue::CancelWait(kernel, waiting_thread, wait_result, cancel_timer_task); } private: @@ -113,7 +113,7 @@ namespace Kernel { : KAutoObjectWithSlabHeapAndContainer{kernel}, m_activity_pause_lock{kernel} {} KThread::~KThread() = default; - Result KThread::Initialize(KThreadFunction func, uintptr_t arg, KProcessAddress user_stack_top, + Result KThread::Initialize(KernelCore& kernel, KThreadFunction func, uintptr_t arg, KProcessAddress user_stack_top, s32 prio, s32 virt_core, KProcess* owner, ThreadType type) { // Assert parameters are valid. ASSERT((type == ThreadType::Main) || (type == ThreadType::Dummy) || @@ -225,12 +225,12 @@ namespace Kernel { if (owner != nullptr) { // Setup the TLS, if needed. if (type == ThreadType::User) { - R_TRY(owner->CreateThreadLocalRegion(std::addressof(m_tls_address))); + R_TRY(owner->CreateThreadLocalRegion(kernel, std::addressof(m_tls_address))); owner->GetMemory().ZeroBlock(m_tls_address, Svc::ThreadLocalRegionSize); } m_parent = owner; - m_parent->Open(); + m_parent->Open(kernel); } // Initialize thread context. @@ -247,28 +247,28 @@ namespace Kernel { this->SetInExceptionHandler(); // Set thread ID. - m_thread_id = m_kernel.CreateNewThreadID(); + m_thread_id = kernel.CreateNewThreadID(); // We initialized! m_initialized = true; // Register ourselves with our parent process. if (m_parent != nullptr) { - m_parent->RegisterThread(this); + m_parent->RegisterThread(kernel, this); if (m_parent->IsSuspended()) { - RequestSuspend(SuspendType::Process); + RequestSuspend(kernel, SuspendType::Process); } } R_SUCCEED(); } - Result KThread::InitializeThread(KThread* thread, KThreadFunction func, uintptr_t arg, + Result KThread::InitializeThread(KernelCore& kernel, KThread* thread, KThreadFunction func, uintptr_t arg, KProcessAddress user_stack_top, s32 prio, s32 core, KProcess* owner, ThreadType type, std::function&& init_func) { // Initialize the thread. - R_TRY(thread->Initialize(func, arg, user_stack_top, prio, core, owner, type)); + R_TRY(thread->Initialize(kernel, func, arg, user_stack_top, prio, core, owner, type)); // Initialize emulation parameters. thread->m_host_context = std::make_shared(std::move(init_func)); @@ -276,9 +276,9 @@ namespace Kernel { R_SUCCEED(); } - Result KThread::InitializeDummyThread(KThread* thread, KProcess* owner) { + Result KThread::InitializeDummyThread(Core::System& system, KThread* thread, KProcess* owner) { // Initialize the thread. - R_TRY(thread->Initialize({}, {}, {}, DummyThreadPriority, 3, owner, ThreadType::Dummy)); + R_TRY(thread->Initialize(system.Kernel(), {}, {}, {}, DummyThreadPriority, 3, owner, ThreadType::Dummy)); // Initialize emulation parameters. thread->m_stack_parameters.disable_count = 0; @@ -287,37 +287,27 @@ namespace Kernel { } Result KThread::InitializeMainThread(Core::System& system, KThread* thread, s32 virt_core) { - R_RETURN(InitializeThread(thread, {}, {}, {}, IdleThreadPriority, virt_core, {}, - ThreadType::Main, system.GetCpuManager().GetGuestActivateFunc())); + R_RETURN(InitializeThread(system.Kernel(), thread, {}, {}, {}, IdleThreadPriority, virt_core, {}, ThreadType::Main, system.GetCpuManager().GetGuestActivateFunc(system.Kernel()))); } Result KThread::InitializeIdleThread(Core::System& system, KThread* thread, s32 virt_core) { - R_RETURN(InitializeThread(thread, {}, {}, {}, IdleThreadPriority, virt_core, {}, - ThreadType::Main, system.GetCpuManager().GetIdleThreadStartFunc())); + R_RETURN(InitializeThread(system.Kernel(), thread, {}, {}, {}, IdleThreadPriority, virt_core, {}, ThreadType::Main, system.GetCpuManager().GetIdleThreadStartFunc(system.Kernel()))); } - Result KThread::InitializeHighPriorityThread(Core::System& system, KThread* thread, - KThreadFunction func, uintptr_t arg, s32 virt_core) { - R_RETURN(InitializeThread(thread, func, arg, {}, {}, virt_core, nullptr, - ThreadType::HighPriority, - system.GetCpuManager().GetShutdownThreadStartFunc())); + Result KThread::InitializeHighPriorityThread(Core::System& system, KThread* thread, KThreadFunction func, uintptr_t arg, s32 virt_core) { + R_RETURN(InitializeThread(system.Kernel(), thread, func, arg, {}, {}, virt_core, nullptr, ThreadType::HighPriority, system.GetCpuManager().GetShutdownThreadStartFunc(system.Kernel()))); } - Result KThread::InitializeUserThread(Core::System& system, KThread* thread, KThreadFunction func, - uintptr_t arg, KProcessAddress user_stack_top, s32 prio, - s32 virt_core, KProcess* owner) { + Result KThread::InitializeUserThread(Core::System& system, KThread* thread, KThreadFunction func, uintptr_t arg, KProcessAddress user_stack_top, s32 prio, s32 virt_core, KProcess* owner) { system.Kernel().GlobalSchedulerContext().AddThread(thread); - R_RETURN(InitializeThread(thread, func, arg, user_stack_top, prio, virt_core, owner, - ThreadType::User, system.GetCpuManager().GetGuestThreadFunc())); + R_RETURN(InitializeThread(system.Kernel(), thread, func, arg, user_stack_top, prio, virt_core, owner, ThreadType::User, system.GetCpuManager().GetGuestThreadFunc(system.Kernel()))); } - Result KThread::InitializeServiceThread(Core::System& system, KThread* thread, - std::function&& func, s32 prio, s32 virt_core, - KProcess* owner) { + Result KThread::InitializeServiceThread(Core::System& system, KThread* thread, std::function&& func, s32 prio, s32 virt_core, KProcess* owner) { system.Kernel().GlobalSchedulerContext().AddThread(thread); std::function func2{[&system, func_{std::move(func)}] { // Similar to UserModeThreadStarter. - system.Kernel().CurrentScheduler()->OnThreadStart(); + system.Kernel().CurrentScheduler()->OnThreadStart(system.Kernel()); // Run the guest function. func_(); @@ -326,35 +316,35 @@ namespace Kernel { Svc::ExitThread(system); }}; - R_RETURN(InitializeThread(thread, {}, {}, {}, prio, virt_core, owner, ThreadType::HighPriority, + R_RETURN(InitializeThread(system.Kernel(), thread, {}, {}, {}, prio, virt_core, owner, ThreadType::HighPriority, std::move(func2))); } - void KThread::PostDestroy(uintptr_t arg) { + void KThread::PostDestroy(KernelCore& kernel, uintptr_t arg) { KProcess* owner = reinterpret_cast(arg & ~1ULL); const bool resource_limit_release_hint = (arg & 1); const s64 hint_value = (resource_limit_release_hint ? 0 : 1); if (owner != nullptr) { - owner->GetResourceLimit()->Release(LimitableResource::ThreadCountMax, 1, hint_value); - owner->Close(); + owner->GetResourceLimit()->Release(kernel, LimitableResource::ThreadCountMax, 1, hint_value); + owner->Close(kernel); } } - void KThread::Finalize() { + void KThread::Finalize(KernelCore& kernel) { // If the thread has an owner process, unregister it. if (m_parent != nullptr) { - m_parent->UnregisterThread(this); + m_parent->UnregisterThread(kernel, this); } // If the thread has a local region, delete it. if (m_tls_address != 0) { - ASSERT(m_parent->DeleteThreadLocalRegion(m_tls_address).IsSuccess()); + ASSERT(m_parent->DeleteThreadLocalRegion(kernel, m_tls_address).IsSuccess()); } // Release any waiters. { ASSERT(m_waiting_lock_info == nullptr); - KScopedSchedulerLock sl{m_kernel}; + KScopedSchedulerLock sl{kernel}; // Check that we have no kernel waiters. ASSERT(m_num_kernel_waiters == 0); @@ -378,14 +368,14 @@ namespace Kernel { } // Cancel the thread's wait. - waiter->CancelWait(ResultInvalidState, true); + waiter->CancelWait(kernel, ResultInvalidState, true); } // Remove the held lock from our list. it = m_held_lock_info_list.erase(it); // Free the lock info. - LockWithPriorityInheritanceInfo::Free(m_kernel, lock_info); + LockWithPriorityInheritanceInfo::Free(kernel, lock_info); } } @@ -393,35 +383,35 @@ namespace Kernel { m_host_context.reset(); // Perform inherited finalization. - KSynchronizationObject::Finalize(); + KSynchronizationObject::Finalize(kernel); } - bool KThread::IsSignaled() const { + bool KThread::IsSignaled(KernelCore& kernel) const { return m_signaled; } - void KThread::OnTimer() { - ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); + void KThread::OnTimer(KernelCore& kernel) { + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel)); // If we're waiting, cancel the wait. if (this->GetState() == ThreadState::Waiting) { - m_wait_queue->CancelWait(this, ResultTimedOut, false); + m_wait_queue->CancelWait(kernel, this, ResultTimedOut, false); } } - void KThread::StartTermination() { - ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); + void KThread::StartTermination(KernelCore& kernel) { + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel)); // Release user exception and unpin, if relevant. if (m_parent != nullptr) { - m_parent->ReleaseUserException(this); - if (m_parent->GetPinnedThread(GetCurrentCoreId(m_kernel)) == this) { - m_parent->UnpinCurrentThread(); + m_parent->ReleaseUserException(kernel, this); + if (m_parent->GetPinnedThread(GetCurrentCoreId(kernel)) == this) { + m_parent->UnpinCurrentThread(kernel); } } // Set state to terminated. - this->SetState(ThreadState::Terminated); + this->SetState(kernel, ThreadState::Terminated); // Clear the thread's status as running in parent. if (m_parent != nullptr) { @@ -429,41 +419,41 @@ namespace Kernel { } // Clear previous thread in KScheduler. - KScheduler::ClearPreviousThread(m_kernel, this); + KScheduler::ClearPreviousThread(kernel, this); // Register terminated dpc flag. this->RegisterDpc(DpcFlag::Terminated); } - void KThread::FinishTermination() { + void KThread::FinishTermination(KernelCore& kernel) { // Ensure that the thread is not executing on any core. if (m_parent != nullptr) { - for (std::size_t i = 0; i < static_cast(Core::Hardware::NUM_CPU_CORES); ++i) { + for (std::size_t i = 0; i < std::size_t(Core::Hardware::NUM_CPU_CORES); ++i) { KThread* core_thread{}; do { - core_thread = m_kernel.Scheduler(i).GetSchedulerCurrentThread(); + core_thread = kernel.Scheduler(i).GetSchedulerCurrentThread(); } while (core_thread == this); } } // Acquire the scheduler lock. - KScopedSchedulerLock sl{m_kernel}; + KScopedSchedulerLock sl{kernel}; // Signal. m_signaled = true; - KSynchronizationObject::NotifyAvailable(); + KSynchronizationObject::NotifyAvailable(kernel); // Close the thread. - this->Close(); + this->Close(kernel); } - void KThread::DoWorkerTaskImpl() { + void KThread::DoWorkerTaskImpl(KernelCore& kernel) { // Finish the termination that was begun by Exit(). - this->FinishTermination(); + this->FinishTermination(kernel); } - void KThread::Pin(s32 current_core) { - ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); + void KThread::Pin(KernelCore& kernel, s32 current_core) { + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel)); // Set ourselves as pinned. GetStackParameters().is_pinned = true; @@ -487,7 +477,7 @@ namespace Kernel { if (active_core != current_core || m_physical_affinity_mask.GetAffinityMask() != m_original_physical_affinity_mask.GetAffinityMask()) { - KScheduler::OnThreadAffinityMaskChanged(m_kernel, this, + KScheduler::OnThreadAffinityMaskChanged(kernel, this, m_original_physical_affinity_mask, active_core); } } @@ -499,15 +489,15 @@ namespace Kernel { static_cast(ThreadState::SuspendShift))); // Update our state. - this->UpdateState(); + this->UpdateState(kernel); } // TODO(bunnei): Update our SVC access permissions. ASSERT(m_parent != nullptr); } - void KThread::Unpin() { - ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); + void KThread::Unpin(KernelCore& kernel) { + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel)); // Set ourselves as unpinned. this->GetStackParameters().is_pinned = false; @@ -535,7 +525,7 @@ namespace Kernel { std::countl_zero(m_physical_affinity_mask.GetAffinityMask()))); } } - KScheduler::OnThreadAffinityMaskChanged(m_kernel, this, old_mask, active_core); + KScheduler::OnThreadAffinityMaskChanged(kernel, this, old_mask, active_core); } } @@ -546,20 +536,19 @@ namespace Kernel { static_cast(ThreadState::SuspendShift))); // Update our state. - this->UpdateState(); + this->UpdateState(kernel); } // TODO(bunnei): Update our SVC access permissions. ASSERT(m_parent != nullptr); // Resume any threads that began waiting on us while we were pinned. - for (auto it = m_pinned_waiter_list.begin(); it != m_pinned_waiter_list.end(); - it = m_pinned_waiter_list.erase(it)) { - it->EndWait(ResultSuccess); - } + for (auto it = m_pinned_waiter_list.begin(); it != m_pinned_waiter_list.end(); it = m_pinned_waiter_list.erase(it)) { + it->EndWait(kernel, ResultSuccess); + } } - u16 KThread::GetUserDisableCount() const { + u16 KThread::GetUserDisableCount(KernelCore& kernel) const { if (!this->IsUserThread()) { // We only emulate TLS for user threads return {}; @@ -569,7 +558,7 @@ namespace Kernel { return memory.Read16(m_tls_address + offsetof(ThreadLocalRegion, disable_count)); } - void KThread::SetInterruptFlag() { + void KThread::SetInterruptFlag(KernelCore& kernel) { if (!this->IsUserThread()) { // We only emulate TLS for user threads return; @@ -579,7 +568,7 @@ namespace Kernel { memory.Write16(m_tls_address + offsetof(ThreadLocalRegion, interrupt_flag), 1); } - void KThread::ClearInterruptFlag() { + void KThread::ClearInterruptFlag(KernelCore& kernel) { if (!this->IsUserThread()) { // We only emulate TLS for user threads return; @@ -589,7 +578,7 @@ namespace Kernel { memory.Write16(m_tls_address + offsetof(ThreadLocalRegion, interrupt_flag), 0); } - void KThread::UpdateTlsThreadCpuTime(s64 switch_tick) { + void KThread::UpdateTlsThreadCpuTime(KernelCore& kernel, s64 switch_tick) { if (!this->IsUserThread()) { return; } @@ -602,8 +591,8 @@ namespace Kernel { memory.Write64(m_tls_address + offsetof(ThreadLocalRegion, thread_cpu_time), static_cast(value)); } - Result KThread::GetCoreMask(s32* out_ideal_core, u64* out_affinity_mask) { - KScopedSchedulerLock sl{m_kernel}; + Result KThread::GetCoreMask(KernelCore& kernel, s32* out_ideal_core, u64* out_affinity_mask) { + KScopedSchedulerLock sl{kernel}; // Get the virtual mask. *out_ideal_core = m_virtual_ideal_core_id; @@ -612,8 +601,8 @@ namespace Kernel { R_SUCCEED(); } - Result KThread::GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_mask) { - KScopedSchedulerLock sl{m_kernel}; + Result KThread::GetPhysicalCoreMask(KernelCore& kernel, s32* out_ideal_core, u64* out_affinity_mask) { + KScopedSchedulerLock sl{kernel}; ASSERT(m_num_core_migration_disables >= 0); // Select between core mask and original core mask. @@ -628,7 +617,7 @@ namespace Kernel { R_SUCCEED(); } - Result KThread::SetCoreMask(s32 core_id, u64 v_affinity_mask) { + Result KThread::SetCoreMask(KernelCore& kernel, s32 core_id, u64 v_affinity_mask) { ASSERT(m_parent != nullptr); ASSERT(v_affinity_mask != 0); KScopedLightLock lk(m_activity_pause_lock); @@ -636,7 +625,7 @@ namespace Kernel { // Set the core mask. u64 p_affinity_mask = 0; { - KScopedSchedulerLock sl(m_kernel); + KScopedSchedulerLock sl(kernel); ASSERT(m_num_core_migration_disables >= 0); // If we're updating, set our ideal virtual core. @@ -682,7 +671,7 @@ namespace Kernel { std::countl_zero(m_physical_affinity_mask.GetAffinityMask())); SetActiveCore(new_core); } - KScheduler::OnThreadAffinityMaskChanged(m_kernel, this, old_mask, active_core); + KScheduler::OnThreadAffinityMaskChanged(kernel, this, old_mask, active_core); } } else { // Otherwise, we edit the original affinity for restoration later. @@ -692,12 +681,12 @@ namespace Kernel { } // Update the pinned waiter list. - ThreadQueueImplForKThreadSetProperty wait_queue(m_kernel, std::addressof(m_pinned_waiter_list)); + ThreadQueueImplForKThreadSetProperty wait_queue(kernel, std::addressof(m_pinned_waiter_list)); { bool retry_update{}; do { // Lock the scheduler. - KScopedSchedulerLock sl(m_kernel); + KScopedSchedulerLock sl(kernel); // Don't do any further management if our termination has been requested. R_SUCCEED_IF(this->IsTerminationRequested()); @@ -710,7 +699,7 @@ namespace Kernel { s32 thread_core; for (thread_core = 0; thread_core < static_cast(Core::Hardware::NUM_CPU_CORES); ++thread_core) { - if (m_kernel.Scheduler(thread_core).GetSchedulerCurrentThread() == this) { + if (kernel.Scheduler(thread_core).GetSchedulerCurrentThread() == this) { thread_is_current = true; break; } @@ -722,12 +711,12 @@ namespace Kernel { // If the thread is pinned, we want to wait until it's not pinned. if (this->GetStackParameters().is_pinned) { // Verify that the current thread isn't terminating. - R_UNLESS(!GetCurrentThread(m_kernel).IsTerminationRequested(), + R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(), ResultTerminationRequested); // Wait until the thread isn't pinned any more. - m_pinned_waiter_list.push_back(GetCurrentThread(m_kernel)); - GetCurrentThread(m_kernel).BeginWait(std::addressof(wait_queue)); + m_pinned_waiter_list.push_back(GetCurrentThread(kernel)); + GetCurrentThread(kernel).BeginWait(kernel, std::addressof(wait_queue)); } else { // If the thread isn't pinned, release the scheduler lock and retry until it's // not current. @@ -740,25 +729,25 @@ namespace Kernel { R_SUCCEED(); } - void KThread::SetBasePriority(s32 value) { + void KThread::SetBasePriority(KernelCore& kernel, s32 value) { ASSERT(Svc::HighestThreadPriority <= value && value <= Svc::LowestThreadPriority); - KScopedSchedulerLock sl{m_kernel}; + KScopedSchedulerLock sl{kernel}; // Change our base priority. m_base_priority = value; // Perform a priority restoration. - RestorePriority(m_kernel, this); + RestorePriority(kernel, this); } - KThread* KThread::GetLockOwner() const { + KThread* KThread::GetLockOwner(KernelCore& kernel) const { return m_waiting_lock_info != nullptr ? m_waiting_lock_info->GetOwner() : nullptr; } - void KThread::IncreaseBasePriority(s32 priority) { + void KThread::IncreaseBasePriority(KernelCore& kernel, s32 priority) { ASSERT(Svc::HighestThreadPriority <= priority && priority <= Svc::LowestThreadPriority); - ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel)); ASSERT(!this->GetStackParameters().is_pinned); // Set our base priority. @@ -766,47 +755,47 @@ namespace Kernel { m_base_priority = priority; // Perform a priority restoration. - RestorePriority(m_kernel, this); + RestorePriority(kernel, this); } } - void KThread::RequestSuspend(SuspendType type) { - KScopedSchedulerLock sl{m_kernel}; + void KThread::RequestSuspend(KernelCore& kernel, SuspendType type) { + KScopedSchedulerLock sl{kernel}; // Note the request in our flags. m_suspend_request_flags |= (1U << (static_cast(ThreadState::SuspendShift) + static_cast(type))); // Try to perform the suspend. - this->TrySuspend(); + this->TrySuspend(kernel); } - void KThread::Resume(SuspendType type) { - KScopedSchedulerLock sl{m_kernel}; + void KThread::Resume(KernelCore& kernel, SuspendType type) { + KScopedSchedulerLock sl{kernel}; // Clear the request in our flags. m_suspend_request_flags &= ~(1U << (static_cast(ThreadState::SuspendShift) + static_cast(type))); // Update our state. - this->UpdateState(); + this->UpdateState(kernel); } - void KThread::WaitCancel() { - KScopedSchedulerLock sl{m_kernel}; + void KThread::WaitCancel(KernelCore& kernel) { + KScopedSchedulerLock sl{kernel}; // Check if we're waiting and cancellable. if (this->GetState() == ThreadState::Waiting && m_cancellable) { m_wait_cancelled = false; - m_wait_queue->CancelWait(this, ResultCancelled, true); + m_wait_queue->CancelWait(kernel, this, ResultCancelled, true); } else { // Otherwise, note that we cancelled a wait. m_wait_cancelled = true; } } - void KThread::TrySuspend() { - ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); + void KThread::TrySuspend(KernelCore& kernel) { + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel)); ASSERT(this->IsSuspendRequested()); // Ensure that we have no waiters. @@ -816,11 +805,11 @@ namespace Kernel { ASSERT(this->GetNumKernelWaiters() == 0); // Perform the suspend. - this->UpdateState(); + this->UpdateState(kernel); } - void KThread::UpdateState() { - ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); + void KThread::UpdateState(KernelCore& kernel) { + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel)); // Set our suspend flags in state. const ThreadState old_state = m_thread_state.load(std::memory_order_relaxed); @@ -830,37 +819,37 @@ namespace Kernel { // Note the state change in scheduler. if (new_state != old_state) { - KScheduler::OnThreadStateChanged(m_kernel, this, old_state); + KScheduler::OnThreadStateChanged(kernel, this, old_state); } } - void KThread::Continue() { - ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); + void KThread::Continue(KernelCore& kernel) { + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel)); // Clear our suspend flags in state. const ThreadState old_state = m_thread_state.load(std::memory_order_relaxed); m_thread_state.store(old_state & ThreadState::Mask, std::memory_order_relaxed); // Note the state change in scheduler. - KScheduler::OnThreadStateChanged(m_kernel, this, old_state); + KScheduler::OnThreadStateChanged(kernel, this, old_state); } - void KThread::CloneFpuStatus() { + void KThread::CloneFpuStatus(KernelCore& kernel) { // We shouldn't reach here when starting kernel threads. ASSERT(this->GetOwnerProcess() != nullptr); - ASSERT(this->GetOwnerProcess() == GetCurrentProcessPointer(m_kernel)); + ASSERT(this->GetOwnerProcess() == GetCurrentProcessPointer(kernel)); - m_kernel.CurrentPhysicalCore().CloneFpuStatus(this); + kernel.CurrentPhysicalCore().CloneFpuStatus(this); } - Result KThread::SetActivity(Svc::ThreadActivity activity) { + Result KThread::SetActivity(KernelCore& kernel, Svc::ThreadActivity activity) { // Lock ourselves. KScopedLightLock lk(m_activity_pause_lock); // Set the activity. { // Lock the scheduler. - KScopedSchedulerLock sl(m_kernel); + KScopedSchedulerLock sl(kernel); // Verify our state. const auto cur_state = this->GetState(); @@ -873,7 +862,7 @@ namespace Kernel { R_UNLESS(!this->IsSuspendRequested(SuspendType::Thread), ResultInvalidState); // Suspend. - this->RequestSuspend(SuspendType::Thread); + this->RequestSuspend(kernel, SuspendType::Thread); } else { ASSERT(activity == Svc::ThreadActivity::Runnable); @@ -881,19 +870,19 @@ namespace Kernel { R_UNLESS(this->IsSuspendRequested(SuspendType::Thread), ResultInvalidState); // Resume. - this->Resume(SuspendType::Thread); + this->Resume(kernel, SuspendType::Thread); } } // If the thread is now paused, update the pinned waiter list. if (activity == Svc::ThreadActivity::Paused) { - ThreadQueueImplForKThreadSetProperty wait_queue(m_kernel, + ThreadQueueImplForKThreadSetProperty wait_queue(kernel, std::addressof(m_pinned_waiter_list)); bool thread_is_current{}; do { // Lock the scheduler. - KScopedSchedulerLock sl(m_kernel); + KScopedSchedulerLock sl(kernel); // Don't do any further management if our termination has been requested. R_SUCCEED_IF(this->IsTerminationRequested()); @@ -904,17 +893,17 @@ namespace Kernel { // Check whether the thread is pinned. if (this->GetStackParameters().is_pinned) { // Verify that the current thread isn't terminating. - R_UNLESS(!GetCurrentThread(m_kernel).IsTerminationRequested(), + R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(), ResultTerminationRequested); // Wait until the thread isn't pinned any more. - m_pinned_waiter_list.push_back(GetCurrentThread(m_kernel)); - GetCurrentThread(m_kernel).BeginWait(std::addressof(wait_queue)); + m_pinned_waiter_list.push_back(GetCurrentThread(kernel)); + GetCurrentThread(kernel).BeginWait(kernel, std::addressof(wait_queue)); } else { // Check if the thread is currently running. // If it is, we'll need to retry. for (auto i = 0; i < static_cast(Core::Hardware::NUM_CPU_CORES); ++i) { - if (m_kernel.Scheduler(i).GetSchedulerCurrentThread() == this) { + if (kernel.Scheduler(i).GetSchedulerCurrentThread() == this) { thread_is_current = true; break; } @@ -926,14 +915,14 @@ namespace Kernel { R_SUCCEED(); } - Result KThread::GetThreadContext3(Svc::ThreadContext* out) { + Result KThread::GetThreadContext3(KernelCore& kernel, Svc::ThreadContext* out) { // Lock ourselves. KScopedLightLock lk{m_activity_pause_lock}; // Get the context. { // Lock the scheduler. - KScopedSchedulerLock sl{m_kernel}; + KScopedSchedulerLock sl{kernel}; // Verify that we're suspended. R_UNLESS(this->IsSuspendRequested(SuspendType::Thread), ResultInvalidState); @@ -957,8 +946,8 @@ namespace Kernel { R_SUCCEED(); } - void KThread::AddHeldLock(LockWithPriorityInheritanceInfo* lock_info) { - ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); + void KThread::AddHeldLock(KernelCore& kernel, LockWithPriorityInheritanceInfo* lock_info) { + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel)); // Set ourselves as the lock's owner. lock_info->SetOwner(this); @@ -967,23 +956,21 @@ namespace Kernel { m_held_lock_info_list.push_front(*lock_info); } - KThread::LockWithPriorityInheritanceInfo* KThread::FindHeldLock(KProcessAddress address_key, - bool is_kernel_address_key) { - ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); + KThread::LockWithPriorityInheritanceInfo* KThread::FindHeldLock(KernelCore& kernel, KProcessAddress address_key, bool is_kernel_address_key) { + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel)); // Try to find an existing held lock. for (auto& held_lock : m_held_lock_info_list) { - if (held_lock.GetAddressKey() == address_key && - held_lock.GetIsKernelAddressKey() == is_kernel_address_key) { + if (held_lock.GetAddressKey() == address_key && held_lock.GetIsKernelAddressKey() == is_kernel_address_key) { return std::addressof(held_lock); - } + } } return nullptr; } - void KThread::AddWaiterImpl(KThread* thread) { - ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); + void KThread::AddWaiterImpl(KernelCore& kernel, KThread* thread) { + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel)); ASSERT(thread->GetConditionVariableTree() == nullptr); // Get the thread's address key. @@ -993,31 +980,31 @@ namespace Kernel { // Keep track of how many kernel waiters we have. if (is_kernel_address_key) { ASSERT((m_num_kernel_waiters++) >= 0); - KScheduler::SetSchedulerUpdateNeeded(m_kernel); + KScheduler::SetSchedulerUpdateNeeded(kernel); } // Get the relevant lock info. - auto* lock_info = this->FindHeldLock(address_key, is_kernel_address_key); + auto* lock_info = this->FindHeldLock(kernel, address_key, is_kernel_address_key); if (lock_info == nullptr) { // Create a new lock for the address key. lock_info = - LockWithPriorityInheritanceInfo::Create(m_kernel, address_key, is_kernel_address_key); + LockWithPriorityInheritanceInfo::Create(kernel, address_key, is_kernel_address_key); // Add the new lock to our list. - this->AddHeldLock(lock_info); + this->AddHeldLock(kernel, lock_info); } // Add the thread as waiter to the lock info. lock_info->AddWaiter(thread); } - void KThread::RemoveWaiterImpl(KThread* thread) { - ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); + void KThread::RemoveWaiterImpl(KernelCore& kernel, KThread* thread) { + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel)); // Keep track of how many kernel waiters we have. if (thread->GetIsKernelAddressKey()) { ASSERT((m_num_kernel_waiters--) > 0); - KScheduler::SetSchedulerUpdateNeeded(m_kernel); + KScheduler::SetSchedulerUpdateNeeded(kernel); } // Get the info for the lock the thread is waiting on. @@ -1027,7 +1014,7 @@ namespace Kernel { // Remove the waiter. if (lock_info->RemoveWaiter(thread)) { m_held_lock_info_list.erase(m_held_lock_info_list.iterator_to(*lock_info)); - LockWithPriorityInheritanceInfo::Free(m_kernel, lock_info); + LockWithPriorityInheritanceInfo::Free(kernel, lock_info); } } @@ -1048,12 +1035,12 @@ namespace Kernel { } // Get the owner of whatever lock this thread is waiting on. - KThread* const lock_owner = thread->GetLockOwner(); + KThread* const lock_owner = thread->GetLockOwner(kernel); // If the thread is waiting on some lock, remove it as a waiter to prevent violating red // black tree invariants. if (lock_owner != nullptr) { - lock_owner->RemoveWaiterImpl(thread); + lock_owner->RemoveWaiterImpl(kernel, thread); } // Ensure we don't violate condition variable red black tree invariants. @@ -1072,7 +1059,7 @@ namespace Kernel { // If we removed the thread from some lock's waiting list, add it back. if (lock_owner != nullptr) { - lock_owner->AddWaiterImpl(thread); + lock_owner->AddWaiterImpl(kernel, thread); } // Update the scheduler. @@ -1083,32 +1070,30 @@ namespace Kernel { } } - void KThread::AddWaiter(KThread* thread) { - this->AddWaiterImpl(thread); + void KThread::AddWaiter(KernelCore& kernel, KThread* thread) { + this->AddWaiterImpl(kernel, thread); // If the thread has a higher priority than us, we should inherit. if (thread->GetPriority() < this->GetPriority()) { - RestorePriority(m_kernel, this); + RestorePriority(kernel, this); } } - void KThread::RemoveWaiter(KThread* thread) { - this->RemoveWaiterImpl(thread); + void KThread::RemoveWaiter(KernelCore& kernel, KThread* thread) { + this->RemoveWaiterImpl(kernel, thread); // If our priority is the same as the thread's (and we've inherited), we may need to restore to // lower priority. - if (this->GetPriority() == thread->GetPriority() && - this->GetPriority() < this->GetBasePriority()) { - RestorePriority(m_kernel, this); - } + if (this->GetPriority() == thread->GetPriority() && this->GetPriority() < this->GetBasePriority()) { + RestorePriority(kernel, this); + } } - KThread* KThread::RemoveWaiterByKey(bool* out_has_waiters, KProcessAddress key, - bool is_kernel_address_key_) { - ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); + KThread* KThread::RemoveWaiterByKey(KernelCore& kernel, bool* out_has_waiters, KProcessAddress key, bool is_kernel_address_key_) { + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel)); // Get the relevant lock info. - auto* lock_info = this->FindHeldLock(key, is_kernel_address_key_); + auto* lock_info = this->FindHeldLock(kernel, key, is_kernel_address_key_); if (lock_info == nullptr) { *out_has_waiters = false; return nullptr; @@ -1121,7 +1106,7 @@ namespace Kernel { if (lock_info->GetIsKernelAddressKey()) { m_num_kernel_waiters -= lock_info->GetWaiterCount(); ASSERT(m_num_kernel_waiters >= 0); - KScheduler::SetSchedulerUpdateNeeded(m_kernel); + KScheduler::SetSchedulerUpdateNeeded(kernel); } ASSERT(lock_info->GetWaiterCount() > 0); @@ -1133,13 +1118,13 @@ namespace Kernel { *out_has_waiters = false; // Free the lock info, since it has no waiters. - LockWithPriorityInheritanceInfo::Free(m_kernel, lock_info); + LockWithPriorityInheritanceInfo::Free(kernel, lock_info); } else { // There are additional waiters on the lock. *out_has_waiters = true; // Add the lock to the new owner's held list. - next_lock_owner->AddHeldLock(lock_info); + next_lock_owner->AddHeldLock(kernel, lock_info); // Keep track of any kernel waiters for the new owner. if (lock_info->GetIsKernelAddressKey()) { @@ -1155,7 +1140,7 @@ namespace Kernel { // to lower priority. if (this->GetPriority() == next_lock_owner->GetPriority() && this->GetPriority() < this->GetBasePriority()) { - RestorePriority(m_kernel, this); + RestorePriority(kernel, this); // NOTE: No need to restore priority on the next lock owner, because it was already the // highest priority waiter on the lock. } @@ -1164,91 +1149,91 @@ namespace Kernel { return next_lock_owner; } - Result KThread::Run() { + Result KThread::Run(KernelCore& kernel) { while (true) { - KScopedSchedulerLock lk{m_kernel}; + KScopedSchedulerLock lk{kernel}; // If either this thread or the current thread are requesting termination, note it. R_UNLESS(!this->IsTerminationRequested(), ResultTerminationRequested); - R_UNLESS(!GetCurrentThread(m_kernel).IsTerminationRequested(), ResultTerminationRequested); + R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(), ResultTerminationRequested); // Ensure our thread state is correct. R_UNLESS(this->GetState() == ThreadState::Initialized, ResultInvalidState); // If the current thread has been asked to suspend, suspend it and retry. - if (GetCurrentThread(m_kernel).IsSuspended()) { - GetCurrentThread(m_kernel).UpdateState(); + if (GetCurrentThread(kernel).IsSuspended()) { + GetCurrentThread(kernel).UpdateState(kernel); continue; } // If we're not a kernel thread and we've been asked to suspend, suspend ourselves. if (KProcess* owner = this->GetOwnerProcess(); owner != nullptr) { if (this->IsUserThread() && this->IsSuspended()) { - this->UpdateState(); + this->UpdateState(kernel); } - owner->IncrementRunningThreadCount(); + owner->IncrementRunningThreadCount(kernel); } // Open a reference, now that we're running. - this->Open(); + this->Open(kernel); // Set our state and finish. - this->SetState(ThreadState::Runnable); + this->SetState(kernel, ThreadState::Runnable); R_SUCCEED(); } } - void KThread::Exit() { - ASSERT(this == GetCurrentThreadPointer(m_kernel)); + void KThread::Exit(KernelCore& kernel) { + ASSERT(this == GetCurrentThreadPointer(kernel)); // Release the thread resource hint, running thread count from parent. if (m_parent != nullptr) { - m_parent->GetResourceLimit()->Release(Kernel::LimitableResource::ThreadCountMax, 0, 1); + m_parent->GetResourceLimit()->Release(kernel, Kernel::LimitableResource::ThreadCountMax, 0, 1); m_resource_limit_release_hint = true; - m_parent->DecrementRunningThreadCount(); + m_parent->DecrementRunningThreadCount(kernel); } // Perform termination. { - KScopedSchedulerLock sl{m_kernel}; + KScopedSchedulerLock sl{kernel}; // Disallow all suspension. m_suspend_allowed_flags = 0; - this->UpdateState(); + this->UpdateState(kernel); // Disallow all suspension. m_suspend_allowed_flags = 0; // Start termination. - this->StartTermination(); + this->StartTermination(kernel); // Register the thread as a work task. - KWorkerTaskManager::AddTask(m_kernel, KWorkerTaskManager::WorkerType::Exit, this); + KWorkerTaskManager::AddTask(kernel, KWorkerTaskManager::WorkerType::Exit, this); } UNREACHABLE_MSG("KThread::Exit() would return"); } - Result KThread::Terminate() { - ASSERT(this != GetCurrentThreadPointer(m_kernel)); + Result KThread::Terminate(KernelCore& kernel) { + ASSERT(this != GetCurrentThreadPointer(kernel)); // Request the thread terminate if it hasn't already. - if (const auto new_state = this->RequestTerminate(); new_state != ThreadState::Terminated) { + if (const auto new_state = this->RequestTerminate(kernel); new_state != ThreadState::Terminated) { // If the thread isn't terminated, wait for it to terminate. s32 index; KSynchronizationObject* objects[] = {this}; - R_TRY(KSynchronizationObject::Wait(m_kernel, std::addressof(index), objects, 1, + R_TRY(KSynchronizationObject::Wait(kernel, std::addressof(index), objects, 1, Svc::WaitInfinite)); } R_SUCCEED(); } - ThreadState KThread::RequestTerminate() { - ASSERT(this != GetCurrentThreadPointer(m_kernel)); + ThreadState KThread::RequestTerminate(KernelCore& kernel) { + ASSERT(this != GetCurrentThreadPointer(kernel)); - KScopedSchedulerLock sl{m_kernel}; + KScopedSchedulerLock sl{kernel}; // Determine if this is the first termination request. const bool first_request = [&]() -> bool { @@ -1270,46 +1255,46 @@ namespace Kernel { // If the thread is pinned, unpin it. if (this->GetStackParameters().is_pinned) { - this->GetOwnerProcess()->UnpinThread(this); + this->GetOwnerProcess()->UnpinThread(kernel, this); } // If the thread is suspended, continue it. if (this->IsSuspended()) { m_suspend_allowed_flags = 0; - this->UpdateState(); + this->UpdateState(kernel); } // Change the thread's priority to be higher than any system thread's. - this->IncreaseBasePriority(TerminatingThreadPriority); + this->IncreaseBasePriority(kernel, TerminatingThreadPriority); // If the thread is runnable, send a termination interrupt to cores it may be running on. if (this->GetState() == ThreadState::Runnable) { // NOTE: We do not mask the "current core", because this code may not actually be // executing from the thread representing the "current core". if (const u64 core_mask = m_physical_affinity_mask.GetAffinityMask(); core_mask != 0) { - Kernel::KInterruptManager::SendInterProcessorInterrupt(m_kernel, core_mask); + Kernel::KInterruptManager::SendInterProcessorInterrupt(kernel, core_mask); } } // Wake up the thread. if (this->GetState() == ThreadState::Waiting) { - m_wait_queue->CancelWait(this, ResultTerminationRequested, true); + m_wait_queue->CancelWait(kernel, this, ResultTerminationRequested, true); } } return this->GetState(); } - Result KThread::Sleep(s64 timeout) { - ASSERT(!KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); - ASSERT(this == GetCurrentThreadPointer(m_kernel)); + Result KThread::Sleep(KernelCore& kernel, s64 timeout) { + ASSERT(!KScheduler::IsSchedulerLockedByCurrentThread(kernel)); + ASSERT(this == GetCurrentThreadPointer(kernel)); ASSERT(timeout > 0); - ThreadQueueImplForKThreadSleep wait_queue(m_kernel); + ThreadQueueImplForKThreadSleep wait_queue(kernel); KHardwareTimer* timer{}; { // Setup the scheduling lock and sleep. - KScopedSchedulerLockAndSleep slp(m_kernel, std::addressof(timer), this, timeout); + KScopedSchedulerLockAndSleep slp(kernel, std::addressof(timer), this, timeout); // Check if the thread should terminate. if (this->IsTerminationRequested()) { @@ -1319,15 +1304,15 @@ namespace Kernel { // Wait for the sleep to end. wait_queue.SetHardwareTimer(timer); - this->BeginWait(std::addressof(wait_queue)); + this->BeginWait(kernel, std::addressof(wait_queue)); this->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Sleep); } R_SUCCEED(); } - void KThread::RequestDummyThreadWait() { - ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); + void KThread::RequestDummyThreadWait(KernelCore& kernel) { + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel)); ASSERT(this->IsDummyThread()); // We will block when the scheduler lock is released. @@ -1335,8 +1320,8 @@ namespace Kernel { m_dummy_thread_runnable = false; } - void KThread::DummyThreadBeginWait() { - if (!this->IsDummyThread() || m_kernel.IsPhantomModeForSingleCore()) { + void KThread::DummyThreadBeginWait(KernelCore& kernel) { + if (!this->IsDummyThread() || kernel.IsPhantomModeForSingleCore()) { // Occurs in single core mode. return; } @@ -1346,8 +1331,8 @@ namespace Kernel { m_dummy_thread_cv.wait(lock, [this] { return m_dummy_thread_runnable; }); } - void KThread::DummyThreadEndWait() { - ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); + void KThread::DummyThreadEndWait(KernelCore& kernel) { + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel)); ASSERT(this->IsDummyThread()); // Wake up the waiting thread. @@ -1358,28 +1343,26 @@ namespace Kernel { m_dummy_thread_cv.notify_one(); } - void KThread::BeginWait(KThreadQueue* queue) { + void KThread::BeginWait(KernelCore& kernel, KThreadQueue* queue) { // Set our state as waiting. - this->SetState(ThreadState::Waiting); + this->SetState(kernel, ThreadState::Waiting); // Set our wait queue. m_wait_queue = queue; } - void KThread::NotifyAvailable(KSynchronizationObject* signaled_object, Result wait_result) { + void KThread::NotifyAvailable(KernelCore& kernel, KSynchronizationObject* signaled_object, Result wait_result) { // Lock the scheduler. - KScopedSchedulerLock sl(m_kernel); - + KScopedSchedulerLock sl(kernel); // If we're waiting, notify our queue that we're available. if (this->GetState() == ThreadState::Waiting) { - m_wait_queue->NotifyAvailable(this, signaled_object, wait_result); + m_wait_queue->NotifyAvailable(kernel, this, signaled_object, wait_result); } } - void KThread::EndWait(Result wait_result) { + void KThread::EndWait(KernelCore& kernel, Result wait_result) { // Lock the scheduler. - KScopedSchedulerLock sl(m_kernel); - + KScopedSchedulerLock sl(kernel); // If we're waiting, notify our queue that we're available. if (this->GetState() == ThreadState::Waiting) { if (m_wait_queue == nullptr) { @@ -1388,32 +1371,29 @@ namespace Kernel { return; } - m_wait_queue->EndWait(this, wait_result); + m_wait_queue->EndWait(kernel, this, wait_result); } } - void KThread::CancelWait(Result wait_result, bool cancel_timer_task) { + void KThread::CancelWait(KernelCore& kernel, Result wait_result, bool cancel_timer_task) { // Lock the scheduler. - KScopedSchedulerLock sl(m_kernel); - + KScopedSchedulerLock sl(kernel); // If we're waiting, notify our queue that we're available. if (this->GetState() == ThreadState::Waiting) { - m_wait_queue->CancelWait(this, wait_result, cancel_timer_task); + m_wait_queue->CancelWait(kernel, this, wait_result, cancel_timer_task); } } - void KThread::SetState(ThreadState state) { - KScopedSchedulerLock sl{m_kernel}; - + void KThread::SetState(KernelCore& kernel, ThreadState state) { + KScopedSchedulerLock sl{kernel}; // Clear debugging state this->SetWaitReasonForDebugging({}); - const ThreadState old_state = m_thread_state.load(std::memory_order_relaxed); m_thread_state.store( static_cast((old_state & ~ThreadState::Mask) | (state & ThreadState::Mask)), std::memory_order_relaxed); if (m_thread_state.load(std::memory_order_relaxed) != old_state) { - KScheduler::OnThreadStateChanged(m_kernel, this, old_state); + KScheduler::OnThreadStateChanged(kernel, this, old_state); } } @@ -1459,12 +1439,12 @@ namespace Kernel { auto* scheduler = m_kernel.CurrentScheduler(); if (scheduler && !m_kernel.IsPhantomModeForSingleCore()) { - scheduler->RescheduleCurrentCore(); + scheduler->RescheduleCurrentCore(m_kernel); } else { KScheduler::RescheduleCurrentHLEThread(m_kernel); } } else { - GetCurrentThread(m_kernel).EnableDispatch(); + GetCurrentThread(m_kernel).EnableDispatch(m_kernel); } } } \ No newline at end of file diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index e79704ce4b..95ba1b4d76 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project @@ -181,21 +181,17 @@ public: return m_thread_id; } - void ContinueIfHasKernelWaiters() { + void ContinueIfHasKernelWaiters(KernelCore& kernel) { if (GetNumKernelWaiters() > 0) { - Continue(); + Continue(kernel); } } - void SetBasePriority(s32 value); - - Result Run(); - - void Exit(); - - Result Terminate(); - - ThreadState RequestTerminate(); + void SetBasePriority(KernelCore& kernel, s32 value); + Result Run(KernelCore& kernel); + void Exit(KernelCore& kernel); + Result Terminate(KernelCore& kernel); + ThreadState RequestTerminate(KernelCore& kernel); u32 GetSuspendFlags() const { return m_suspend_allowed_flags & m_suspend_request_flags; @@ -215,15 +211,11 @@ public: return m_suspend_request_flags != 0; } - void RequestSuspend(SuspendType type); - - void Resume(SuspendType type); - - void TrySuspend(); - - void UpdateState(); - - void Continue(); + void RequestSuspend(KernelCore& kernel, SuspendType type); + void Resume(KernelCore& kernel, SuspendType type); + void TrySuspend(KernelCore& kernel); + void UpdateState(KernelCore& kernel); + void Continue(KernelCore& kernel); constexpr void SetSyncedIndex(s32 index) { m_synced_index = index; @@ -262,7 +254,7 @@ public: m_thread_context.tpidr = value; } - void CloneFpuStatus(); + void CloneFpuStatus(KernelCore& kernel); Svc::ThreadContext& GetContext() { return m_thread_context; @@ -282,7 +274,7 @@ public: return m_thread_state.load(std::memory_order_relaxed); } - void SetState(ThreadState state); + void SetState(KernelCore& kernel, ThreadState state); StepState GetStepState() const { return m_step_state; @@ -342,27 +334,27 @@ public: Svc::ArgumentHandleCountMax}; } - u16 GetUserDisableCount() const; - void SetInterruptFlag(); - void ClearInterruptFlag(); + u16 GetUserDisableCount(KernelCore& kernel) const; + void SetInterruptFlag(KernelCore& kernel); + void ClearInterruptFlag(KernelCore& kernel); - void UpdateTlsThreadCpuTime(s64 switch_tick); + void UpdateTlsThreadCpuTime(KernelCore& kernel, s64 switch_tick); - KThread* GetLockOwner() const; + KThread* GetLockOwner(KernelCore& kernel) const; const KAffinityMask& GetAffinityMask() const { return m_physical_affinity_mask; } - Result GetCoreMask(s32* out_ideal_core, u64* out_affinity_mask); + Result GetCoreMask(KernelCore& kernel, s32* out_ideal_core, u64* out_affinity_mask); - Result GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_mask); + Result GetPhysicalCoreMask(KernelCore& kernel, s32* out_ideal_core, u64* out_affinity_mask); - Result SetCoreMask(s32 cpu_core_id, u64 v_affinity_mask); + Result SetCoreMask(KernelCore& kernel, s32 cpu_core_id, u64 v_affinity_mask); - Result SetActivity(Svc::ThreadActivity activity); + Result SetActivity(KernelCore& kernel, Svc::ThreadActivity activity); - Result Sleep(s64 timeout); + Result Sleep(KernelCore& kernel, s64 timeout); s64 GetYieldScheduleCount() const { return m_schedule_count; @@ -372,7 +364,7 @@ public: m_schedule_count = count; } - void WaitCancel(); + void WaitCancel(KernelCore& kernel); bool IsWaitCancelled() const { return m_wait_cancelled; @@ -417,17 +409,17 @@ public: return reinterpret_cast(m_parent) | (m_resource_limit_release_hint ? 1 : 0); } - void Finalize() override; + void Finalize(KernelCore& kernel) override; - bool IsSignaled() const override; + bool IsSignaled(KernelCore& kernel) const override; - void OnTimer(); + void OnTimer(KernelCore& kernel); - void DoWorkerTaskImpl(); + void DoWorkerTaskImpl(KernelCore& kernel); - static void PostDestroy(uintptr_t arg); + static void PostDestroy(KernelCore& kernel, uintptr_t arg); - static Result InitializeDummyThread(KThread* thread, KProcess* owner); + static Result InitializeDummyThread(Core::System& system, KThread* thread, KProcess* owner); static Result InitializeMainThread(Core::System& system, KThread* thread, s32 virt_core); @@ -514,19 +506,18 @@ public: return this->GetStackParameters().disable_count; } - void DisableDispatch() { - ASSERT(GetCurrentThread(m_kernel).GetDisableDispatchCount() >= 0); + void DisableDispatch(KernelCore& kernel) { + ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() >= 0); this->GetStackParameters().disable_count++; } - void EnableDispatch() { - ASSERT(GetCurrentThread(m_kernel).GetDisableDispatchCount() > 0); + void EnableDispatch(KernelCore& kernel) { + ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() > 0); this->GetStackParameters().disable_count--; } - void Pin(s32 current_core); - - void Unpin(); + void Pin(KernelCore& kernel, s32 current_core); + void Unpin(KernelCore& kernel); void SetInExceptionHandler() { this->GetStackParameters().is_in_exception_handler = true; @@ -592,18 +583,18 @@ public: return this->GetThreadType() == ThreadType::Dummy; } - void AddWaiter(KThread* thread); + void AddWaiter(KernelCore& kernel, KThread* thread); - void RemoveWaiter(KThread* thread); + void RemoveWaiter(KernelCore& kernel, KThread* thread); - Result GetThreadContext3(Svc::ThreadContext* out); + Result GetThreadContext3(KernelCore& kernel, Svc::ThreadContext* out); - KThread* RemoveUserWaiterByKey(bool* out_has_waiters, KProcessAddress key) { - return this->RemoveWaiterByKey(out_has_waiters, key, false); + KThread* RemoveUserWaiterByKey(KernelCore& kernel, bool* out_has_waiters, KProcessAddress key) { + return this->RemoveWaiterByKey(kernel, out_has_waiters, key, false); } - KThread* RemoveKernelWaiterByKey(bool* out_has_waiters, KProcessAddress key) { - return this->RemoveWaiterByKey(out_has_waiters, key, true); + KThread* RemoveKernelWaiterByKey(KernelCore& kernel, bool* out_has_waiters, KProcessAddress key) { + return this->RemoveWaiterByKey(kernel, out_has_waiters, key, true); } KProcessAddress GetAddressKey() const { @@ -641,10 +632,10 @@ public: m_wait_queue = nullptr; } - void BeginWait(KThreadQueue* queue); - void NotifyAvailable(KSynchronizationObject* signaled_object, Result wait_result); - void EndWait(Result wait_result); - void CancelWait(Result wait_result, bool cancel_timer_task); + void BeginWait(KernelCore& kernel, KThreadQueue* queue); + void NotifyAvailable(KernelCore& kernel, KSynchronizationObject* signaled_object, Result wait_result); + void EndWait(KernelCore& kernel, Result wait_result); + void CancelWait(KernelCore& kernel, Result wait_result, bool cancel_timer_task); s32 GetNumKernelWaiters() const { return m_num_kernel_waiters; @@ -662,9 +653,9 @@ public: // therefore will not block on guest kernel synchronization primitives. These methods handle // blocking as needed. - void RequestDummyThreadWait(); - void DummyThreadBeginWait(); - void DummyThreadEndWait(); + void RequestDummyThreadWait(KernelCore& kernel); + void DummyThreadBeginWait(KernelCore& kernel); + void DummyThreadEndWait(KernelCore& kernel); uintptr_t GetArgument() const { return m_argument; @@ -690,8 +681,7 @@ public: } private: - KThread* RemoveWaiterByKey(bool* out_has_waiters, KProcessAddress key, - bool is_kernel_address_key); + KThread* RemoveWaiterByKey(KernelCore& kernel, bool* out_has_waiters, KProcessAddress key, bool is_kernel_address_key); static constexpr size_t PriorityInheritanceCountMax = 10; union SyncObjectBuffer { @@ -735,19 +725,19 @@ private: } }; - void AddWaiterImpl(KThread* thread); - void RemoveWaiterImpl(KThread* thread); + void AddWaiterImpl(KernelCore& kernel, KThread* thread); + void RemoveWaiterImpl(KernelCore& kernel, KThread* thread); static void RestorePriority(KernelCore& kernel, KThread* thread); - void StartTermination(); - void FinishTermination(); + void StartTermination(KernelCore& kernel); + void FinishTermination(KernelCore& kernel); - void IncreaseBasePriority(s32 priority); + void IncreaseBasePriority(KernelCore& kernel, s32 priority); - Result Initialize(KThreadFunction func, uintptr_t arg, KProcessAddress user_stack_top, s32 prio, + Result Initialize(KernelCore& kernel, KThreadFunction func, uintptr_t arg, KProcessAddress user_stack_top, s32 prio, s32 virt_core, KProcess* owner, ThreadType type); - static Result InitializeThread(KThread* thread, KThreadFunction func, uintptr_t arg, + static Result InitializeThread(KernelCore& kernel, KThread* thread, KThreadFunction func, uintptr_t arg, KProcessAddress user_stack_top, s32 prio, s32 core, KProcess* owner, ThreadType type, std::function&& init_func); @@ -878,9 +868,8 @@ public: return m_waiting_lock_info; } - void AddHeldLock(LockWithPriorityInheritanceInfo* lock_info); - LockWithPriorityInheritanceInfo* FindHeldLock(KProcessAddress address_key, - bool is_kernel_address_key); + void AddHeldLock(KernelCore& kernel, LockWithPriorityInheritanceInfo* lock_info); + LockWithPriorityInheritanceInfo* FindHeldLock(KernelCore& kernel, KProcessAddress address_key, bool is_kernel_address_key); private: using LockWithPriorityInheritanceInfoList = @@ -996,7 +985,7 @@ public: if (m_kernel.IsShuttingDown()) { return; } - GetCurrentThread(kernel).DisableDispatch(); + GetCurrentThread(kernel).DisableDispatch(kernel); } ~KScopedDisableDispatch(); @@ -1005,8 +994,8 @@ private: KernelCore& m_kernel; }; -inline void KTimerTask::OnTimer() { - static_cast(this)->OnTimer(); +inline void KTimerTask::OnTimer(KernelCore& kernel) { + static_cast(this)->OnTimer(kernel); } } // namespace Kernel diff --git a/src/core/hle/kernel/k_thread_queue.cpp b/src/core/hle/kernel/k_thread_queue.cpp index 61488f4ce6..7c4bfe5a49 100644 --- a/src/core/hle/kernel/k_thread_queue.cpp +++ b/src/core/hle/kernel/k_thread_queue.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -7,17 +10,16 @@ namespace Kernel { -void KThreadQueue::NotifyAvailable(KThread* waiting_thread, KSynchronizationObject* signaled_object, - Result wait_result) { +void KThreadQueue::NotifyAvailable(KernelCore& kernel, KThread* waiting_thread, KSynchronizationObject* signaled_object, Result wait_result) { UNREACHABLE(); } -void KThreadQueue::EndWait(KThread* waiting_thread, Result wait_result) { +void KThreadQueue::EndWait(KernelCore& kernel, KThread* waiting_thread, Result wait_result) { // Set the thread's wait result. waiting_thread->SetWaitResult(wait_result); // Set the thread as runnable. - waiting_thread->SetState(ThreadState::Runnable); + waiting_thread->SetState(kernel, ThreadState::Runnable); // Clear the thread's wait queue. waiting_thread->ClearWaitQueue(); @@ -28,12 +30,12 @@ void KThreadQueue::EndWait(KThread* waiting_thread, Result wait_result) { } } -void KThreadQueue::CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) { +void KThreadQueue::CancelWait(KernelCore& kernel, KThread* waiting_thread, Result wait_result, bool cancel_timer_task) { // Set the thread's wait result. waiting_thread->SetWaitResult(wait_result); // Set the thread as runnable. - waiting_thread->SetState(ThreadState::Runnable); + waiting_thread->SetState(kernel, ThreadState::Runnable); // Clear the thread's wait queue. waiting_thread->ClearWaitQueue(); @@ -44,7 +46,7 @@ void KThreadQueue::CancelWait(KThread* waiting_thread, Result wait_result, bool } } -void KThreadQueueWithoutEndWait::EndWait(KThread* waiting_thread, Result wait_result) { +void KThreadQueueWithoutEndWait::EndWait(KernelCore& kernel, KThread* waiting_thread, Result wait_result) { UNREACHABLE(); } diff --git a/src/core/hle/kernel/k_thread_queue.h b/src/core/hle/kernel/k_thread_queue.h index 117af0919a..953bb78ac6 100644 --- a/src/core/hle/kernel/k_thread_queue.h +++ b/src/core/hle/kernel/k_thread_queue.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -12,28 +15,25 @@ class KHardwareTimer; class KThreadQueue { public: - explicit KThreadQueue(KernelCore& kernel) : m_kernel{kernel}, m_hardware_timer{} {} + explicit KThreadQueue(KernelCore& kernel) : m_hardware_timer{} {} virtual ~KThreadQueue() = default; void SetHardwareTimer(KHardwareTimer* timer) { m_hardware_timer = timer; } - virtual void NotifyAvailable(KThread* waiting_thread, KSynchronizationObject* signaled_object, - Result wait_result); - virtual void EndWait(KThread* waiting_thread, Result wait_result); - virtual void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task); + virtual void NotifyAvailable(KernelCore& kernel, KThread* waiting_thread, KSynchronizationObject* signaled_object, Result wait_result); + virtual void EndWait(KernelCore& kernel, KThread* waiting_thread, Result wait_result); + virtual void CancelWait(KernelCore& kernel, KThread* waiting_thread, Result wait_result, bool cancel_timer_task); private: - KernelCore& m_kernel; KHardwareTimer* m_hardware_timer{}; }; class KThreadQueueWithoutEndWait : public KThreadQueue { public: explicit KThreadQueueWithoutEndWait(KernelCore& kernel) : KThreadQueue(kernel) {} - - void EndWait(KThread* waiting_thread, Result wait_result) override final; + void EndWait(KernelCore& kernel, KThread* waiting_thread, Result wait_result) override final; }; } // namespace Kernel diff --git a/src/core/hle/kernel/k_timer_task.h b/src/core/hle/kernel/k_timer_task.h index 66f0a5a908..7625c1fee1 100644 --- a/src/core/hle/kernel/k_timer_task.h +++ b/src/core/hle/kernel/k_timer_task.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -7,6 +10,7 @@ namespace Kernel { +class KernelCore; class KTimerTask : public Common::IntrusiveRedBlackTreeBaseNode { public: static constexpr int Compare(const KTimerTask& lhs, const KTimerTask& rhs) { @@ -30,7 +34,7 @@ public: // NOTE: This is virtual in Nintendo's kernel. Prior to 13.0.0, KWaitObject was also a // TimerTask; this is no longer the case. Since this is now KThread exclusive, we have // devirtualized (see inline declaration for this inside k_thread.h). - void OnTimer(); + void OnTimer(KernelCore& kernel); private: // Absolute time in nanoseconds diff --git a/src/core/hle/kernel/k_trace.h b/src/core/hle/kernel/k_trace.h index d61af48303..84dd4134e1 100644 --- a/src/core/hle/kernel/k_trace.h +++ b/src/core/hle/kernel/k_trace.h @@ -1,8 +1,13 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once +#include "common/literals.h" + namespace Kernel { using namespace Common::Literals; diff --git a/src/core/hle/kernel/k_transfer_memory.cpp b/src/core/hle/kernel/k_transfer_memory.cpp index 09295e8ad8..a4ef530b5b 100644 --- a/src/core/hle/kernel/k_transfer_memory.cpp +++ b/src/core/hle/kernel/k_transfer_memory.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -14,16 +17,15 @@ KTransferMemory::KTransferMemory(KernelCore& kernel) KTransferMemory::~KTransferMemory() = default; -Result KTransferMemory::Initialize(KProcessAddress addr, std::size_t size, - Svc::MemoryPermission own_perm) { +Result KTransferMemory::Initialize(KernelCore& kernel, KProcessAddress addr, std::size_t size, Svc::MemoryPermission own_perm) { // Set members. - m_owner = GetCurrentProcessPointer(m_kernel); + m_owner = GetCurrentProcessPointer(kernel); // Get the owner page table. auto& page_table = m_owner->GetPageTable(); // Construct the page group, guarding to make sure our state is valid on exit. - m_page_group.emplace(m_kernel, page_table.GetBlockInfoManager()); + m_page_group.emplace(kernel, page_table.GetBlockInfoManager()); auto pg_guard = SCOPE_GUARD { m_page_group.reset(); }; @@ -33,7 +35,7 @@ Result KTransferMemory::Initialize(KProcessAddress addr, std::size_t size, ConvertToKMemoryPermission(own_perm))); // Set remaining tracking members. - m_owner->Open(); + m_owner->Open(kernel); m_owner_perm = own_perm; m_address = addr; m_is_initialized = true; @@ -44,7 +46,7 @@ Result KTransferMemory::Initialize(KProcessAddress addr, std::size_t size, R_SUCCEED(); } -void KTransferMemory::Finalize() { +void KTransferMemory::Finalize(KernelCore& kernel) { // Unlock. if (!m_is_mapped) { const size_t size = m_page_group->GetNumPages() * PageSize; @@ -53,17 +55,17 @@ void KTransferMemory::Finalize() { } // Close the page group. - m_page_group->Close(); + m_page_group->Close(kernel); m_page_group->Finalize(); } -void KTransferMemory::PostDestroy(uintptr_t arg) { +void KTransferMemory::PostDestroy(KernelCore& kernel, uintptr_t arg) { KProcess* owner = reinterpret_cast(arg); - owner->GetResourceLimit()->Release(LimitableResource::TransferMemoryCountMax, 1); - owner->Close(); + owner->GetResourceLimit()->Release(kernel, LimitableResource::TransferMemoryCountMax, 1); + owner->Close(kernel); } -Result KTransferMemory::Map(KProcessAddress address, size_t size, Svc::MemoryPermission map_perm) { +Result KTransferMemory::Map(KernelCore& kernel, KProcessAddress address, size_t size, Svc::MemoryPermission map_perm) { // Validate the size. R_UNLESS(m_page_group->GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize); @@ -80,7 +82,7 @@ Result KTransferMemory::Map(KProcessAddress address, size_t size, Svc::MemoryPer const KMemoryState state = (m_owner_perm == Svc::MemoryPermission::None) ? KMemoryState::Transferred : KMemoryState::SharedTransferred; - R_TRY(GetCurrentProcess(m_kernel).GetPageTable().MapPageGroup( + R_TRY(GetCurrentProcess(kernel).GetPageTable().MapPageGroup( address, *m_page_group, state, KMemoryPermission::UserReadWrite)); // Mark ourselves as mapped. @@ -89,7 +91,7 @@ Result KTransferMemory::Map(KProcessAddress address, size_t size, Svc::MemoryPer R_SUCCEED(); } -Result KTransferMemory::Unmap(KProcessAddress address, size_t size) { +Result KTransferMemory::Unmap(KernelCore& kernel, KProcessAddress address, size_t size) { // Validate the size. R_UNLESS(m_page_group->GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize); @@ -100,7 +102,7 @@ Result KTransferMemory::Unmap(KProcessAddress address, size_t size) { const KMemoryState state = (m_owner_perm == Svc::MemoryPermission::None) ? KMemoryState::Transferred : KMemoryState::SharedTransferred; - R_TRY(GetCurrentProcess(m_kernel).GetPageTable().UnmapPageGroup(address, *m_page_group, state)); + R_TRY(GetCurrentProcess(kernel).GetPageTable().UnmapPageGroup(address, *m_page_group, state)); // Mark ourselves as unmapped. ASSERT(m_is_mapped); diff --git a/src/core/hle/kernel/k_transfer_memory.h b/src/core/hle/kernel/k_transfer_memory.h index 530b452182..0753b4fd6d 100644 --- a/src/core/hle/kernel/k_transfer_memory.h +++ b/src/core/hle/kernel/k_transfer_memory.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -30,9 +33,9 @@ public: explicit KTransferMemory(KernelCore& kernel); ~KTransferMemory() override; - Result Initialize(KProcessAddress address, std::size_t size, Svc::MemoryPermission owner_perm); + Result Initialize(KernelCore& kernel, KProcessAddress address, std::size_t size, Svc::MemoryPermission owner_perm); - void Finalize() override; + void Finalize(KernelCore& kernel) override; bool IsInitialized() const override { return m_is_initialized; @@ -42,7 +45,7 @@ public: return reinterpret_cast(m_owner); } - static void PostDestroy(uintptr_t arg); + static void PostDestroy(KernelCore& kernel, uintptr_t arg); KProcess* GetOwner() const override { return m_owner; @@ -54,8 +57,8 @@ public: size_t GetSize() const; - Result Map(KProcessAddress address, size_t size, Svc::MemoryPermission map_perm); - Result Unmap(KProcessAddress address, size_t size); + Result Map(KernelCore& kernel, KProcessAddress address, size_t size, Svc::MemoryPermission map_perm); + Result Unmap(KernelCore& kernel, KProcessAddress address, size_t size); private: std::optional m_page_group{}; diff --git a/src/core/hle/kernel/k_typed_address.h b/src/core/hle/kernel/k_typed_address.h index d57535ba01..d793d67a10 100644 --- a/src/core/hle/kernel/k_typed_address.h +++ b/src/core/hle/kernel/k_typed_address.h @@ -1,6 +1,11 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#pragma once + #include "common/typed_address.h" namespace Kernel { diff --git a/src/core/hle/kernel/k_worker_task.h b/src/core/hle/kernel/k_worker_task.h index 9a230c03c4..a4820bc412 100644 --- a/src/core/hle/kernel/k_worker_task.h +++ b/src/core/hle/kernel/k_worker_task.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -10,8 +13,7 @@ namespace Kernel { class KWorkerTask : public KSynchronizationObject { public: explicit KWorkerTask(KernelCore& kernel); - - void DoWorkerTask(); + void DoWorkerTask(KernelCore& kernel); }; } // namespace Kernel diff --git a/src/core/hle/kernel/k_worker_task_manager.cpp b/src/core/hle/kernel/k_worker_task_manager.cpp index 8ead395911..4427f9859a 100644 --- a/src/core/hle/kernel/k_worker_task_manager.cpp +++ b/src/core/hle/kernel/k_worker_task_manager.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -12,14 +15,14 @@ namespace Kernel { KWorkerTask::KWorkerTask(KernelCore& kernel) : KSynchronizationObject{kernel} {} -void KWorkerTask::DoWorkerTask() { +void KWorkerTask::DoWorkerTask(KernelCore& kernel) { if (auto* const thread = this->DynamicCast(); thread != nullptr) { - return thread->DoWorkerTaskImpl(); + return thread->DoWorkerTaskImpl(kernel); } else { auto* const process = this->DynamicCast(); ASSERT(process != nullptr); - return process->DoWorkerTaskImpl(); + return process->DoWorkerTaskImpl(kernel); } } @@ -32,9 +35,9 @@ void KWorkerTaskManager::AddTask(KernelCore& kernel, WorkerType type, KWorkerTas void KWorkerTaskManager::AddTask(KernelCore& kernel, KWorkerTask* task) { KScopedSchedulerLock sl(kernel); - m_waiting_thread.QueueWork([task]() { + m_waiting_thread.QueueWork([&kernel, task]() { // Do the task. - task->DoWorkerTask(); + task->DoWorkerTask(kernel); }); } diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 5f88fa498f..d25ac37df4 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -121,14 +121,14 @@ struct KernelCore::Impl { void TerminateAllProcesses() { std::scoped_lock lk{process_list_lock}; for (auto& process : process_list) { - process->Terminate(); - process->Close(); + process->Terminate(system.Kernel()); + process->Close(system.Kernel()); process = nullptr; } process_list.clear(); } - void Shutdown() { + void Shutdown(KernelCore& kernel) { is_shutting_down.store(true, std::memory_order_relaxed); SCOPE_EXIT { is_shutting_down.store(false, std::memory_order_relaxed); @@ -137,7 +137,7 @@ struct KernelCore::Impl { CloseServices(); if (application_process) { - application_process->Close(); + application_process->Close(system.Kernel()); application_process = nullptr; } @@ -149,9 +149,9 @@ struct KernelCore::Impl { preemption_event = nullptr; // Cleanup persistent kernel objects - auto CleanupObject = [](KAutoObject* obj) { + auto CleanupObject = [&kernel](KAutoObject* obj) { if (obj) { - obj->Close(); + obj->Close(kernel); obj = nullptr; } }; @@ -163,7 +163,7 @@ struct KernelCore::Impl { for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { if (shutdown_threads[core_id]) { - shutdown_threads[core_id]->Close(); + shutdown_threads[core_id]->Close(kernel); shutdown_threads[core_id] = nullptr; } @@ -178,7 +178,7 @@ struct KernelCore::Impl { std::scoped_lock lk{registered_in_use_objects_lock}; if (registered_in_use_objects.size()) { for (auto& object : registered_in_use_objects) { - object->Close(); + object->Close(kernel); } registered_in_use_objects.clear(); } @@ -226,7 +226,7 @@ struct KernelCore::Impl { ASSERT(Kernel::KThread::InitializeIdleThread(system, idle_thread, core).IsSuccess()); KThread::Register(system.Kernel(), idle_thread); - schedulers[i]->Initialize(main_thread, idle_thread, core); + schedulers[i]->Initialize(system.Kernel(), main_thread, idle_thread, core); } } @@ -247,19 +247,18 @@ struct KernelCore::Impl { ASSERT(system_resource_limit->SetLimitValue(LimitableResource::EventCountMax, 900).IsSuccess()); ASSERT(system_resource_limit->SetLimitValue(LimitableResource::TransferMemoryCountMax, 200).IsSuccess()); ASSERT(system_resource_limit->SetLimitValue(LimitableResource::SessionCountMax, 1133).IsSuccess()); - system_resource_limit->Reserve(LimitableResource::PhysicalMemoryMax, kernel_size); + system_resource_limit->Reserve(kernel, LimitableResource::PhysicalMemoryMax, kernel_size); // Reserve secure applet memory, introduced in firmware 5.0.0 constexpr u64 secure_applet_memory_size{4_MiB}; - ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemoryMax, - secure_applet_memory_size)); + ASSERT(system_resource_limit->Reserve(kernel, LimitableResource::PhysicalMemoryMax, secure_applet_memory_size)); } void InitializePreemption(KernelCore& kernel) { preemption_event = Core::Timing::CreateEvent("PreemptionCallback", [this, &kernel](s64 time, std::chrono::nanoseconds) -> std::optional { { KScopedSchedulerLock lock(kernel); - global_scheduler_context->PreemptThreads(); + global_scheduler_context->PreemptThreads(kernel); } return std::nullopt; }); @@ -350,9 +349,9 @@ struct KernelCore::Impl { object_name_global_data.emplace(kernel); } - void MakeApplicationProcess(KProcess* process) { + void MakeApplicationProcess(KernelCore& kernel, KProcess* process) { application_process = process; - application_process->Open(); + application_process->Open(kernel); } /// Sets the host thread ID for the caller. @@ -374,10 +373,10 @@ struct KernelCore::Impl { // Gets the dummy KThread for the caller, allocating a new one if this is the first time KThread* GetHostDummyThread(ThreadLocalData& t, KThread* existing_thread) { if (t.thread == nullptr) { - auto const initialize{[](KThread* thread) { - ASSERT(KThread::InitializeDummyThread(thread, nullptr).IsSuccess()); + auto const initialize = [this](KThread* thread) { + ASSERT(KThread::InitializeDummyThread(system, thread, nullptr).IsSuccess()); return thread; - }}; + }; t.raw_thread.emplace(system.Kernel()); t.thread = existing_thread ? existing_thread : initialize(&*t.raw_thread); ASSERT(t.thread != nullptr); @@ -418,8 +417,7 @@ struct KernelCore::Impl { return is_shutting_down.load(std::memory_order_relaxed); } - KThread* GetCurrentEmuThread() { - auto& t = tls_data; + KThread* GetCurrentEmuThread(ThreadLocalData& t) { return t.current_thread ? t.current_thread : (t.current_thread = GetHostDummyThread(t, nullptr)); } @@ -742,19 +740,19 @@ struct KernelCore::Impl { time_shared_mem = KSharedMemory::Create(system.Kernel()); hidbus_shared_mem = KSharedMemory::Create(system.Kernel()); - font_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None, + font_shared_mem->Initialize(system.Kernel(), system.DeviceMemory(), nullptr, Svc::MemoryPermission::None, Svc::MemoryPermission::Read, font_size); KSharedMemory::Register(kernel, font_shared_mem); - irs_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None, + irs_shared_mem->Initialize(system.Kernel(), system.DeviceMemory(), nullptr, Svc::MemoryPermission::None, Svc::MemoryPermission::Read, irs_size); KSharedMemory::Register(kernel, irs_shared_mem); - time_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None, + time_shared_mem->Initialize(system.Kernel(), system.DeviceMemory(), nullptr, Svc::MemoryPermission::None, Svc::MemoryPermission::Read, time_size); KSharedMemory::Register(kernel, time_shared_mem); - hidbus_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None, + hidbus_shared_mem->Initialize(system.Kernel(), system.DeviceMemory(), nullptr, Svc::MemoryPermission::None, Svc::MemoryPermission::Read, hidbus_size); KSharedMemory::Register(kernel, hidbus_shared_mem); } @@ -852,7 +850,7 @@ void KernelCore::Initialize() { } void KernelCore::Shutdown() { - impl->Shutdown(); + impl->Shutdown(*this); } void KernelCore::CloseServices() { @@ -868,7 +866,7 @@ KResourceLimit* KernelCore::GetSystemResourceLimit() { } void KernelCore::AppendNewProcess(KProcess* process) { - process->Open(); + process->Open(*this); std::scoped_lock lk{impl->process_list_lock}; impl->process_list.push_back(process); @@ -877,12 +875,12 @@ void KernelCore::AppendNewProcess(KProcess* process) { void KernelCore::RemoveProcess(KProcess* process) { std::scoped_lock lk{impl->process_list_lock}; if (std::erase(impl->process_list, process)) { - process->Close(); + process->Close(*this); } } void KernelCore::MakeApplicationProcess(KProcess* process) { - impl->MakeApplicationProcess(process); + impl->MakeApplicationProcess(*this, process); } KProcess* KernelCore::ApplicationProcess() { @@ -896,11 +894,8 @@ const KProcess* KernelCore::ApplicationProcess() const { std::list> KernelCore::GetProcessList() { std::list> processes; std::scoped_lock lk{impl->process_list_lock}; - - for (auto* const process : impl->process_list) { - processes.emplace_back(process); - } - + for (auto* const process : impl->process_list) + processes.emplace_back(*this, process); return processes; } @@ -1029,15 +1024,14 @@ void KernelCore::RegisterHostThread(KThread* existing_thread) { } } -static std::jthread RunHostThreadFunc(KernelCore& kernel, KProcess* process, - std::string&& thread_name, std::function&& func) { +static std::jthread RunHostThreadFunc(KernelCore& kernel, KProcess* process, std::string&& thread_name, std::function&& func) { // Reserve a new thread from the process resource limit. - KScopedResourceReservation thread_reservation(process, LimitableResource::ThreadCountMax); + KScopedResourceReservation thread_reservation(kernel, process, LimitableResource::ThreadCountMax); ASSERT(thread_reservation.Succeeded()); // Initialize the thread. KThread* thread = KThread::Create(kernel); - ASSERT(R_SUCCEEDED(KThread::InitializeDummyThread(thread, process))); + ASSERT(R_SUCCEEDED(KThread::InitializeDummyThread(kernel.System(), thread, process))); // Commit the thread reservation. thread_reservation.Commit(); @@ -1057,7 +1051,7 @@ static std::jthread RunHostThreadFunc(KernelCore& kernel, KProcess* process, // Close the thread. // This will free the process if it is the last reference. - thread->Close(); + thread->Close(kernel); }); } @@ -1066,11 +1060,11 @@ std::jthread KernelCore::RunOnHostCoreProcess(std::string&& process_name, // Make a new process. KProcess* process = KProcess::Create(*this); ASSERT(R_SUCCEEDED( - process->Initialize(Svc::CreateProcessParameter{}, GetSystemResourceLimit(), false))); + process->Initialize(*this, Svc::CreateProcessParameter{}, GetSystemResourceLimit(), false))); // Ensure that we don't hold onto any extra references. SCOPE_EXIT { - process->Close(); + process->Close(*this); }; // Register the new process. @@ -1096,18 +1090,18 @@ void KernelCore::RunOnGuestCoreProcess(std::string&& process_name, std::function // Make a new process. KProcess* process = KProcess::Create(*this); ASSERT(R_SUCCEEDED( - process->Initialize(Svc::CreateProcessParameter{}, GetSystemResourceLimit(), false))); + process->Initialize(*this, Svc::CreateProcessParameter{}, GetSystemResourceLimit(), false))); // Ensure that we don't hold onto any extra references. SCOPE_EXIT { - process->Close(); + process->Close(*this); }; // Register the new process. KProcess::Register(*this, process); // Reserve a new thread from the process resource limit. - KScopedResourceReservation thread_reservation(process, LimitableResource::ThreadCountMax); + KScopedResourceReservation thread_reservation(*this, process, LimitableResource::ThreadCountMax); ASSERT(thread_reservation.Succeeded()); // Initialize the thread. @@ -1122,7 +1116,7 @@ void KernelCore::RunOnGuestCoreProcess(std::string&& process_name, std::function KThread::Register(*this, thread); // Begin running the thread. - ASSERT(R_SUCCEEDED(thread->Run())); + ASSERT(R_SUCCEEDED(thread->Run(*this))); } u32 KernelCore::GetCurrentHostThreadID() const { @@ -1130,7 +1124,7 @@ u32 KernelCore::GetCurrentHostThreadID() const { } KThread* KernelCore::GetCurrentEmuThread() const { - return impl->GetCurrentEmuThread(); + return impl->GetCurrentEmuThread(Impl::tls_data); } void KernelCore::SetCurrentEmuThread(KThread* thread) { @@ -1206,9 +1200,9 @@ void KernelCore::SuspendEmulation(bool suspended) { for (auto& thread : process->GetThreadList()) { if (should_suspend) { - thread.RequestSuspend(SuspendType::System); + thread.RequestSuspend(*this, SuspendType::System); } else { - thread.Resume(SuspendType::System); + thread.Resume(*this, SuspendType::System); } } } @@ -1244,12 +1238,9 @@ void KernelCore::SuspendEmulation(bool suspended) { void KernelCore::ShutdownCores() { impl->TerminateAllProcesses(); - KScopedSchedulerLock lk{*this}; - - for (auto* thread : impl->shutdown_threads) { - void(thread->Run()); - } + for (auto* thread : impl->shutdown_threads) + void(thread->Run(*this)); } bool KernelCore::IsMulticore() const { diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp index f394122764..a81c14f643 100644 --- a/src/core/hle/kernel/physical_core.cpp +++ b/src/core/hle/kernel/physical_core.cpp @@ -17,14 +17,15 @@ namespace Kernel { PhysicalCore::PhysicalCore(KernelCore& kernel, std::size_t core_index) - : m_kernel{kernel}, m_core_index{core_index} { + : m_core_index{core_index} +{ m_is_single_core = !kernel.IsMulticore(); } PhysicalCore::~PhysicalCore() = default; -void PhysicalCore::RunThread(Kernel::KThread* thread) { +void PhysicalCore::RunThread(KernelCore& kernel, Kernel::KThread* thread) { auto* process = thread->GetOwnerProcess(); - auto& system = m_kernel.System(); + auto& system = kernel.System(); auto* interface = process->GetArmInterface(m_core_index); interface->Initialize(); @@ -72,14 +73,14 @@ void PhysicalCore::RunThread(Kernel::KThread* thread) { while (true) { // If the thread is scheduled for termination, exit. if (thread->HasDpc() && thread->IsTerminationRequested()) { - thread->Exit(); + thread->Exit(kernel); } // Notify the debugger and go to sleep if a step was performed // and this thread has been scheduled again. if (thread->GetStepState() == StepState::StepPerformed) { system.GetDebugger().NotifyThreadStopped(thread); - thread->RequestSuspend(SuspendType::Debug); + thread->RequestSuspend(kernel, SuspendType::Debug); return; } @@ -135,7 +136,7 @@ void PhysicalCore::RunThread(Kernel::KThread* thread) { } else { interface->LogBacktrace(process); } - thread->RequestSuspend(SuspendType::Debug); + thread->RequestSuspend(kernel, SuspendType::Debug); return; } @@ -144,7 +145,7 @@ void PhysicalCore::RunThread(Kernel::KThread* thread) { if (system.DebuggerEnabled()) { system.GetDebugger().NotifyThreadWatchpoint(thread, *interface->HaltedWatchpoint()); } - thread->RequestSuspend(SuspendType::Debug); + thread->RequestSuspend(kernel, SuspendType::Debug); return; } @@ -208,8 +209,8 @@ void PhysicalCore::CloneFpuStatus(KThread* dst) const { dst->GetContext().fpsr = ctx.fpsr; } -void PhysicalCore::LogBacktrace() { - auto* process = GetCurrentProcessPointer(m_kernel); +void PhysicalCore::LogBacktrace(KernelCore& kernel) { + auto* process = GetCurrentProcessPointer(kernel); if (!process) { return; } diff --git a/src/core/hle/kernel/physical_core.h b/src/core/hle/kernel/physical_core.h index bae4fe5b88..b92201204a 100644 --- a/src/core/hle/kernel/physical_core.h +++ b/src/core/hle/kernel/physical_core.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -30,7 +33,7 @@ public: YUZU_NON_MOVEABLE(PhysicalCore); // Execute guest code running on the given thread. - void RunThread(KThread* thread); + void RunThread(KernelCore& kernel, KThread* thread); // Copy context from thread to current core. void LoadContext(const KThread* thread); @@ -44,7 +47,7 @@ public: void CloneFpuStatus(KThread* dst) const; // Log backtrace of current processor state. - void LogBacktrace(); + void LogBacktrace(KernelCore& kernel); // Wait for an interrupt. void Idle(); @@ -63,9 +66,7 @@ public: } private: - KernelCore& m_kernel; const std::size_t m_core_index; - std::mutex m_guard; std::condition_variable m_on_interrupt; Core::ArmInterface* m_arm_interface{}; diff --git a/src/core/hle/kernel/slab_helpers.h b/src/core/hle/kernel/slab_helpers.h index d1bbc76709..95e6b9f6e6 100644 --- a/src/core/hle/kernel/slab_helpers.h +++ b/src/core/hle/kernel/slab_helpers.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -69,16 +72,16 @@ public: explicit KAutoObjectWithSlabHeap(KernelCore& kernel) : Base(kernel) {} virtual ~KAutoObjectWithSlabHeap() = default; - virtual void Destroy() override { + virtual void Destroy(KernelCore& kernel) override { const bool is_initialized = this->IsInitialized(); uintptr_t arg = 0; if (is_initialized) { arg = this->GetPostDestroyArgument(); - this->Finalize(); + this->Finalize(kernel); } - Free(Base::m_kernel, static_cast(this)); + Free(kernel, static_cast(this)); if (is_initialized) { - Derived::PostDestroy(arg); + Derived::PostDestroy(kernel, arg); } } @@ -89,8 +92,8 @@ public: return 0; } - size_t GetSlabIndex() const { - return SlabHeap(Base::m_kernel).GetObjectIndex(static_cast(this)); + size_t GetSlabIndex(KernelCore& kernel) const { + return SlabHeap(kernel).GetObjectIndex(static_cast(this)); } public: @@ -144,17 +147,17 @@ public: KAutoObjectWithSlabHeapAndContainer(KernelCore& kernel) : Base(kernel) {} virtual ~KAutoObjectWithSlabHeapAndContainer() {} - virtual void Destroy() override { + virtual void Destroy(KernelCore& kernel) override { const bool is_initialized = this->IsInitialized(); uintptr_t arg = 0; if (is_initialized) { - Base::m_kernel.ObjectListContainer().Unregister(this); + kernel.ObjectListContainer().Unregister(this); arg = this->GetPostDestroyArgument(); - this->Finalize(); + this->Finalize(kernel); } - Free(Base::m_kernel, static_cast(this)); + Free(kernel, static_cast(this)); if (is_initialized) { - Derived::PostDestroy(arg); + Derived::PostDestroy(kernel, arg); } } @@ -165,8 +168,8 @@ public: return 0; } - size_t GetSlabIndex() const { - return SlabHeap(Base::m_kernel).GetObjectIndex(static_cast(this)); + size_t GetSlabIndex(KernelCore& kernel) const { + return SlabHeap(kernel).GetObjectIndex(static_cast(this)); } public: @@ -177,9 +180,8 @@ public: static Derived* Create(KernelCore& kernel) { Derived* obj = Allocate(kernel); - if (obj != nullptr) { + if (obj != nullptr) KAutoObject::Create(obj); - } return obj; } diff --git a/src/core/hle/kernel/svc/svc_activity.cpp b/src/core/hle/kernel/svc/svc_activity.cpp index 63bc085554..c1ab3daab3 100644 --- a/src/core/hle/kernel/svc/svc_activity.cpp +++ b/src/core/hle/kernel/svc/svc_activity.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -22,17 +25,15 @@ Result SetThreadActivity(Core::System& system, Handle thread_handle, R_UNLESS(IsValidThreadActivity(thread_activity), ResultInvalidEnumValue); // Get the thread from its handle. - KScopedAutoObject thread = - GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject(thread_handle); + KScopedAutoObject thread = GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject(system.Kernel(), thread_handle); R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); // Check that the activity is being set on a non-current thread for the current process. - R_UNLESS(thread->GetOwnerProcess() == GetCurrentProcessPointer(system.Kernel()), - ResultInvalidHandle); + R_UNLESS(thread->GetOwnerProcess() == GetCurrentProcessPointer(system.Kernel()), ResultInvalidHandle); R_UNLESS(thread.GetPointerUnsafe() != GetCurrentThreadPointer(system.Kernel()), ResultBusy); // Set the activity. - R_TRY(thread->SetActivity(thread_activity)); + R_TRY(thread->SetActivity(system.Kernel(), thread_activity)); return ResultSuccess; } diff --git a/src/core/hle/kernel/svc/svc_cache.cpp b/src/core/hle/kernel/svc/svc_cache.cpp index c2c8be10f5..24f64f28f1 100644 --- a/src/core/hle/kernel/svc/svc_cache.cpp +++ b/src/core/hle/kernel/svc/svc_cache.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -37,8 +40,7 @@ Result FlushProcessDataCache(Core::System& system, Handle process_handle, u64 ad R_UNLESS(size == static_cast(size), ResultInvalidCurrentMemory); // Get the process from its handle. - KScopedAutoObject process = - GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject(process_handle); + KScopedAutoObject process = GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject(system.Kernel(), process_handle); R_UNLESS(process.IsNotNull(), ResultInvalidHandle); // Verify the region is within range. diff --git a/src/core/hle/kernel/svc/svc_code_memory.cpp b/src/core/hle/kernel/svc/svc_code_memory.cpp index 4e7af9f575..4133586d11 100644 --- a/src/core/hle/kernel/svc/svc_code_memory.cpp +++ b/src/core/hle/kernel/svc/svc_code_memory.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project @@ -35,9 +35,6 @@ constexpr bool IsValidUnmapFromOwnerCodeMemoryPermission(MemoryPermission perm) Result CreateCodeMemory(Core::System& system, Handle* out, u64 address, uint64_t size) { LOG_TRACE(Kernel_SVC, "called, address={:#X}, size=0x{:X}", address, size); - // Get kernel instance. - auto& kernel = system.Kernel(); - // Validate address / size. R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); @@ -46,24 +43,23 @@ Result CreateCodeMemory(Core::System& system, Handle* out, u64 address, uint64_t // Create the code memory. - KCodeMemory* code_mem = KCodeMemory::Create(kernel); + KCodeMemory* code_mem = KCodeMemory::Create(system.Kernel()); R_UNLESS(code_mem != nullptr, ResultOutOfResource); SCOPE_EXIT { - code_mem->Close(); + code_mem->Close(system.Kernel()); }; // Verify that the region is in range. - R_UNLESS(GetCurrentProcess(system.Kernel()).GetPageTable().Contains(address, size), - ResultInvalidCurrentMemory); + R_UNLESS(GetCurrentProcess(system.Kernel()).GetPageTable().Contains(address, size), ResultInvalidCurrentMemory); // Initialize the code memory. - R_TRY(code_mem->Initialize(system.DeviceMemory(), address, size)); + R_TRY(code_mem->Initialize(system.Kernel(), system.DeviceMemory(), address, size)); // Register the code memory. - KCodeMemory::Register(kernel, code_mem); + KCodeMemory::Register(system.Kernel(), code_mem); // Add the code memory to the handle table. - R_TRY(GetCurrentProcess(system.Kernel()).GetHandleTable().Add(out, code_mem)); + R_TRY(GetCurrentProcess(system.Kernel()).GetHandleTable().Add(system.Kernel(), out, code_mem)); R_SUCCEED(); } @@ -85,8 +81,8 @@ Result ControlCodeMemory(Core::System& system, Handle code_memory_handle, // Get the code memory from its handle. KScopedAutoObject code_mem = GetCurrentProcess(system.Kernel()) - .GetHandleTable() - .GetObject(code_memory_handle); + .GetHandleTable() + .GetObject(system.Kernel(), code_memory_handle); R_UNLESS(code_mem.IsNotNull(), ResultInvalidHandle); // NOTE: Here, Atmosphere extends the SVC to allow code memory operations on one's own process. @@ -96,29 +92,23 @@ Result ControlCodeMemory(Core::System& system, Handle code_memory_handle, switch (operation) { case CodeMemoryOperation::Map: { // Check that the region is in range. - R_UNLESS(GetCurrentProcess(system.Kernel()) - .GetPageTable() - .CanContain(address, size, KMemoryState::CodeOut), - ResultInvalidMemoryRegion); + R_UNLESS(GetCurrentProcess(system.Kernel()).GetPageTable().CanContain(address, size, KMemoryState::CodeOut), ResultInvalidMemoryRegion); // Check the memory permission. R_UNLESS(IsValidMapCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission); // Map the memory. - R_TRY(code_mem->Map(address, size)); + R_TRY(code_mem->Map(system.Kernel(), address, size)); } break; case CodeMemoryOperation::Unmap: { // Check that the region is in range. - R_UNLESS(GetCurrentProcess(system.Kernel()) - .GetPageTable() - .CanContain(address, size, KMemoryState::CodeOut), - ResultInvalidMemoryRegion); + R_UNLESS(GetCurrentProcess(system.Kernel()).GetPageTable().CanContain(address, size, KMemoryState::CodeOut), ResultInvalidMemoryRegion); // Check the memory permission. R_UNLESS(IsValidUnmapCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission); // Unmap the memory. - R_TRY(code_mem->Unmap(address, size)); + R_TRY(code_mem->Unmap(system.Kernel(), address, size)); } break; case CodeMemoryOperation::MapToOwner: { // Check that the region is in range. @@ -130,7 +120,7 @@ Result ControlCodeMemory(Core::System& system, Handle code_memory_handle, R_UNLESS(IsValidMapToOwnerCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission); // Map the memory to its owner. - R_TRY(code_mem->MapToOwner(address, size, perm)); + R_TRY(code_mem->MapToOwner(system.Kernel(), address, size, perm)); } break; case CodeMemoryOperation::UnmapFromOwner: { // Check that the region is in range. @@ -142,7 +132,7 @@ Result ControlCodeMemory(Core::System& system, Handle code_memory_handle, R_UNLESS(IsValidUnmapFromOwnerCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission); // Unmap the memory from its owner. - R_TRY(code_mem->UnmapFromOwner(address, size)); + R_TRY(code_mem->UnmapFromOwner(system.Kernel(), address, size)); } break; default: R_THROW(ResultInvalidEnumValue); diff --git a/src/core/hle/kernel/svc/svc_debug.cpp b/src/core/hle/kernel/svc/svc_debug.cpp index a4d1f700e8..7781339a9e 100644 --- a/src/core/hle/kernel/svc/svc_debug.cpp +++ b/src/core/hle/kernel/svc/svc_debug.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -12,8 +15,8 @@ Result DebugActiveProcess(Core::System& system, Handle* out_handle, uint64_t pro } Result BreakDebugProcess(Core::System& system, Handle debug_handle) { - UNIMPLEMENTED(); - R_THROW(ResultNotImplemented); + LOG_WARNING(Service, "(STUBBED) called"); + R_SUCCEED(); } Result TerminateDebugProcess(Core::System& system, Handle debug_handle) { diff --git a/src/core/hle/kernel/svc/svc_device_address_space.cpp b/src/core/hle/kernel/svc/svc_device_address_space.cpp index ac828320f4..2deb683d4b 100644 --- a/src/core/hle/kernel/svc/svc_device_address_space.cpp +++ b/src/core/hle/kernel/svc/svc_device_address_space.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -29,17 +32,17 @@ Result CreateDeviceAddressSpace(Core::System& system, Handle* out, uint64_t das_ KDeviceAddressSpace* das = KDeviceAddressSpace::Create(system.Kernel()); R_UNLESS(das != nullptr, ResultOutOfResource); SCOPE_EXIT { - das->Close(); + das->Close(system.Kernel()); }; // Initialize the device address space. - R_TRY(das->Initialize(das_address, das_size)); + R_TRY(das->Initialize(system.Kernel(), das_address, das_size)); // Register the device address space. KDeviceAddressSpace::Register(system.Kernel(), das); // Add to the handle table. - R_TRY(GetCurrentProcess(system.Kernel()).GetHandleTable().Add(out, das)); + R_TRY(GetCurrentProcess(system.Kernel()).GetHandleTable().Add(system.Kernel(), out, das)); R_SUCCEED(); } @@ -47,8 +50,8 @@ Result CreateDeviceAddressSpace(Core::System& system, Handle* out, uint64_t das_ Result AttachDeviceAddressSpace(Core::System& system, DeviceName device_name, Handle das_handle) { // Get the device address space. KScopedAutoObject das = GetCurrentProcess(system.Kernel()) - .GetHandleTable() - .GetObject(das_handle); + .GetHandleTable() + .GetObject(system.Kernel(), das_handle); R_UNLESS(das.IsNotNull(), ResultInvalidHandle); // Attach. @@ -58,8 +61,8 @@ Result AttachDeviceAddressSpace(Core::System& system, DeviceName device_name, Ha Result DetachDeviceAddressSpace(Core::System& system, DeviceName device_name, Handle das_handle) { // Get the device address space. KScopedAutoObject das = GetCurrentProcess(system.Kernel()) - .GetHandleTable() - .GetObject(das_handle); + .GetHandleTable() + .GetObject(system.Kernel(), das_handle); R_UNLESS(das.IsNotNull(), ResultInvalidHandle); // Detach. @@ -99,13 +102,13 @@ Result MapDeviceAddressSpaceByForce(Core::System& system, Handle das_handle, Han // Get the device address space. KScopedAutoObject das = GetCurrentProcess(system.Kernel()) - .GetHandleTable() - .GetObject(das_handle); + .GetHandleTable() + .GetObject(system.Kernel(), das_handle); R_UNLESS(das.IsNotNull(), ResultInvalidHandle); // Get the process. KScopedAutoObject process = - GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject(process_handle); + GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject(system.Kernel(), process_handle); R_UNLESS(process.IsNotNull(), ResultInvalidHandle); // Validate that the process address is within range. @@ -140,13 +143,13 @@ Result MapDeviceAddressSpaceAligned(Core::System& system, Handle das_handle, Han // Get the device address space. KScopedAutoObject das = GetCurrentProcess(system.Kernel()) - .GetHandleTable() - .GetObject(das_handle); + .GetHandleTable() + .GetObject(system.Kernel(), das_handle); R_UNLESS(das.IsNotNull(), ResultInvalidHandle); // Get the process. KScopedAutoObject process = - GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject(process_handle); + GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject(system.Kernel(), process_handle); R_UNLESS(process.IsNotNull(), ResultInvalidHandle); // Validate that the process address is within range. @@ -172,13 +175,12 @@ Result UnmapDeviceAddressSpace(Core::System& system, Handle das_handle, Handle p // Get the device address space. KScopedAutoObject das = GetCurrentProcess(system.Kernel()) - .GetHandleTable() - .GetObject(das_handle); + .GetHandleTable() + .GetObject(system.Kernel(), das_handle); R_UNLESS(das.IsNotNull(), ResultInvalidHandle); // Get the process. - KScopedAutoObject process = - GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject(process_handle); + KScopedAutoObject process = GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject(system.Kernel(), process_handle); R_UNLESS(process.IsNotNull(), ResultInvalidHandle); // Validate that the process address is within range. diff --git a/src/core/hle/kernel/svc/svc_event.cpp b/src/core/hle/kernel/svc/svc_event.cpp index 586cb2b14e..36de3b9096 100644 --- a/src/core/hle/kernel/svc/svc_event.cpp +++ b/src/core/hle/kernel/svc/svc_event.cpp @@ -22,9 +22,9 @@ Result SignalEvent(Core::System& system, Handle event_handle) { // Fail-safe for system applets const auto program_id = GetCurrentProcess(system.Kernel()).GetProgramId(); if ((program_id & 0xFFFFFFFFFFFFFF00ull) == 0x0100000000001000ull) { - KScopedAutoObject event = handle_table.GetObject(event_handle); + KScopedAutoObject event = handle_table.GetObject(system.Kernel(), event_handle); if (event.IsNotNull()) { - event->Signal(); + event->Signal(system.Kernel()); } else { LOG_WARNING(Kernel_SVC, "SignalEvent best-effort unknown handle=0x{:08X} (ignored)", event_handle); @@ -34,10 +34,10 @@ Result SignalEvent(Core::System& system, Handle event_handle) { // Get the event. - KScopedAutoObject event = handle_table.GetObject(event_handle); + KScopedAutoObject event = handle_table.GetObject(system.Kernel(), event_handle); R_UNLESS(event.IsNotNull(), ResultInvalidHandle); - R_RETURN(event->Signal()); + R_RETURN(event->Signal(system.Kernel())); } Result ClearEvent(Core::System& system, Handle event_handle) { @@ -48,18 +48,18 @@ Result ClearEvent(Core::System& system, Handle event_handle) { // Try to clear the writable event. { - KScopedAutoObject event = handle_table.GetObject(event_handle); + KScopedAutoObject event = handle_table.GetObject(system.Kernel(), event_handle); if (event.IsNotNull()) { - event->Clear(); + event->Clear(system.Kernel()); R_SUCCEED(); } } // Try to clear the readable event. { - KScopedAutoObject readable_event = handle_table.GetObject(event_handle); + KScopedAutoObject readable_event = handle_table.GetObject(system.Kernel(), event_handle); if (readable_event.IsNotNull()) { - readable_event->Clear(); + readable_event->Clear(system.Kernel()); R_SUCCEED(); } } @@ -71,43 +71,42 @@ Result CreateEvent(Core::System& system, Handle* out_write, Handle* out_read) { LOG_DEBUG(Kernel_SVC, "called"); // Get the kernel reference and handle table. - auto& kernel = system.Kernel(); - auto& handle_table = GetCurrentProcess(kernel).GetHandleTable(); + auto& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable(); // Reserve a new event from the process resource limit - KScopedResourceReservation event_reservation(GetCurrentProcessPointer(kernel), + KScopedResourceReservation event_reservation(system.Kernel(), GetCurrentProcessPointer(system.Kernel()), LimitableResource::EventCountMax); R_UNLESS(event_reservation.Succeeded(), ResultLimitReached); // Create a new event. - KEvent* event = KEvent::Create(kernel); + KEvent* event = KEvent::Create(system.Kernel()); R_UNLESS(event != nullptr, ResultOutOfResource); // Initialize the event. - event->Initialize(GetCurrentProcessPointer(kernel)); + event->Initialize(system.Kernel(), GetCurrentProcessPointer(system.Kernel())); // Commit the thread reservation. event_reservation.Commit(); // Ensure that we clean up the event (and its only references are handle table) on function end. SCOPE_EXIT { - event->GetReadableEvent().Close(); - event->Close(); + event->GetReadableEvent().Close(system.Kernel()); + event->Close(system.Kernel()); }; // Register the event. - KEvent::Register(kernel, event); + KEvent::Register(system.Kernel(), event); // Add the event to the handle table. - R_TRY(handle_table.Add(out_write, event)); + R_TRY(handle_table.Add(system.Kernel(), out_write, event)); // Ensure that we maintain a clean handle state on exit. ON_RESULT_FAILURE { - handle_table.Remove(*out_write); + handle_table.Remove(system.Kernel(), *out_write); }; // Add the readable event to the handle table. - R_RETURN(handle_table.Add(out_read, std::addressof(event->GetReadableEvent()))); + R_RETURN(handle_table.Add(system.Kernel(), out_read, std::addressof(event->GetReadableEvent()))); } Result SignalEvent64(Core::System& system, Handle event_handle) { diff --git a/src/core/hle/kernel/svc/svc_exception.cpp b/src/core/hle/kernel/svc/svc_exception.cpp index 47b7568288..e1b0fa7b95 100644 --- a/src/core/hle/kernel/svc/svc_exception.cpp +++ b/src/core/hle/kernel/svc/svc_exception.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -103,16 +106,14 @@ void Break(Core::System& system, BreakReason reason, u64 info1, u64 info2) { handle_debug_buffer(info1, info2); - system.CurrentPhysicalCore().LogBacktrace(); + system.CurrentPhysicalCore().LogBacktrace(system.Kernel()); } - const bool is_hbl = GetCurrentProcess(system.Kernel()).IsHbl(); - const bool should_break = is_hbl || !notification_only; - + const bool should_break = !notification_only; if (system.DebuggerEnabled() && should_break) { auto* thread = system.Kernel().GetCurrentEmuThread(); system.GetDebugger().NotifyThreadStopped(thread); - thread->RequestSuspend(Kernel::SuspendType::Debug); + thread->RequestSuspend(system.Kernel(), Kernel::SuspendType::Debug); } } diff --git a/src/core/hle/kernel/svc/svc_info.cpp b/src/core/hle/kernel/svc/svc_info.cpp index 9e651e1171..62f203476e 100644 --- a/src/core/hle/kernel/svc/svc_info.cpp +++ b/src/core/hle/kernel/svc/svc_info.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project @@ -46,7 +46,7 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle R_UNLESS(info_sub_id == 0, ResultInvalidEnumValue); const auto& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable(); - KScopedAutoObject process = handle_table.GetObject(handle); + KScopedAutoObject process = handle_table.GetObject(system.Kernel(), handle); R_UNLESS(process.IsNotNull(), ResultInvalidHandle); switch (info_id_type) { @@ -90,11 +90,11 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle R_SUCCEED(); case InfoType::TotalMemorySize: - *result = process->GetTotalUserPhysicalMemorySize(); + *result = process->GetTotalUserPhysicalMemorySize(system.Kernel()); R_SUCCEED(); case InfoType::UsedMemorySize: - *result = process->GetUsedUserPhysicalMemorySize(); + *result = process->GetUsedUserPhysicalMemorySize(system.Kernel()); R_SUCCEED(); case InfoType::SystemResourceSizeTotal: @@ -114,11 +114,11 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle R_SUCCEED(); case InfoType::TotalNonSystemMemorySize: - *result = process->GetTotalNonSystemUserPhysicalMemorySize(); + *result = process->GetTotalNonSystemUserPhysicalMemorySize(system.Kernel()); R_SUCCEED(); case InfoType::UsedNonSystemMemorySize: - *result = process->GetUsedNonSystemUserPhysicalMemorySize(); + *result = process->GetUsedNonSystemUserPhysicalMemorySize(system.Kernel()); R_SUCCEED(); case InfoType::IsApplication: @@ -175,7 +175,7 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle } Handle resource_handle{}; - R_TRY(handle_table.Add(std::addressof(resource_handle), resource_limit)); + R_TRY(handle_table.Add(system.Kernel(), std::addressof(resource_handle), resource_limit)); *result = resource_handle; R_SUCCEED(); @@ -203,8 +203,8 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle } KScopedAutoObject thread = GetCurrentProcess(system.Kernel()) - .GetHandleTable() - .GetObject(static_cast(handle)); + .GetHandleTable() + .GetObject(system.Kernel(), Handle(handle)); if (thread.IsNull()) { LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", static_cast(handle)); @@ -256,7 +256,7 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle // Get a new handle for the current process. Handle tmp; - R_TRY(handle_table.Add(std::addressof(tmp), current_process)); + R_TRY(handle_table.Add(system.Kernel(), std::addressof(tmp), current_process)); // Set the output. *result = tmp; diff --git a/src/core/hle/kernel/svc/svc_ipc.cpp b/src/core/hle/kernel/svc/svc_ipc.cpp index db912b71fb..875590d6f7 100644 --- a/src/core/hle/kernel/svc/svc_ipc.cpp +++ b/src/core/hle/kernel/svc/svc_ipc.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project @@ -20,19 +20,17 @@ namespace Kernel::Svc { namespace { -Result SendSyncRequestImpl(KernelCore& kernel, uintptr_t message, size_t buffer_size, - Handle session_handle) { +Result SendSyncRequestImpl(KernelCore& kernel, uintptr_t message, size_t buffer_size, Handle session_handle) { // Get the client session. - KScopedAutoObject session = - GetCurrentProcess(kernel).GetHandleTable().GetObject(session_handle); + KScopedAutoObject session = GetCurrentProcess(kernel).GetHandleTable().GetObject(kernel, session_handle); R_UNLESS(session.IsNotNull(), ResultInvalidHandle); // Get the parent, and persist a reference to it until we're done. - KScopedAutoObject parent = session->GetParent(); + KScopedAutoObject parent = {kernel, session->GetParent()}; ASSERT(parent.IsNotNull()); // Send the request. - R_RETURN(session->SendSyncRequest(message, buffer_size)); + R_RETURN(session->SendSyncRequest(kernel, message, buffer_size)); } Result ReplyAndReceiveImpl(KernelCore& kernel, int32_t* out_index, uintptr_t message, @@ -41,8 +39,7 @@ Result ReplyAndReceiveImpl(KernelCore& kernel, int32_t* out_index, uintptr_t mes int64_t timeout_ns) { // Reply to the target, if one is specified. if (reply_target != InvalidHandle) { - KScopedAutoObject session = - GetCurrentProcess(kernel).GetHandleTable().GetObject(reply_target); + KScopedAutoObject session = GetCurrentProcess(kernel).GetHandleTable().GetObject(kernel, reply_target); R_UNLESS(session.IsNotNull(), ResultInvalidHandle); // If we fail to reply, we want to set the output index to -1. @@ -51,7 +48,7 @@ Result ReplyAndReceiveImpl(KernelCore& kernel, int32_t* out_index, uintptr_t mes }; // Send the reply. - R_TRY(session->SendReply(message, buffer_size, message_paddr)); + R_TRY(session->SendReply(kernel, message, buffer_size, message_paddr)); } // Receive a message. @@ -77,8 +74,7 @@ Result ReplyAndReceiveImpl(KernelCore& kernel, int32_t* out_index, uintptr_t mes while (true) { // Wait for an object. s32 index; - Result result = KSynchronizationObject::Wait(kernel, std::addressof(index), objs, - num_objects, timeout); + Result result = KSynchronizationObject::Wait(kernel, std::addressof(index), objs, num_objects, timeout); if (ResultTimedOut == result) { R_THROW(result); } @@ -87,7 +83,7 @@ Result ReplyAndReceiveImpl(KernelCore& kernel, int32_t* out_index, uintptr_t mes if (R_SUCCEEDED(result)) { KServerSession* session = objs[index]->DynamicCast(); if (session != nullptr) { - result = session->ReceiveRequest(message, buffer_size, message_paddr); + result = session->ReceiveRequest(kernel, message, buffer_size, message_paddr); if (ResultNotFound == result) { continue; } @@ -127,14 +123,14 @@ Result ReplyAndReceiveImpl(KernelCore& kernel, int32_t* out_index, uintptr_t mes // Convert the handles to objects. R_UNLESS( - handle_table.GetMultipleObjects(objs, handles, num_handles), + handle_table.GetMultipleObjects(kernel, objs, handles, num_handles), ResultInvalidHandle); } // Ensure handles are closed when we're done. SCOPE_EXIT { for (auto i = 0; i < num_handles; ++i) { - objs[i]->Close(); + objs[i]->Close(kernel); } }; @@ -188,16 +184,15 @@ Result SendAsyncRequestWithUserBuffer(Core::System& system, Handle* out_event_ha auto& handle_table = process.GetHandleTable(); // Reserve a new event from the process resource limit. - KScopedResourceReservation event_reservation(std::addressof(process), - Svc::LimitableResource::EventCountMax); + KScopedResourceReservation event_reservation(system.Kernel(), std::addressof(process), Svc::LimitableResource::EventCountMax); R_UNLESS(event_reservation.Succeeded(), ResultLimitReached); // Get the client session. - KScopedAutoObject session = process.GetHandleTable().GetObject(session_handle); + KScopedAutoObject session = process.GetHandleTable().GetObject(system.Kernel(), session_handle); R_UNLESS(session.IsNotNull(), ResultInvalidHandle); // Get the parent, and persist a reference to it until we're done. - KScopedAutoObject parent = session->GetParent(); + KScopedAutoObject parent = {system.Kernel(), session->GetParent()}; ASSERT(parent.IsNotNull()); // Create a new event. @@ -205,30 +200,30 @@ Result SendAsyncRequestWithUserBuffer(Core::System& system, Handle* out_event_ha R_UNLESS(event != nullptr, ResultOutOfResource); // Initialize the event. - event->Initialize(std::addressof(process)); + event->Initialize(system.Kernel(), std::addressof(process)); // Commit our reservation. event_reservation.Commit(); // At end of scope, kill the standing references to the sub events. SCOPE_EXIT { - event->GetReadableEvent().Close(); - event->Close(); + event->GetReadableEvent().Close(system.Kernel()); + event->Close(system.Kernel()); }; // Register the event. KEvent::Register(system.Kernel(), event); // Add the readable event to the handle table. - R_TRY(handle_table.Add(out_event_handle, std::addressof(event->GetReadableEvent()))); + R_TRY(handle_table.Add(system.Kernel(), out_event_handle, std::addressof(event->GetReadableEvent()))); // Ensure that if we fail to send the request, we close the readable handle. ON_RESULT_FAILURE { - handle_table.Remove(*out_event_handle); + handle_table.Remove(system.Kernel(), *out_event_handle); }; // Send the async request. - R_RETURN(session->SendAsyncRequest(event, message, buffer_size)); + R_RETURN(session->SendAsyncRequest(system.Kernel(), event, message, buffer_size)); } Result ReplyAndReceive(Core::System& system, s32* out_index, uint64_t handles, s32 num_handles, diff --git a/src/core/hle/kernel/svc/svc_light_ipc.cpp b/src/core/hle/kernel/svc/svc_light_ipc.cpp index 4772cbda17..ae3ff85a88 100644 --- a/src/core/hle/kernel/svc/svc_light_ipc.cpp +++ b/src/core/hle/kernel/svc/svc_light_ipc.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -14,12 +17,12 @@ namespace Kernel::Svc { Result SendSyncRequestLight(Core::System& system, Handle session_handle, u32* args) { // Get the light client session from its handle. KScopedAutoObject session = GetCurrentProcess(system.Kernel()) - .GetHandleTable() - .GetObject(session_handle); + .GetHandleTable() + .GetObject(system.Kernel(), session_handle); R_UNLESS(session.IsNotNull(), ResultInvalidHandle); // Send the request. - R_TRY(session->SendSyncRequest(args)); + R_TRY(session->SendSyncRequest(system.Kernel(), args)); R_SUCCEED(); } @@ -27,12 +30,12 @@ Result SendSyncRequestLight(Core::System& system, Handle session_handle, u32* ar Result ReplyAndReceiveLight(Core::System& system, Handle session_handle, u32* args) { // Get the light server session from its handle. KScopedAutoObject session = GetCurrentProcess(system.Kernel()) - .GetHandleTable() - .GetObject(session_handle); + .GetHandleTable() + .GetObject(system.Kernel(), session_handle); R_UNLESS(session.IsNotNull(), ResultInvalidHandle); // Handle the request. - R_TRY(session->ReplyAndReceive(args)); + R_TRY(session->ReplyAndReceive(system.Kernel(), args)); R_SUCCEED(); } diff --git a/src/core/hle/kernel/svc/svc_port.cpp b/src/core/hle/kernel/svc/svc_port.cpp index 9a22dadaf7..bddd57e8f9 100644 --- a/src/core/hle/kernel/svc/svc_port.cpp +++ b/src/core/hle/kernel/svc/svc_port.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -33,18 +36,18 @@ Result ConnectToNamedPort(Core::System& system, Handle* out, u64 user_name) { // Reserve a handle for the port. // NOTE: Nintendo really does write directly to the output handle here. - R_TRY(handle_table.Reserve(out)); + R_TRY(handle_table.Reserve(system.Kernel(), out)); ON_RESULT_FAILURE { - handle_table.Unreserve(*out); + handle_table.Unreserve(system.Kernel(), *out); }; // Create a session. KClientSession* session; - R_TRY(port->CreateSession(std::addressof(session))); + R_TRY(port->CreateSession(system.Kernel(), std::addressof(session))); // Register the session in the table, close the extra reference. - handle_table.Register(*out, session); - session->Close(); + handle_table.Register(system.Kernel(), *out, session); + session->Close(system.Kernel()); // We succeeded. R_SUCCEED(); @@ -65,27 +68,27 @@ Result CreatePort(Core::System& system, Handle* out_server, Handle* out_client, R_UNLESS(port != nullptr, ResultOutOfResource); // Initialize the port. - port->Initialize(max_sessions, is_light, name); + port->Initialize(system.Kernel(), max_sessions, is_light, name); // Ensure that we clean up the port (and its only references are handle table) on function end. SCOPE_EXIT { - port->GetServerPort().Close(); - port->GetClientPort().Close(); + port->GetServerPort().Close(system.Kernel()); + port->GetClientPort().Close(system.Kernel()); }; // Register the port. KPort::Register(kernel, port); // Add the client to the handle table. - R_TRY(handle_table.Add(out_client, std::addressof(port->GetClientPort()))); + R_TRY(handle_table.Add(system.Kernel(), out_client, std::addressof(port->GetClientPort()))); // Ensure that we maintain a clean handle state on exit. ON_RESULT_FAILURE { - handle_table.Remove(*out_client); + handle_table.Remove(system.Kernel(), *out_client); }; // Add the server to the handle table. - R_RETURN(handle_table.Add(out_server, std::addressof(port->GetServerPort()))); + R_RETURN(handle_table.Add(system.Kernel(), out_server, std::addressof(port->GetServerPort()))); } Result ConnectToPort(Core::System& system, Handle* out, Handle port) { @@ -93,36 +96,33 @@ Result ConnectToPort(Core::System& system, Handle* out, Handle port) { auto& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable(); // Get the client port. - KScopedAutoObject client_port = handle_table.GetObject(port); + KScopedAutoObject client_port = handle_table.GetObject(system.Kernel(), port); R_UNLESS(client_port.IsNotNull(), ResultInvalidHandle); // Reserve a handle for the port. // NOTE: Nintendo really does write directly to the output handle here. - R_TRY(handle_table.Reserve(out)); + R_TRY(handle_table.Reserve(system.Kernel(), out)); ON_RESULT_FAILURE { - handle_table.Unreserve(*out); + handle_table.Unreserve(system.Kernel(), *out); }; // Create the session. KAutoObject* session; - if (client_port->IsLight()) { - R_TRY(client_port->CreateLightSession( - reinterpret_cast(std::addressof(session)))); + if (client_port->IsLight(system.Kernel())) { + R_TRY(client_port->CreateLightSession(system.Kernel(), reinterpret_cast(std::addressof(session)))); } else { - R_TRY(client_port->CreateSession( - reinterpret_cast(std::addressof(session)))); + R_TRY(client_port->CreateSession(system.Kernel(), reinterpret_cast(std::addressof(session)))); } // Register the session. - handle_table.Register(*out, session); - session->Close(); + handle_table.Register(system.Kernel(), *out, session); + session->Close(system.Kernel()); // We succeeded. R_SUCCEED(); } -Result ManageNamedPort(Core::System& system, Handle* out_server_handle, uint64_t user_name, - int32_t max_sessions) { +Result ManageNamedPort(Core::System& system, Handle* out_server_handle, uint64_t user_name, int32_t max_sessions) { // Copy the provided name from user memory to kernel memory. auto string_name = GetCurrentMemory(system.Kernel()).ReadCString(user_name, KObjectName::NameLengthMax); @@ -144,21 +144,21 @@ Result ManageNamedPort(Core::System& system, Handle* out_server_handle, uint64_t R_UNLESS(port != nullptr, ResultOutOfResource); // Initialize the new port. - port->Initialize(max_sessions, false, 0); + port->Initialize(system.Kernel(), max_sessions, false, 0); // Register the port. KPort::Register(system.Kernel(), port); // Ensure that our only reference to the port is in the handle table when we're done. SCOPE_EXIT { - port->GetClientPort().Close(); - port->GetServerPort().Close(); + port->GetClientPort().Close(system.Kernel()); + port->GetServerPort().Close(system.Kernel()); }; // Register the handle in the table. - R_TRY(handle_table.Add(out_server_handle, std::addressof(port->GetServerPort()))); + R_TRY(handle_table.Add(system.Kernel(), out_server_handle, std::addressof(port->GetServerPort()))); ON_RESULT_FAILURE { - handle_table.Remove(*out_server_handle); + handle_table.Remove(system.Kernel(), *out_server_handle); }; // Create a new object name. diff --git a/src/core/hle/kernel/svc/svc_process.cpp b/src/core/hle/kernel/svc/svc_process.cpp index 6d63892a94..dc2566e85a 100644 --- a/src/core/hle/kernel/svc/svc_process.cpp +++ b/src/core/hle/kernel/svc/svc_process.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project @@ -27,8 +27,8 @@ Result GetProcessId(Core::System& system, u64* out_process_id, Handle handle) { // Get the object from the handle table. KScopedAutoObject obj = GetCurrentProcess(system.Kernel()) - .GetHandleTable() - .GetObject(static_cast(handle)); + .GetHandleTable() + .GetObject(system.Kernel(), Handle(handle)); R_UNLESS(obj.IsNotNull(), ResultInvalidHandle); // Get the process from the object. @@ -98,7 +98,7 @@ Result GetProcessInfo(Core::System& system, s64* out, Handle process_handle, LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type={:#X}", process_handle, info_type); const auto& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable(); - KScopedAutoObject process = handle_table.GetObject(process_handle); + KScopedAutoObject process = handle_table.GetObject(system.Kernel(), process_handle); if (process.IsNull()) { LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}", process_handle); diff --git a/src/core/hle/kernel/svc/svc_process_memory.cpp b/src/core/hle/kernel/svc/svc_process_memory.cpp index 3313118dfa..0c18be91ba 100644 --- a/src/core/hle/kernel/svc/svc_process_memory.cpp +++ b/src/core/hle/kernel/svc/svc_process_memory.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project @@ -48,7 +48,7 @@ Result SetProcessMemoryPermission(Core::System& system, Handle process_handle, u // Get the process from its handle. KScopedAutoObject process = - GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject(process_handle); + GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject(system.Kernel(), process_handle); R_UNLESS(process.IsNotNull(), ResultInvalidHandle); // Validate that the address is in range. @@ -76,7 +76,7 @@ Result MapProcessMemory(Core::System& system, u64 dst_address, Handle process_ha // Get the processes. KProcess* dst_process = GetCurrentProcessPointer(system.Kernel()); KScopedAutoObject src_process = - dst_process->GetHandleTable().GetObjectWithoutPseudoHandle(process_handle); + dst_process->GetHandleTable().GetObjectWithoutPseudoHandle(system.Kernel(), process_handle); R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle); // Get the page tables. @@ -117,7 +117,7 @@ Result UnmapProcessMemory(Core::System& system, u64 dst_address, Handle process_ // Get the processes. KProcess* dst_process = GetCurrentProcessPointer(system.Kernel()); KScopedAutoObject src_process = - dst_process->GetHandleTable().GetObjectWithoutPseudoHandle(process_handle); + dst_process->GetHandleTable().GetObjectWithoutPseudoHandle(system.Kernel(), process_handle); R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle); // Get the page tables. @@ -174,7 +174,7 @@ Result MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst } const auto& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable(); - KScopedAutoObject process = handle_table.GetObject(process_handle); + KScopedAutoObject process = handle_table.GetObject(system.Kernel(), process_handle); if (process.IsNull()) { LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).", process_handle); @@ -234,7 +234,7 @@ Result UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 d } const auto& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable(); - KScopedAutoObject process = handle_table.GetObject(process_handle); + KScopedAutoObject process = handle_table.GetObject(system.Kernel(), process_handle); if (process.IsNull()) { LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).", process_handle); diff --git a/src/core/hle/kernel/svc/svc_query_memory.cpp b/src/core/hle/kernel/svc/svc_query_memory.cpp index 816dcb8d0f..ad7a667bb5 100644 --- a/src/core/hle/kernel/svc/svc_query_memory.cpp +++ b/src/core/hle/kernel/svc/svc_query_memory.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -23,7 +26,7 @@ Result QueryProcessMemory(Core::System& system, uint64_t out_memory_info, PageIn Handle process_handle, uint64_t address) { LOG_TRACE(Kernel_SVC, "called process=0x{:08X} address={:X}", process_handle, address); const auto& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable(); - KScopedAutoObject process = handle_table.GetObject(process_handle); + KScopedAutoObject process = handle_table.GetObject(system.Kernel(), process_handle); if (process.IsNull()) { LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}", process_handle); diff --git a/src/core/hle/kernel/svc/svc_resource_limit.cpp b/src/core/hle/kernel/svc/svc_resource_limit.cpp index 6f3972482f..7f78aa8b10 100644 --- a/src/core/hle/kernel/svc/svc_resource_limit.cpp +++ b/src/core/hle/kernel/svc/svc_resource_limit.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -19,7 +22,7 @@ Result CreateResourceLimit(Core::System& system, Handle* out_handle) { // Ensure we don't leak a reference to the limit. SCOPE_EXIT { - resource_limit->Close(); + resource_limit->Close(system.Kernel()); }; // Initialize the resource limit. @@ -29,7 +32,7 @@ Result CreateResourceLimit(Core::System& system, Handle* out_handle) { KResourceLimit::Register(kernel, resource_limit); // Add the limit to the handle table. - R_RETURN(GetCurrentProcess(kernel).GetHandleTable().Add(out_handle, resource_limit)); + R_RETURN(GetCurrentProcess(kernel).GetHandleTable().Add(system.Kernel(), out_handle, resource_limit)); } Result GetResourceLimitLimitValue(Core::System& system, s64* out_limit_value, @@ -42,8 +45,8 @@ Result GetResourceLimitLimitValue(Core::System& system, s64* out_limit_value, // Get the resource limit. KScopedAutoObject resource_limit = GetCurrentProcess(system.Kernel()) - .GetHandleTable() - .GetObject(resource_limit_handle); + .GetHandleTable() + .GetObject(system.Kernel(), resource_limit_handle); R_UNLESS(resource_limit.IsNotNull(), ResultInvalidHandle); // Get the limit value. @@ -62,8 +65,8 @@ Result GetResourceLimitCurrentValue(Core::System& system, s64* out_current_value // Get the resource limit. KScopedAutoObject resource_limit = GetCurrentProcess(system.Kernel()) - .GetHandleTable() - .GetObject(resource_limit_handle); + .GetHandleTable() + .GetObject(system.Kernel(), resource_limit_handle); R_UNLESS(resource_limit.IsNotNull(), ResultInvalidHandle); // Get the current value. @@ -83,7 +86,7 @@ Result SetResourceLimitLimitValue(Core::System& system, Handle resource_limit_ha // Get the resource limit. KScopedAutoObject resource_limit = GetCurrentProcess(system.Kernel()) .GetHandleTable() - .GetObject(resource_limit_handle); + .GetObject(system.Kernel(), resource_limit_handle); R_UNLESS(resource_limit.IsNotNull(), ResultInvalidHandle); // Set the limit value. diff --git a/src/core/hle/kernel/svc/svc_session.cpp b/src/core/hle/kernel/svc/svc_session.cpp index b034d21d15..4d04f0c5a3 100644 --- a/src/core/hle/kernel/svc/svc_session.cpp +++ b/src/core/hle/kernel/svc/svc_session.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -23,7 +26,7 @@ Result CreateSession(Core::System& system, Handle* out_server, Handle* out_clien // Reserve a new session from the process resource limit. // TODO: Dynamic resource limits - KScopedResourceReservation session_reservation(std::addressof(process), + KScopedResourceReservation session_reservation(system.Kernel(), std::addressof(process), LimitableResource::SessionCountMax); if (session_reservation.Succeeded()) { session = T::Create(system.Kernel()); @@ -62,7 +65,7 @@ Result CreateSession(Core::System& system, Handle* out_server, Handle* out_clien R_UNLESS(session != nullptr, ResultOutOfResource); // Initialize the session. - session->Initialize(nullptr, name); + session->Initialize(system.Kernel(), nullptr, name); // Commit the session reservation. session_reservation.Commit(); @@ -70,23 +73,23 @@ Result CreateSession(Core::System& system, Handle* out_server, Handle* out_clien // Ensure that we clean up the session (and its only references are handle table) on function // end. SCOPE_EXIT { - session->GetClientSession().Close(); - session->GetServerSession().Close(); + session->GetClientSession().Close(system.Kernel()); + session->GetServerSession().Close(system.Kernel()); }; // Register the session. T::Register(system.Kernel(), session); // Add the server session to the handle table. - R_TRY(handle_table.Add(out_server, std::addressof(session->GetServerSession()))); + R_TRY(handle_table.Add(system.Kernel(), out_server, std::addressof(session->GetServerSession()))); // Ensure that we maintain a clean handle state on exit. ON_RESULT_FAILURE { - handle_table.Remove(*out_server); + handle_table.Remove(system.Kernel(), *out_server); }; // Add the client session to the handle table. - R_RETURN(handle_table.Add(out_client, std::addressof(session->GetClientSession()))); + R_RETURN(handle_table.Add(system.Kernel(), out_client, std::addressof(session->GetClientSession()))); } } // namespace @@ -105,29 +108,29 @@ Result AcceptSession(Core::System& system, Handle* out, Handle port_handle) { auto& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable(); // Get the server port. - KScopedAutoObject port = handle_table.GetObject(port_handle); + KScopedAutoObject port = handle_table.GetObject(system.Kernel(), port_handle); R_UNLESS(port.IsNotNull(), ResultInvalidHandle); // Reserve an entry for the new session. - R_TRY(handle_table.Reserve(out)); + R_TRY(handle_table.Reserve(system.Kernel(), out)); ON_RESULT_FAILURE { - handle_table.Unreserve(*out); + handle_table.Unreserve(system.Kernel(), *out); }; // Accept the session. KAutoObject* session; - if (port->IsLight()) { - session = port->AcceptLightSession(); + if (port->IsLight(system.Kernel())) { + session = port->AcceptLightSession(system.Kernel()); } else { - session = port->AcceptSession(); + session = port->AcceptSession(system.Kernel()); } // Ensure we accepted successfully. R_UNLESS(session != nullptr, ResultNotFound); // Register the session. - handle_table.Register(*out, session); - session->Close(); + handle_table.Register(system.Kernel(), *out, session); + session->Close(system.Kernel()); R_SUCCEED(); } diff --git a/src/core/hle/kernel/svc/svc_shared_memory.cpp b/src/core/hle/kernel/svc/svc_shared_memory.cpp index 3ca07abe8b..66cf5902ec 100644 --- a/src/core/hle/kernel/svc/svc_shared_memory.cpp +++ b/src/core/hle/kernel/svc/svc_shared_memory.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project @@ -49,18 +49,18 @@ Result MapSharedMemory(Core::System& system, Handle shmem_handle, u64 address, u auto& page_table = process.GetPageTable(); // Get the shared memory. - KScopedAutoObject shmem = process.GetHandleTable().GetObject(shmem_handle); + KScopedAutoObject shmem = process.GetHandleTable().GetObject(system.Kernel(), shmem_handle); R_UNLESS(shmem.IsNotNull(), ResultInvalidHandle); // Verify that the mapping is in range. R_UNLESS(page_table.CanContain(address, size, KMemoryState::Shared), ResultInvalidMemoryRegion); // Add the shared memory to the process. - R_TRY(process.AddSharedMemory(shmem.GetPointerUnsafe(), address, size)); + R_TRY(process.AddSharedMemory(system.Kernel(), shmem.GetPointerUnsafe(), address, size)); // Ensure that we clean up the shared memory if we fail to map it. ON_RESULT_FAILURE { - process.RemoveSharedMemory(shmem.GetPointerUnsafe(), address, size); + process.RemoveSharedMemory(system.Kernel(), shmem.GetPointerUnsafe(), address, size); }; // Map the shared memory. @@ -79,7 +79,7 @@ Result UnmapSharedMemory(Core::System& system, Handle shmem_handle, u64 address, auto& page_table = process.GetPageTable(); // Get the shared memory. - KScopedAutoObject shmem = process.GetHandleTable().GetObject(shmem_handle); + KScopedAutoObject shmem = process.GetHandleTable().GetObject(system.Kernel(), shmem_handle); R_UNLESS(shmem.IsNotNull(), ResultInvalidHandle); // Verify that the mapping is in range. @@ -89,7 +89,7 @@ Result UnmapSharedMemory(Core::System& system, Handle shmem_handle, u64 address, R_TRY(shmem->Unmap(process, address, size)); // Remove the shared memory from the process. - process.RemoveSharedMemory(shmem.GetPointerUnsafe(), address, size); + process.RemoveSharedMemory(system.Kernel(), shmem.GetPointerUnsafe(), address, size); R_SUCCEED(); } diff --git a/src/core/hle/kernel/svc/svc_synchronization.cpp b/src/core/hle/kernel/svc/svc_synchronization.cpp index fdd4408d4d..1b29fd5865 100644 --- a/src/core/hle/kernel/svc/svc_synchronization.cpp +++ b/src/core/hle/kernel/svc/svc_synchronization.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project @@ -20,7 +20,7 @@ Result CloseHandle(Core::System& system, Handle handle) { LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle); // Remove the handle. - R_UNLESS(GetCurrentProcess(system.Kernel()).GetHandleTable().Remove(handle), + R_UNLESS(GetCurrentProcess(system.Kernel()).GetHandleTable().Remove(system.Kernel(), handle), ResultInvalidHandle); R_SUCCEED(); @@ -35,17 +35,17 @@ Result ResetSignal(Core::System& system, Handle handle) { // Try to reset as readable event. { - KScopedAutoObject readable_event = handle_table.GetObject(handle); + KScopedAutoObject readable_event = handle_table.GetObject(system.Kernel(), handle); if (readable_event.IsNotNull()) { - R_RETURN(readable_event->Reset()); + R_RETURN(readable_event->Reset(system.Kernel())); } } // Try to reset as process. { - KScopedAutoObject process = handle_table.GetObject(handle); + KScopedAutoObject process = handle_table.GetObject(system.Kernel(), handle); if (process.IsNotNull()) { - R_RETURN(process->Reset()); + R_RETURN(process->Reset(system.Kernel())); } } @@ -62,35 +62,30 @@ Result WaitSynchronization(Core::System& system, int32_t* out_index, u64 user_ha R_UNLESS(0 <= num_handles && num_handles <= Svc::ArgumentHandleCountMax, ResultOutOfRange); // Get the synchronization context. - auto& kernel = system.Kernel(); - auto& handle_table = GetCurrentProcess(kernel).GetHandleTable(); - auto objs = GetCurrentThread(kernel).GetSynchronizationObjectBuffer(); - auto handles = GetCurrentThread(kernel).GetHandleBuffer(); + auto& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable(); + auto objs = GetCurrentThread(system.Kernel()).GetSynchronizationObjectBuffer(); + auto handles = GetCurrentThread(system.Kernel()).GetHandleBuffer(); // Copy user handles. if (num_handles > 0) { // Get the handles. - R_UNLESS(GetCurrentMemory(kernel).ReadBlock(user_handles, handles.data(), - sizeof(Handle) * num_handles), - ResultInvalidPointer); + R_UNLESS(GetCurrentMemory(system.Kernel()).ReadBlock(user_handles, handles.data(), sizeof(Handle) * num_handles), ResultInvalidPointer); // Convert the handles to objects. - R_UNLESS(handle_table.GetMultipleObjects( - objs.data(), handles.data(), num_handles), - ResultInvalidHandle); + R_UNLESS(handle_table.GetMultipleObjects(system.Kernel(), objs.data(), handles.data(), num_handles), ResultInvalidHandle); } // Ensure handles are closed when we're done. SCOPE_EXIT { for (auto i = 0; i < num_handles; ++i) { - objs[i]->Close(); + objs[i]->Close(system.Kernel()); } }; // Convert the timeout from nanoseconds to ticks. s64 timeout; if (timeout_ns > 0) { - u64 ticks = kernel.HardwareTimer().GetTick(); + u64 ticks = system.Kernel().HardwareTimer().GetTick(); ticks += timeout_ns; ticks += 2; @@ -100,7 +95,7 @@ Result WaitSynchronization(Core::System& system, int32_t* out_index, u64 user_ha } // Wait on the objects. - Result res = KSynchronizationObject::Wait(kernel, out_index, objs.data(), num_handles, timeout); + Result res = KSynchronizationObject::Wait(system.Kernel(), out_index, objs.data(), num_handles, timeout); R_SUCCEED_IF(res == ResultSessionClosed); R_RETURN(res); @@ -111,31 +106,28 @@ Result CancelSynchronization(Core::System& system, Handle handle) { LOG_TRACE(Kernel_SVC, "called handle={:#X}", handle); // Get the thread from its handle. - KScopedAutoObject thread = - GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject(handle); + KScopedAutoObject thread = GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject(system.Kernel(), handle); R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); // Cancel the thread's wait. - thread->WaitCancel(); + thread->WaitCancel(system.Kernel()); R_SUCCEED(); } void SynchronizePreemptionState(Core::System& system) { - auto& kernel = system.Kernel(); - // Lock the scheduler. - KScopedSchedulerLock sl{kernel}; + KScopedSchedulerLock sl{system.Kernel()}; // If the current thread is pinned, unpin it. - KProcess* cur_process = GetCurrentProcessPointer(kernel); - const auto core_id = GetCurrentCoreId(kernel); + KProcess* cur_process = GetCurrentProcessPointer(system.Kernel()); + const auto core_id = GetCurrentCoreId(system.Kernel()); - if (cur_process->GetPinnedThread(core_id) == GetCurrentThreadPointer(kernel)) { + if (cur_process->GetPinnedThread(core_id) == GetCurrentThreadPointer(system.Kernel())) { // Clear the current thread's interrupt flag. - GetCurrentThread(kernel).ClearInterruptFlag(); + GetCurrentThread(system.Kernel()).ClearInterruptFlag(system.Kernel()); // Unpin the current thread. - cur_process->UnpinCurrentThread(); + cur_process->UnpinCurrentThread(system.Kernel()); } } diff --git a/src/core/hle/kernel/svc/svc_thread.cpp b/src/core/hle/kernel/svc/svc_thread.cpp index 1aa6f87c04..b1159020d3 100644 --- a/src/core/hle/kernel/svc/svc_thread.cpp +++ b/src/core/hle/kernel/svc/svc_thread.cpp @@ -46,7 +46,7 @@ Result CreateThread(Core::System& system, Handle* out_handle, u64 entry_point, u R_UNLESS(process.CheckThreadPriority(priority), ResultInvalidPriority); // Reserve a new thread from the process resource limit (waiting up to 100ms). - KScopedResourceReservation thread_reservation(std::addressof(process), + KScopedResourceReservation thread_reservation(system.Kernel(), std::addressof(process), LimitableResource::ThreadCountMax, 1, kernel.HardwareTimer().GetTick() + 100000000); R_UNLESS(thread_reservation.Succeeded(), ResultLimitReached); @@ -55,7 +55,7 @@ Result CreateThread(Core::System& system, Handle* out_handle, u64 entry_point, u KThread* thread = KThread::Create(kernel); R_UNLESS(thread != nullptr, ResultOutOfResource) SCOPE_EXIT { - thread->Close(); + thread->Close(system.Kernel()); }; // Initialize the thread. @@ -69,13 +69,13 @@ Result CreateThread(Core::System& system, Handle* out_handle, u64 entry_point, u thread_reservation.Commit(); // Clone the current fpu status to the new thread. - thread->CloneFpuStatus(); + thread->CloneFpuStatus(system.Kernel()); // Register the new thread. KThread::Register(kernel, thread); // Add the thread to the handle table. - R_TRY(process.GetHandleTable().Add(out_handle, thread)); + R_TRY(process.GetHandleTable().Add(system.Kernel(), out_handle, thread)); // Pass the thread handle to the thread local region. process.GetMemory().Write32(GetInteger(thread->GetTlsAddress()) + 0x110, *out_handle); @@ -88,12 +88,11 @@ Result StartThread(Core::System& system, Handle thread_handle) { LOG_DEBUG(Kernel_SVC, "called thread=0x{:08X}", thread_handle); // Get the thread from its handle. - KScopedAutoObject thread = - GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject(thread_handle); + KScopedAutoObject thread = GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject(system.Kernel(), thread_handle); R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); // Try to start the thread. - R_TRY(thread->Run()); + R_TRY(thread->Run(system.Kernel())); R_SUCCEED(); } @@ -102,7 +101,7 @@ Result StartThread(Core::System& system, Handle thread_handle) { void ExitThread(Core::System& system) { auto* const current_thread = GetCurrentThreadPointer(system.Kernel()); system.GlobalSchedulerContext().RemoveThread(current_thread); - current_thread->Exit(); + current_thread->Exit(system.Kernel()); } /// Sleep the current thread @@ -130,7 +129,7 @@ void SleepThread(Core::System& system, s64 ns) { // Sleep. // NOTE: Nintendo does not check the result of this sleep. - static_cast(GetCurrentThread(kernel).Sleep(timeout)); + static_cast(GetCurrentThread(kernel).Sleep(kernel, timeout)); } else if (yield_type == Svc::YieldType::WithoutCoreMigration) { KScheduler::YieldWithoutCoreMigration(kernel); } else if (yield_type == Svc::YieldType::WithCoreMigration) { @@ -144,27 +143,23 @@ void SleepThread(Core::System& system, s64 ns) { /// Gets the thread context Result GetThreadContext3(Core::System& system, u64 out_context, Handle thread_handle) { - LOG_DEBUG(Kernel_SVC, "called, out_context=0x{:08X}, thread_handle={:#X}", out_context, - thread_handle); - - auto& kernel = system.Kernel(); + LOG_DEBUG(Kernel_SVC, "called, out_context=0x{:08X}, thread_handle={:#X}", out_context, thread_handle); // Get the thread from its handle. - KScopedAutoObject thread = - GetCurrentProcess(kernel).GetHandleTable().GetObject(thread_handle); + KScopedAutoObject thread = GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject(system.Kernel(), thread_handle); R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); // Require the handle be to a non-current thread in the current process. - R_UNLESS(thread->GetOwnerProcess() == GetCurrentProcessPointer(kernel), ResultInvalidHandle); - R_UNLESS(thread.GetPointerUnsafe() != GetCurrentThreadPointer(kernel), ResultBusy); + R_UNLESS(thread->GetOwnerProcess() == GetCurrentProcessPointer(system.Kernel()), ResultInvalidHandle); + R_UNLESS(thread.GetPointerUnsafe() != GetCurrentThreadPointer(system.Kernel()), ResultBusy); // Get the thread context. Svc::ThreadContext context{}; - R_TRY(thread->GetThreadContext3(std::addressof(context))); + R_TRY(thread->GetThreadContext3(system.Kernel(), std::addressof(context))); // Copy the thread context to user space. R_UNLESS( - GetCurrentMemory(kernel).WriteBlock(out_context, std::addressof(context), sizeof(context)), + GetCurrentMemory(system.Kernel()).WriteBlock(out_context, std::addressof(context), sizeof(context)), ResultInvalidPointer); R_SUCCEED(); @@ -175,8 +170,7 @@ Result GetThreadPriority(Core::System& system, s32* out_priority, Handle handle) LOG_TRACE(Kernel_SVC, "called"); // Get the thread from its handle. - KScopedAutoObject thread = - GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject(handle); + KScopedAutoObject thread = GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject(system.Kernel(), handle); R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); // Get the thread's priority. @@ -195,11 +189,11 @@ Result SetThreadPriority(Core::System& system, Handle thread_handle, s32 priorit R_UNLESS(process.CheckThreadPriority(priority), ResultInvalidPriority); // Get the thread from its handle. - KScopedAutoObject thread = process.GetHandleTable().GetObject(thread_handle); + KScopedAutoObject thread = process.GetHandleTable().GetObject(system.Kernel(), thread_handle); R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); // Set the thread priority. - thread->SetBasePriority(priority); + thread->SetBasePriority(system.Kernel(), priority); R_SUCCEED(); } @@ -248,12 +242,11 @@ Result GetThreadCoreMask(Core::System& system, s32* out_core_id, u64* out_affini LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle); // Get the thread from its handle. - KScopedAutoObject thread = - GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject(thread_handle); + KScopedAutoObject thread = GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject(system.Kernel(), thread_handle); R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); // Get the core mask. - R_RETURN(thread->GetCoreMask(out_core_id, out_affinity_mask)); + R_RETURN(thread->GetCoreMask(system.Kernel(), out_core_id, out_affinity_mask)); } Result SetThreadCoreMask(Core::System& system, Handle thread_handle, s32 core_id, @@ -279,18 +272,17 @@ Result SetThreadCoreMask(Core::System& system, Handle thread_handle, s32 core_id // Get the thread from its handle. KScopedAutoObject thread = - GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject(thread_handle); + GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject(system.Kernel(), thread_handle); R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); // Set the core mask. - R_RETURN(thread->SetCoreMask(core_id, affinity_mask)); + R_RETURN(thread->SetCoreMask(system.Kernel(), core_id, affinity_mask)); } /// Get the ID for the specified thread. Result GetThreadId(Core::System& system, u64* out_thread_id, Handle thread_handle) { // Get the thread from its handle. - KScopedAutoObject thread = - GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject(thread_handle); + KScopedAutoObject thread = GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject(system.Kernel(), thread_handle); R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); // Get the thread's id. diff --git a/src/core/hle/kernel/svc/svc_transfer_memory.cpp b/src/core/hle/kernel/svc/svc_transfer_memory.cpp index 2ea0d44215..e539eee911 100644 --- a/src/core/hle/kernel/svc/svc_transfer_memory.cpp +++ b/src/core/hle/kernel/svc/svc_transfer_memory.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -43,8 +46,7 @@ Result CreateTransferMemory(Core::System& system, Handle* out, u64 address, u64 auto& handle_table = process.GetHandleTable(); // Reserve a new transfer memory from the process resource limit. - KScopedResourceReservation trmem_reservation(std::addressof(process), - LimitableResource::TransferMemoryCountMax); + KScopedResourceReservation trmem_reservation(system.Kernel(), std::addressof(process), LimitableResource::TransferMemoryCountMax); R_UNLESS(trmem_reservation.Succeeded(), ResultLimitReached); // Create the transfer memory. @@ -53,14 +55,14 @@ Result CreateTransferMemory(Core::System& system, Handle* out, u64 address, u64 // Ensure the only reference is in the handle table when we're done. SCOPE_EXIT { - trmem->Close(); + trmem->Close(system.Kernel()); }; // Ensure that the region is in range. R_UNLESS(process.GetPageTable().Contains(address, size), ResultInvalidCurrentMemory); // Initialize the transfer memory. - R_TRY(trmem->Initialize(address, size, map_perm)); + R_TRY(trmem->Initialize(system.Kernel(), address, size, map_perm)); // Commit the reservation. trmem_reservation.Commit(); @@ -69,7 +71,7 @@ Result CreateTransferMemory(Core::System& system, Handle* out, u64 address, u64 KTransferMemory::Register(kernel, trmem); // Add the transfer memory to the handle table. - R_RETURN(handle_table.Add(out, trmem)); + R_RETURN(handle_table.Add(system.Kernel(), out, trmem)); } Result MapTransferMemory(Core::System& system, Handle trmem_handle, uint64_t address, uint64_t size, @@ -86,7 +88,7 @@ Result MapTransferMemory(Core::System& system, Handle trmem_handle, uint64_t add // Get the transfer memory. KScopedAutoObject trmem = GetCurrentProcess(system.Kernel()) .GetHandleTable() - .GetObject(trmem_handle); + .GetObject(system.Kernel(), trmem_handle); R_UNLESS(trmem.IsNotNull(), ResultInvalidHandle); // Verify that the mapping is in range. @@ -96,7 +98,7 @@ Result MapTransferMemory(Core::System& system, Handle trmem_handle, uint64_t add ResultInvalidMemoryRegion); // Map the transfer memory. - R_TRY(trmem->Map(address, size, map_perm)); + R_TRY(trmem->Map(system.Kernel(), address, size, map_perm)); // We succeeded. R_SUCCEED(); @@ -113,7 +115,7 @@ Result UnmapTransferMemory(Core::System& system, Handle trmem_handle, uint64_t a // Get the transfer memory. KScopedAutoObject trmem = GetCurrentProcess(system.Kernel()) .GetHandleTable() - .GetObject(trmem_handle); + .GetObject(system.Kernel(), trmem_handle); R_UNLESS(trmem.IsNotNull(), ResultInvalidHandle); // Verify that the mapping is in range. @@ -123,7 +125,7 @@ Result UnmapTransferMemory(Core::System& system, Handle trmem_handle, uint64_t a ResultInvalidMemoryRegion); // Unmap the transfer memory. - R_TRY(trmem->Unmap(address, size)); + R_TRY(trmem->Unmap(system.Kernel(), address, size)); R_SUCCEED(); } diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 06ebb22be3..cfad30b992 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -10,7 +10,6 @@ #include "common/bit_field.h" #include "common/common_funcs.h" #include "common/common_types.h" -#include "common/expected.h" // All the constants in this file come from diff --git a/src/core/hle/service/acc/async_context.cpp b/src/core/hle/service/acc/async_context.cpp index c9e0af90ce..c767e46e8f 100644 --- a/src/core/hle/service/acc/async_context.cpp +++ b/src/core/hle/service/acc/async_context.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -64,7 +67,7 @@ void IAsyncContext::GetResult(HLERequestContext& ctx) { void IAsyncContext::MarkComplete() { is_complete.store(true); - completion_event->Signal(); + completion_event->Signal(system.Kernel()); } } // namespace Service::Account diff --git a/src/core/hle/service/am/applet.cpp b/src/core/hle/service/am/applet.cpp index 2ae3102aff..efedcc5692 100644 --- a/src/core/hle/service/am/applet.cpp +++ b/src/core/hle/service/am/applet.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator @@ -52,7 +52,7 @@ void Applet::UpdateSuspensionStateLocked(bool force_message) { // Signal if the focus state was changed or the process state was changed. if (update_requested_focus_state || was_changed || force_message) { - lifecycle_manager.SignalSystemEventIfNeeded(); + lifecycle_manager.SignalSystemEventIfNeeded(context.kernel); } } @@ -76,7 +76,7 @@ void Applet::SetInteractibleLocked(bool interactible) { void Applet::OnProcessTerminatedLocked() { is_completed = true; - state_changed_event.Signal(); + state_changed_event.Signal(context.kernel); } } // namespace Service::AM diff --git a/src/core/hle/service/am/applet.h b/src/core/hle/service/am/applet.h index a693a47d7a..9a3d7c7c7b 100644 --- a/src/core/hle/service/am/applet.h +++ b/src/core/hle/service/am/applet.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project @@ -83,6 +83,7 @@ struct Applet { // Application functions bool game_play_recording_supported{}; + bool media_playback_state{}; GamePlayRecordingState game_play_recording_state{GamePlayRecordingState::Disabled}; bool jit_service_launched{}; bool application_crash_report_enabled{}; @@ -108,6 +109,10 @@ struct Applet { std::list> child_applets{}; bool is_completed{}; + std::shared_ptr reserved_applet{}; + bool unwind_after_reserved{}; + bool is_winding{}; + // Self state bool exit_locked{}; s32 fatal_section_count{}; diff --git a/src/core/hle/service/am/applet_data_broker.cpp b/src/core/hle/service/am/applet_data_broker.cpp index fff78c5afb..ac0246a666 100644 --- a/src/core/hle/service/am/applet_data_broker.cpp +++ b/src/core/hle/service/am/applet_data_broker.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -11,22 +14,30 @@ namespace Service::AM { AppletStorageChannel::AppletStorageChannel(KernelHelpers::ServiceContext& context) - : m_event(context) {} + : m_event(context) +{} AppletStorageChannel::~AppletStorageChannel() = default; -void AppletStorageChannel::Push(std::shared_ptr storage) { +void AppletStorageChannel::Push(Kernel::KernelCore& kernel, std::shared_ptr storage) { std::scoped_lock lk{m_lock}; m_data.emplace_back(std::move(storage)); - m_event.Signal(); + m_event.Signal(kernel); } -Result AppletStorageChannel::Pop(std::shared_ptr* out_storage) { +void AppletStorageChannel::Unpop(Kernel::KernelCore& kernel, std::shared_ptr storage) { + std::scoped_lock lk{m_lock}; + + m_data.emplace_front(std::move(storage)); + m_event.Signal(kernel); +} + +Result AppletStorageChannel::Pop(Kernel::KernelCore& kernel, std::shared_ptr* out_storage) { std::scoped_lock lk{m_lock}; SCOPE_EXIT { if (m_data.empty()) { - m_event.Clear(); + m_event.Clear(kernel); } }; diff --git a/src/core/hle/service/am/applet_data_broker.h b/src/core/hle/service/am/applet_data_broker.h index 2718f608ad..1fbe89a385 100644 --- a/src/core/hle/service/am/applet_data_broker.h +++ b/src/core/hle/service/am/applet_data_broker.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -22,8 +25,9 @@ public: explicit AppletStorageChannel(KernelHelpers::ServiceContext& ctx); ~AppletStorageChannel(); - void Push(std::shared_ptr storage); - Result Pop(std::shared_ptr* out_storage); + void Push(Kernel::KernelCore& kernel, std::shared_ptr storage); + void Unpop(Kernel::KernelCore& kernel, std::shared_ptr storage); + Result Pop(Kernel::KernelCore& kernel, std::shared_ptr* out_storage); Kernel::KReadableEvent* GetEvent(); private: diff --git a/src/core/hle/service/am/applet_manager.cpp b/src/core/hle/service/am/applet_manager.cpp index c2920f91ae..05bcf6cc05 100644 --- a/src/core/hle/service/am/applet_manager.cpp +++ b/src/core/hle/service/am/applet_manager.cpp @@ -34,8 +34,7 @@ struct LaunchParameterAccountPreselectedUser { }; static_assert(sizeof(LaunchParameterAccountPreselectedUser) == 0x88); -AppletStorageChannel& InitializeFakeCallerApplet(Core::System& system, - std::shared_ptr& applet) { +AppletStorageChannel& InitializeFakeCallerApplet(Core::System& system, std::shared_ptr& applet) { applet->caller_applet_broker = std::make_shared(system); return applet->caller_applet_broker->GetInData(); } @@ -52,7 +51,7 @@ void PushInShowQlaunch(Core::System& system, AppletStorageChannel& channel) { std::vector argument_data(sizeof(arguments)); std::memcpy(argument_data.data(), &arguments, sizeof(arguments)); - channel.Push(std::make_shared(system, std::move(argument_data))); + channel.Push(system.Kernel(), std::make_shared(system, std::move(argument_data))); } void PushInShowAlbum(Core::System& system, AppletStorageChannel& channel) { @@ -68,8 +67,8 @@ void PushInShowAlbum(Core::System& system, AppletStorageChannel& channel) { std::vector argument_data(sizeof(arguments)); std::vector settings_data{2}; std::memcpy(argument_data.data(), &arguments, sizeof(arguments)); - channel.Push(std::make_shared(system, std::move(argument_data))); - channel.Push(std::make_shared(system, std::move(settings_data))); + channel.Push(system.Kernel(), std::make_shared(system, std::move(argument_data))); + channel.Push(system.Kernel(), std::make_shared(system, std::move(settings_data))); } void PushInShowController(Core::System& system, AppletStorageChannel& channel) { @@ -116,9 +115,9 @@ void PushInShowController(Core::System& system, AppletStorageChannel& channel) { std::memcpy(private_args_data.data(), &private_args, sizeof(private_args)); std::memcpy(user_args_data.data(), &user_args, sizeof(user_args)); - channel.Push(std::make_shared(system, std::move(common_args_data))); - channel.Push(std::make_shared(system, std::move(private_args_data))); - channel.Push(std::make_shared(system, std::move(user_args_data))); + channel.Push(system.Kernel(), std::make_shared(system, std::move(common_args_data))); + channel.Push(system.Kernel(), std::make_shared(system, std::move(private_args_data))); + channel.Push(system.Kernel(), std::make_shared(system, std::move(user_args_data))); } void PushInShowCabinetData(Core::System& system, AppletStorageChannel& channel) { @@ -146,8 +145,8 @@ void PushInShowCabinetData(Core::System& system, AppletStorageChannel& channel) std::vector settings_data(sizeof(amiibo_settings)); std::memcpy(argument_data.data(), &arguments, sizeof(arguments)); std::memcpy(settings_data.data(), &amiibo_settings, sizeof(amiibo_settings)); - channel.Push(std::make_shared(system, std::move(argument_data))); - channel.Push(std::make_shared(system, std::move(settings_data))); + channel.Push(system.Kernel(), std::make_shared(system, std::move(argument_data))); + channel.Push(system.Kernel(), std::make_shared(system, std::move(settings_data))); } void PushInShowMiiEditData(Core::System& system, AppletStorageChannel& channel) { @@ -169,7 +168,7 @@ void PushInShowMiiEditData(Core::System& system, AppletStorageChannel& channel) std::vector argument_data(sizeof(mii_arguments)); std::memcpy(argument_data.data(), &mii_arguments, sizeof(mii_arguments)); - channel.Push(std::make_shared(system, std::move(argument_data))); + channel.Push(system.Kernel(), std::make_shared(system, std::move(argument_data))); } void PushInShowSoftwareKeyboard(Core::System& system, AppletStorageChannel& channel) { @@ -222,9 +221,9 @@ void PushInShowSoftwareKeyboard(Core::System& system, AppletStorageChannel& chan std::memcpy(work_buffer.data(), initial_string.data(), swkbd_config.initial_string_length * sizeof(char16_t)); - channel.Push(std::make_shared(system, std::move(argument_data))); - channel.Push(std::make_shared(system, std::move(swkbd_data))); - channel.Push(std::make_shared(system, std::move(work_buffer))); + channel.Push(system.Kernel(), std::make_shared(system, std::move(argument_data))); + channel.Push(system.Kernel(), std::make_shared(system, std::move(swkbd_data))); + channel.Push(system.Kernel(), std::make_shared(system, std::move(work_buffer))); } } // namespace @@ -340,7 +339,7 @@ void AppletManager::SetWindowSystem(WindowSystem* window_system) { } // Applet was started by frontend, so it is foreground. - applet->lifecycle_manager.SetFocusState(FocusState::InFocus); + applet->lifecycle_manager.SetFocusState(m_system.Kernel(), FocusState::InFocus); if (applet->applet_id == AppletId::QLaunch) { applet->lifecycle_manager.SetFocusHandlingMode(false); diff --git a/src/core/hle/service/am/button_poller.cpp b/src/core/hle/service/am/button_poller.cpp index 069a1934d4..6999acfa12 100644 --- a/src/core/hle/service/am/button_poller.cpp +++ b/src/core/hle/service/am/button_poller.cpp @@ -1,9 +1,11 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include +#include "common/thread.h" #include "core/core.h" #include "core/hle/service/am/am_types.h" #include "core/hle/service/am/button_poller.h" @@ -34,16 +36,15 @@ ButtonPressDuration ClassifyPressDuration(std::chrono::steady_clock::time_point } // namespace -ButtonPoller::ButtonPoller(Core::System& system, WindowSystem& window_system) - : m_window_system(window_system) { +ButtonPoller::ButtonPoller(Core::System& system, WindowSystem& window_system) { // TODO: am reads this from the home button state in hid, which is controller-agnostic. Core::HID::ControllerUpdateCallback engine_callback{ - .on_change = - [this](Core::HID::ControllerTriggerType type) { - if (type == Core::HID::ControllerTriggerType::Button) { - this->OnButtonStateChanged(); - } - }, + .on_change = [this, &window_system](Core::HID::ControllerTriggerType type) { + if (type == Core::HID::ControllerTriggerType::Button) { + std::unique_lock lk{m_mutex}; + OnButtonStateChanged(window_system); + } + }, .is_npad_service = true, }; @@ -52,25 +53,35 @@ ButtonPoller::ButtonPoller(Core::System& system, WindowSystem& window_system) m_player1 = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); m_player1_key = m_player1->SetCallback(engine_callback); - m_thread = std::thread([this] { this->ThreadLoop(); }); + m_thread = std::jthread([this, &window_system](std::stop_token stop_token) { + Common::SetCurrentThreadName("ButtonPoller"); + while (!stop_token.stop_requested()) { + using namespace std::chrono_literals; + std::unique_lock lk{m_mutex}; + m_cv.wait_for(lk, 50ms); + if (stop_token.stop_requested()) + break; + OnButtonStateChanged(window_system); + std::this_thread::sleep_for(5ms); + } + }); } ButtonPoller::~ButtonPoller() { m_handheld->DeleteCallback(m_handheld_key); m_player1->DeleteCallback(m_player1_key); - m_stop = true; m_cv.notify_all(); if (m_thread.joinable()) { + m_thread.request_stop(); m_thread.join(); } } -void ButtonPoller::OnButtonStateChanged() { - std::lock_guard lk{m_mutex}; - const bool home_button = - m_handheld->GetHomeButtons().home.Value() || m_player1->GetHomeButtons().home.Value(); - const bool capture_button = m_handheld->GetCaptureButtons().capture.Value() || - m_player1->GetCaptureButtons().capture.Value(); +void ButtonPoller::OnButtonStateChanged(WindowSystem& window_system) { + auto const home_button = m_handheld->GetHomeButtons().home.Value() + || m_player1->GetHomeButtons().home.Value(); + auto const capture_button = m_handheld->GetCaptureButtons().capture.Value() + || m_player1->GetCaptureButtons().capture.Value(); // Buttons pressed which were not previously pressed if (home_button && !m_home_button_press_start) { @@ -90,7 +101,7 @@ void ButtonPoller::OnButtonStateChanged() { if (home_button && m_home_button_press_start && !m_home_button_long_sent) { const auto duration = ClassifyPressDuration(*m_home_button_press_start); if (duration != ButtonPressDuration::ShortPressing) { - m_window_system.OnSystemButtonPress(SystemButtonType::HomeButtonLongPressing); + window_system.OnSystemButtonPress(SystemButtonType::HomeButtonLongPressing); m_home_button_long_sent = true; } } @@ -98,7 +109,7 @@ void ButtonPoller::OnButtonStateChanged() { if (capture_button && m_capture_button_press_start && !m_capture_button_long_sent) { const auto duration = ClassifyPressDuration(*m_capture_button_press_start); if (duration != ButtonPressDuration::ShortPressing) { - m_window_system.OnSystemButtonPress(SystemButtonType::CaptureButtonLongPressing); + window_system.OnSystemButtonPress(SystemButtonType::CaptureButtonLongPressing); m_capture_button_long_sent = true; } } @@ -107,9 +118,8 @@ void ButtonPoller::OnButtonStateChanged() { if (!home_button && m_home_button_press_start) { if(!m_home_button_long_sent) { const auto duration = ClassifyPressDuration(*m_home_button_press_start); - m_window_system.OnSystemButtonPress( - duration == ButtonPressDuration::ShortPressing ? SystemButtonType::HomeButtonShortPressing - : SystemButtonType::HomeButtonLongPressing); + window_system.OnSystemButtonPress(duration == ButtonPressDuration::ShortPressing + ? SystemButtonType::HomeButtonShortPressing : SystemButtonType::HomeButtonLongPressing); } m_home_button_press_start = std::nullopt; m_home_button_long_sent = false; @@ -117,9 +127,8 @@ void ButtonPoller::OnButtonStateChanged() { if (!capture_button && m_capture_button_press_start) { if (!m_capture_button_long_sent) { const auto duration = ClassifyPressDuration(*m_capture_button_press_start); - m_window_system.OnSystemButtonPress( - duration == ButtonPressDuration::ShortPressing ? SystemButtonType::CaptureButtonShortPressing - : SystemButtonType::CaptureButtonLongPressing); + window_system.OnSystemButtonPress(duration == ButtonPressDuration::ShortPressing + ? SystemButtonType::CaptureButtonShortPressing : SystemButtonType::CaptureButtonLongPressing); } m_capture_button_press_start = std::nullopt; m_capture_button_long_sent = false; @@ -130,16 +139,4 @@ void ButtonPoller::OnButtonStateChanged() { // } } -void ButtonPoller::ThreadLoop() { - using namespace std::chrono_literals; - std::unique_lock lk{m_mutex}; - while (!m_stop) { - m_cv.wait_for(lk, 50ms); - if (m_stop) break; - lk.unlock(); - OnButtonStateChanged(); - lk.lock(); - } -} - } // namespace Service::AM diff --git a/src/core/hle/service/am/button_poller.h b/src/core/hle/service/am/button_poller.h index 12e7035aa1..11d147d328 100644 --- a/src/core/hle/service/am/button_poller.h +++ b/src/core/hle/service/am/button_poller.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project @@ -30,31 +30,23 @@ class ButtonPoller { public: explicit ButtonPoller(Core::System& system, WindowSystem& window_system); ~ButtonPoller(); + void OnButtonStateChanged(WindowSystem& window_system); private: - void OnButtonStateChanged(); - void ThreadLoop(); - -private: - WindowSystem& m_window_system; - - Core::HID::EmulatedController* m_handheld{}; - int m_handheld_key{}; - Core::HID::EmulatedController* m_player1{}; - int m_player1_key{}; - + std::mutex m_mutex; + std::condition_variable m_cv; + std::jthread m_thread; std::optional m_home_button_press_start{}; std::optional m_capture_button_press_start{}; std::optional m_power_button_press_start{}; - bool m_home_button_long_sent{}; - bool m_capture_button_long_sent{}; - bool m_power_button_long_sent{}; - - std::thread m_thread; - std::atomic m_stop{false}; - std::condition_variable m_cv; - std::mutex m_mutex; + Core::HID::EmulatedController* m_handheld{}; + Core::HID::EmulatedController* m_player1{}; + int32_t m_handheld_key{}; + int32_t m_player1_key{}; + bool m_home_button_long_sent : 1 = false; + bool m_capture_button_long_sent : 1 = false; + bool m_power_button_long_sent : 1 = false; }; } // namespace Service::AM diff --git a/src/core/hle/service/am/event_observer.cpp b/src/core/hle/service/am/event_observer.cpp index 5d1d303ed3..32cb35ee30 100644 --- a/src/core/hle/service/am/event_observer.cpp +++ b/src/core/hle/service/am/event_observer.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -15,18 +18,30 @@ enum class UserDataTag : u32 { }; EventObserver::EventObserver(Core::System& system, WindowSystem& window_system) - : m_system(system), m_context(system, "am:EventObserver"), m_window_system(window_system), - m_wakeup_event(m_context), m_wakeup_holder(m_wakeup_event.GetHandle()) { + : m_system(system), m_context(system, "am:EventObserver") + , m_window_system(window_system) + , m_wakeup_event(m_context) + , m_wakeup_holder(m_wakeup_event.GetHandle()) +{ m_window_system.SetEventObserver(this); m_wakeup_holder.SetUserData(static_cast(UserDataTag::WakeupEvent)); m_wakeup_holder.LinkToMultiWait(std::addressof(m_multi_wait)); - m_thread = std::thread([&] { this->ThreadFunc(); }); + m_thread = std::thread([this] { + Common::SetCurrentThreadName("am:EventObserver"); + while (true) { + auto* signaled_holder = this->WaitSignaled(); + if (!signaled_holder) { + break; + } + this->Process(signaled_holder); + } + }); } EventObserver::~EventObserver() { // Signal thread and wait for processing to finish. m_stop_source.request_stop(); - m_wakeup_event.Signal(); + m_wakeup_event.Signal(m_system.Kernel()); m_thread.join(); // Free remaining owned sessions. @@ -61,11 +76,11 @@ void EventObserver::TrackAppletProcess(Applet& applet) { } // Signal wakeup. - m_wakeup_event.Signal(); + m_wakeup_event.Signal(m_system.Kernel()); } void EventObserver::RequestUpdate() { - m_wakeup_event.Signal(); + m_wakeup_event.Signal(m_system.Kernel()); } void EventObserver::LinkDeferred() { @@ -106,7 +121,7 @@ void EventObserver::Process(MultiWaitHolder* holder) { } void EventObserver::OnWakeupEvent(MultiWaitHolder* holder) { - m_wakeup_event.Clear(); + m_wakeup_event.Clear(m_system.Kernel()); // Perform recalculation. m_window_system.Update(); @@ -146,17 +161,4 @@ void EventObserver::DestroyAppletProcessHolderLocked(ProcessHolder* holder) { delete holder; } -void EventObserver::ThreadFunc() { - Common::SetCurrentThreadName("am:EventObserver"); - - while (true) { - auto* signaled_holder = this->WaitSignaled(); - if (!signaled_holder) { - break; - } - - this->Process(signaled_holder); - } -} - } // namespace Service::AM diff --git a/src/core/hle/service/am/event_observer.h b/src/core/hle/service/am/event_observer.h index 3e52e8494d..a0fa13f823 100644 --- a/src/core/hle/service/am/event_observer.h +++ b/src/core/hle/service/am/event_observer.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -41,9 +44,6 @@ private: private: void DestroyAppletProcessHolderLocked(ProcessHolder* holder); -private: - void ThreadFunc(); - private: // System reference and context. Core::System& m_system; diff --git a/src/core/hle/service/am/frontend/applet_general.cpp b/src/core/hle/service/am/frontend/applet_general.cpp index 5345fdec33..22cad54b66 100644 --- a/src/core/hle/service/am/frontend/applet_general.cpp +++ b/src/core/hle/service/am/frontend/applet_general.cpp @@ -20,16 +20,16 @@ namespace Service::AM::Frontend { constexpr Result ERROR_INVALID_PIN{ErrorModule::PCTL, 221}; -static void LogCurrentStorage(std::shared_ptr applet, std::string_view prefix) { +static void LogCurrentStorage(Kernel::KernelCore& kernel, std::shared_ptr applet, std::string_view prefix) { std::shared_ptr storage; - while (R_SUCCEEDED(applet->caller_applet_broker->GetInData().Pop(&storage))) { + while (R_SUCCEEDED(applet->caller_applet_broker->GetInData().Pop(kernel, &storage))) { const auto data = storage->GetData(); LOG_INFO(Service_AM, "called (STUBBED), during {} received normal data with size={:08X}, data={}", prefix, data.size(), Common::HexToString(data)); } - while (R_SUCCEEDED(applet->caller_applet_broker->GetInteractiveInData().Pop(&storage))) { + while (R_SUCCEEDED(applet->caller_applet_broker->GetInteractiveInData().Pop(kernel, &storage))) { const auto data = storage->GetData(); LOG_INFO(Service_AM, "called (STUBBED), during {} received interactive data with size={:08X}, data={}", @@ -219,7 +219,7 @@ void StubApplet::Initialize() { LOG_WARNING(Service_AM, "called (STUBBED)"); FrontendApplet::Initialize(); - LogCurrentStorage(applet.lock(), "Initialize"); + LogCurrentStorage(system.Kernel(), applet.lock(), "Initialize"); } Result StubApplet::GetStatus() const { @@ -229,7 +229,7 @@ Result StubApplet::GetStatus() const { void StubApplet::ExecuteInteractive() { LOG_WARNING(Service_AM, "called (STUBBED)"); - LogCurrentStorage(applet.lock(), "ExecuteInteractive"); + LogCurrentStorage(system.Kernel(), applet.lock(), "ExecuteInteractive"); PushOutData(std::make_shared(system, std::vector(0x1000))); PushInteractiveOutData(std::make_shared(system, std::vector(0x1000))); @@ -238,7 +238,7 @@ void StubApplet::ExecuteInteractive() { void StubApplet::Execute() { LOG_WARNING(Service_AM, "called (STUBBED)"); - LogCurrentStorage(applet.lock(), "Execute"); + LogCurrentStorage(system.Kernel(), applet.lock(), "Execute"); PushOutData(std::make_shared(system, std::vector(0x1000))); PushInteractiveOutData(std::make_shared(system, std::vector(0x1000))); diff --git a/src/core/hle/service/am/frontend/applet_web_browser.cpp b/src/core/hle/service/am/frontend/applet_web_browser.cpp index 92782c58e2..1ddb1ec2a3 100644 --- a/src/core/hle/service/am/frontend/applet_web_browser.cpp +++ b/src/core/hle/service/am/frontend/applet_web_browser.cpp @@ -4,6 +4,7 @@ // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "applet_web_browser_types.h" #include "common/assert.h" #include "common/fs/file.h" #include "common/fs/fs.h" @@ -370,16 +371,13 @@ void WebBrowser::ExtractOfflineRomFS() { void WebBrowser::WebBrowserExit(WebExitReason exit_reason, std::string last_url) { const bool use_tlv_output = - (web_arg_header.shim_kind == ShimKind::Share && - web_applet_version >= WebAppletVersion::Version196608) || - (web_arg_header.shim_kind == ShimKind::Web && - web_applet_version >= WebAppletVersion::Version524288) || - (web_arg_header.shim_kind == ShimKind::Lhub); + (web_arg_header.shim_kind == ShimKind::Share && web_applet_version >= WebAppletVersion::Version196608) + || (web_arg_header.shim_kind == ShimKind::Web && web_applet_version >= WebAppletVersion::Version524288) + || (web_arg_header.shim_kind == ShimKind::Lhub); // https://switchbrew.org/wiki/Internet_Browser#TLVs if (use_tlv_output) { - LOG_DEBUG(Service_AM, "Using TLV output: exit_reason={}, last_url={}, last_url_size={}", - exit_reason, last_url, last_url.size()); + LOG_DEBUG(Service_AM, "Using TLV output: exit_reason={}, last_url={}, last_url_size={}", exit_reason, last_url, last_url.size()); // storage size for TLVs is 0x2000 bytes (as per switchbrew documentation) constexpr size_t TLV_STORAGE_SIZE = 0x2000; @@ -600,11 +598,14 @@ void WebBrowser::ExecuteShare() { void WebBrowser::ExecuteWeb() { LOG_INFO(Service_AM, "Opening external URL at {}", external_url); - - frontend.OpenExternalWebPage(external_url, - [this](WebExitReason exit_reason, std::string last_url) { - WebBrowserExit(exit_reason, last_url); - }); + frontend.OpenExternalWebPage(external_url, [this](WebExitReason exit_reason, std::string last_url) { + // Offline and web applets must be explicitly exited from because they respect exit state + // Unlike the other web stuffs + if (exit_reason == WebExitReason::ExitRequested || exit_reason == WebExitReason::EndButtonPressed) + exit_reason = (web_arg_header.shim_kind == ShimKind::Web || web_arg_header.shim_kind == ShimKind::Offline) + ? WebExitReason::ExitRequested : WebExitReason::EndButtonPressed; + WebBrowserExit(exit_reason, last_url); + }); } void WebBrowser::ExecuteWifi() { diff --git a/src/core/hle/service/am/frontend/applets.cpp b/src/core/hle/service/am/frontend/applets.cpp index 2ff2bcdbb6..407ca10602 100644 --- a/src/core/hle/service/am/frontend/applets.cpp +++ b/src/core/hle/service/am/frontend/applets.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later #include @@ -52,22 +52,22 @@ void FrontendApplet::Initialize() { std::shared_ptr FrontendApplet::PopInData() { std::shared_ptr ret; - applet.lock()->caller_applet_broker->GetInData().Pop(&ret); + applet.lock()->caller_applet_broker->GetInData().Pop(system.Kernel(), &ret); return ret; } std::shared_ptr FrontendApplet::PopInteractiveInData() { std::shared_ptr ret; - applet.lock()->caller_applet_broker->GetInteractiveInData().Pop(&ret); + applet.lock()->caller_applet_broker->GetInteractiveInData().Pop(system.Kernel(), &ret); return ret; } void FrontendApplet::PushOutData(std::shared_ptr storage) { - applet.lock()->caller_applet_broker->GetOutData().Push(storage); + applet.lock()->caller_applet_broker->GetOutData().Push(system.Kernel(), storage); } void FrontendApplet::PushInteractiveOutData(std::shared_ptr storage) { - applet.lock()->caller_applet_broker->GetInteractiveOutData().Push(storage); + applet.lock()->caller_applet_broker->GetInteractiveOutData().Push(system.Kernel(), storage); } void FrontendApplet::Exit() { @@ -75,7 +75,7 @@ void FrontendApplet::Exit() { std::scoped_lock lk{applet_->lock}; applet_->is_completed = true; - applet_->state_changed_event.Signal(); + applet_->state_changed_event.Signal(system.Kernel()); } FrontendAppletSet::FrontendAppletSet() = default; diff --git a/src/core/hle/service/am/hid_registration.cpp b/src/core/hle/service/am/hid_registration.cpp index ea4bd8f45b..2613fc29a4 100644 --- a/src/core/hle/service/am/hid_registration.cpp +++ b/src/core/hle/service/am/hid_registration.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -12,7 +15,10 @@ namespace Service::AM { HidRegistration::HidRegistration(Core::System& system, Process& process) : m_process(process) { m_hid_server = system.ServiceManager().GetService("hid", true); + this->RegisterCurrentProcess(); +} +void HidRegistration::RegisterCurrentProcess() { if (m_process.IsInitialized()) { m_hid_server->GetResourceManager()->RegisterAppletResourceUserId(m_process.GetProcessId(), true); diff --git a/src/core/hle/service/am/hid_registration.h b/src/core/hle/service/am/hid_registration.h index 54f42af189..560266144f 100644 --- a/src/core/hle/service/am/hid_registration.h +++ b/src/core/hle/service/am/hid_registration.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -24,6 +27,7 @@ public: explicit HidRegistration(Core::System& system, Process& process); ~HidRegistration(); + void RegisterCurrentProcess(); void EnableAppletToGetInput(bool enable); private: diff --git a/src/core/hle/service/am/library_applet_storage.cpp b/src/core/hle/service/am/library_applet_storage.cpp index 0412c215d5..8b14511c72 100644 --- a/src/core/hle/service/am/library_applet_storage.cpp +++ b/src/core/hle/service/am/library_applet_storage.cpp @@ -1,7 +1,11 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/kernel/k_transfer_memory.h" +#include "core/hle/kernel/kernel.h" #include "core/hle/service/am/am_results.h" #include "core/hle/service/am/library_applet_storage.h" #include "core/memory.h" @@ -55,15 +59,18 @@ private: class TransferMemoryLibraryAppletStorage : public LibraryAppletStorage { public: - explicit TransferMemoryLibraryAppletStorage(Core::Memory::Memory& memory, - Kernel::KTransferMemory* trmem, bool is_writable, - s64 size) - : m_memory(memory), m_trmem(trmem), m_is_writable(is_writable), m_size(size) { - m_trmem->Open(); + explicit TransferMemoryLibraryAppletStorage(Kernel::KernelCore& kernel, Core::Memory::Memory& memory, Kernel::KTransferMemory* trmem, bool is_writable, s64 size) + : m_kernel{kernel} + , m_memory(memory) + , m_trmem(trmem) + , m_is_writable(is_writable) + , m_size(size) + { + m_trmem->Open(m_kernel); } ~TransferMemoryLibraryAppletStorage() { - m_trmem->Close(); + m_trmem->Close(m_kernel); m_trmem = nullptr; } @@ -93,6 +100,7 @@ public: } protected: + Kernel::KernelCore& m_kernel; Core::Memory::Memory& m_memory; Kernel::KTransferMemory* m_trmem; bool m_is_writable; @@ -101,9 +109,9 @@ protected: class HandleLibraryAppletStorage : public TransferMemoryLibraryAppletStorage { public: - explicit HandleLibraryAppletStorage(Core::Memory::Memory& memory, - Kernel::KTransferMemory* trmem, s64 size) - : TransferMemoryLibraryAppletStorage(memory, trmem, true, size) {} + explicit HandleLibraryAppletStorage(Kernel::KernelCore& kernel, Core::Memory::Memory& memory, Kernel::KTransferMemory* trmem, s64 size) + : TransferMemoryLibraryAppletStorage(kernel, memory, trmem, true, size) + {} ~HandleLibraryAppletStorage() = default; Kernel::KTransferMemory* GetHandle() override { @@ -125,16 +133,12 @@ std::shared_ptr CreateStorage(std::vector&& data) { return std::make_shared(std::move(data)); } -std::shared_ptr CreateTransferMemoryStorage(Core::Memory::Memory& memory, - Kernel::KTransferMemory* trmem, - bool is_writable, s64 size) { - return std::make_shared(memory, trmem, is_writable, size); +std::shared_ptr CreateTransferMemoryStorage(Kernel::KernelCore& kernel, Core::Memory::Memory& memory, Kernel::KTransferMemory* trmem, bool is_writable, s64 size) { + return std::make_shared(kernel, memory, trmem, is_writable, size); } -std::shared_ptr CreateHandleStorage(Core::Memory::Memory& memory, - Kernel::KTransferMemory* trmem, - s64 size) { - return std::make_shared(memory, trmem, size); +std::shared_ptr CreateHandleStorage(Kernel::KernelCore& kernel, Core::Memory::Memory& memory, Kernel::KTransferMemory* trmem, s64 size) { + return std::make_shared(kernel, memory, trmem, size); } } // namespace Service::AM diff --git a/src/core/hle/service/am/library_applet_storage.h b/src/core/hle/service/am/library_applet_storage.h index 7f53f3a9cd..cfdbef96d0 100644 --- a/src/core/hle/service/am/library_applet_storage.h +++ b/src/core/hle/service/am/library_applet_storage.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -10,6 +13,7 @@ class Memory; } namespace Kernel { +class KernelCore; class KTransferMemory; } @@ -27,10 +31,7 @@ public: }; std::shared_ptr CreateStorage(std::vector&& data); -std::shared_ptr CreateTransferMemoryStorage(Core::Memory::Memory& memory, - Kernel::KTransferMemory* trmem, - bool is_writable, s64 size); -std::shared_ptr CreateHandleStorage(Core::Memory::Memory& memory, - Kernel::KTransferMemory* trmem, s64 size); +std::shared_ptr CreateTransferMemoryStorage(Kernel::KernelCore& kernel, Core::Memory::Memory& memory, Kernel::KTransferMemory* trmem, bool is_writable, s64 size); +std::shared_ptr CreateHandleStorage(Kernel::KernelCore& kernel, Core::Memory::Memory& memory, Kernel::KTransferMemory* trmem, s64 size); } // namespace Service::AM diff --git a/src/core/hle/service/am/lifecycle_manager.cpp b/src/core/hle/service/am/lifecycle_manager.cpp index fbb9ad611f..4b003c4696 100644 --- a/src/core/hle/service/am/lifecycle_manager.cpp +++ b/src/core/hle/service/am/lifecycle_manager.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -25,9 +28,9 @@ Event& LifecycleManager::GetHDCPStateChangedEvent() { return m_hdcp_state_changed_event; } -void LifecycleManager::PushUnorderedMessage(AppletMessage message) { +void LifecycleManager::PushUnorderedMessage(Kernel::KernelCore& kernel, AppletMessage message) { m_unordered_messages.push_back(message); - this->SignalSystemEventIfNeeded(); + this->SignalSystemEventIfNeeded(kernel); } AppletMessage LifecycleManager::PopMessageInOrderOfPriority() { @@ -141,36 +144,48 @@ bool LifecycleManager::ShouldSignalSystemEvent() { m_has_album_screen_shot_taken || m_has_album_recording_saved; } -void LifecycleManager::OnOperationAndPerformanceModeChanged() { +void LifecycleManager::OnOperationAndPerformanceModeChanged(Kernel::KernelCore& kernel) { if (m_operation_mode_changed_notification_enabled) { m_has_operation_mode_changed = true; } if (m_performance_mode_changed_notification_enabled) { m_has_performance_mode_changed = true; } - m_operation_mode_changed_system_event.Signal(); - this->SignalSystemEventIfNeeded(); + m_operation_mode_changed_system_event.Signal(kernel); + this->SignalSystemEventIfNeeded(kernel); } -void LifecycleManager::SignalSystemEventIfNeeded() { +void LifecycleManager::SignalSystemEventIfNeeded(Kernel::KernelCore& kernel) { // Check our cached value for the system event. const bool applet_message_available = m_applet_message_available; // If it's not current, we need to do an update, either clearing or signaling. if (applet_message_available != this->ShouldSignalSystemEvent()) { if (!applet_message_available) { - m_system_event.Signal(); + m_system_event.Signal(kernel); m_applet_message_available = true; } else { - m_system_event.Clear(); + m_system_event.Clear(kernel); m_applet_message_available = false; } } } -bool LifecycleManager::PopMessage(AppletMessage* out_message) { +void LifecycleManager::ResetForRelaunch() { + m_unordered_messages.clear(); + + m_activity_state = ActivityState::BackgroundVisible; + m_requested_focus_state = FocusState{}; + m_acknowledged_focus_state = FocusState{}; + m_has_focus_state_changed = true; + + m_suspend_mode = SuspendMode::NoOverride; + m_forced_suspend = false; +} + +bool LifecycleManager::PopMessage(Kernel::KernelCore& kernel, AppletMessage* out_message) { const auto message = this->PopMessageInOrderOfPriority(); - this->SignalSystemEventIfNeeded(); + this->SignalSystemEventIfNeeded(kernel); *out_message = message; return message != AppletMessage::None; @@ -372,6 +387,10 @@ bool LifecycleManager::UpdateRequestedFocusState() { // Mark the focus state as ready for update. m_requested_focus_state = new_state; + if (m_is_application) { + m_has_focus_state_changed = true; + } + // We changed the focus state. return true; } diff --git a/src/core/hle/service/am/lifecycle_manager.h b/src/core/hle/service/am/lifecycle_manager.h index a1ddb9e2df..ae041d3e29 100644 --- a/src/core/hle/service/am/lifecycle_manager.h +++ b/src/core/hle/service/am/lifecycle_manager.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -66,17 +69,17 @@ public: return m_acknowledged_focus_state; } - void SetFocusState(FocusState state) { + void SetFocusState(Kernel::KernelCore& kernel, FocusState state) { if (m_requested_focus_state != state) { m_has_focus_state_changed = true; } m_requested_focus_state = state; - this->SignalSystemEventIfNeeded(); + this->SignalSystemEventIfNeeded(kernel); } - void RequestExit() { + void RequestExit(Kernel::KernelCore& kernel) { m_has_requested_exit = true; - this->SignalSystemEventIfNeeded(); + this->SignalSystemEventIfNeeded(kernel); } void RequestResumeNotification() { @@ -89,22 +92,22 @@ public: } } - void OnOperationAndPerformanceModeChanged(); + void OnOperationAndPerformanceModeChanged(Kernel::KernelCore& kernel); public: - void SetFocusStateChangedNotificationEnabled(bool enabled) { + void SetFocusStateChangedNotificationEnabled(Kernel::KernelCore& kernel, bool enabled) { m_focus_state_changed_notification_enabled = enabled; - this->SignalSystemEventIfNeeded(); + this->SignalSystemEventIfNeeded(kernel); } - void SetOperationModeChangedNotificationEnabled(bool enabled) { + void SetOperationModeChangedNotificationEnabled(Kernel::KernelCore& kernel, bool enabled) { m_operation_mode_changed_notification_enabled = enabled; - this->SignalSystemEventIfNeeded(); + this->SignalSystemEventIfNeeded(kernel); } - void SetPerformanceModeChangedNotificationEnabled(bool enabled) { + void SetPerformanceModeChangedNotificationEnabled(Kernel::KernelCore& kernel, bool enabled) { m_performance_mode_changed_notification_enabled = enabled; - this->SignalSystemEventIfNeeded(); + this->SignalSystemEventIfNeeded(kernel); } void SetResumeNotificationEnabled(bool enabled) { @@ -129,11 +132,13 @@ public: void RemoveForceResumeIfPossible(); bool IsRunnable() const; bool UpdateRequestedFocusState(); - void SignalSystemEventIfNeeded(); + void SignalSystemEventIfNeeded(Kernel::KernelCore& kernel); public: - void PushUnorderedMessage(AppletMessage message); - bool PopMessage(AppletMessage* out_message); + void PushUnorderedMessage(Kernel::KernelCore& kernel, AppletMessage message); + bool PopMessage(Kernel::KernelCore& kernel, AppletMessage* out_message); + + void ResetForRelaunch(); private: FocusState GetFocusStateWhileForegroundObscured() const; diff --git a/src/core/hle/service/am/process_creation.cpp b/src/core/hle/service/am/process_creation.cpp index b5e31353a2..60154fc301 100644 --- a/src/core/hle/service/am/process_creation.cpp +++ b/src/core/hle/service/am/process_creation.cpp @@ -1,6 +1,10 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include #include "core/core.h" #include "core/file_sys/content_archive.h" #include "core/file_sys/nca_metadata.h" @@ -16,7 +20,7 @@ namespace Service::AM { namespace { -FileSys::StorageId GetStorageIdForFrontendSlot( +[[nodiscard]] FileSys::StorageId GetStorageIdForFrontendSlot( std::optional slot) { if (!slot.has_value()) { return FileSys::StorageId::None; @@ -36,31 +40,23 @@ FileSys::StorageId GetStorageIdForFrontendSlot( } } -std::unique_ptr CreateProcessImpl(std::unique_ptr& out_loader, - Loader::ResultStatus& out_load_result, - Core::System& system, FileSys::VirtualFile file, - u64 program_id, u64 program_index) { +[[nodiscard]] inline std::unique_ptr CreateProcessImpl(std::unique_ptr& out_loader, Loader::ResultStatus& out_load_result, Core::System& system, FileSys::VirtualFile file, u64 program_id, u64 program_index) { // Get the appropriate loader to parse this NCA. out_loader = Loader::GetLoader(system, file, program_id, program_index); - // Ensure we have a loader which can parse the NCA. - if (!out_loader) { - return nullptr; + if (out_loader) { + // Try to load the process. + auto process = std::make_unique(system); + if (process->Initialize(*out_loader, out_load_result)) { + return process; + } } - - // Try to load the process. - auto process = std::make_unique(system); - if (process->Initialize(*out_loader, out_load_result)) { - return process; - } - return nullptr; } } // Anonymous namespace -std::unique_ptr CreateProcess(Core::System& system, u64 program_id, - u8 minimum_key_generation, u8 maximum_key_generation) { +std::unique_ptr CreateProcess(Core::System& system, u64 program_id, u8 minimum_key_generation, u8 maximum_key_generation) { // Attempt to load program NCA. FileSys::VirtualFile nca_raw{}; @@ -79,8 +75,7 @@ std::unique_ptr CreateProcess(Core::System& system, u64 program_id, if (nca.GetStatus() == Loader::ResultStatus::Success && (nca.GetKeyGeneration() < minimum_key_generation || nca.GetKeyGeneration() > maximum_key_generation)) { - LOG_WARNING(Service_LDR, "Skipping program {:016X} with generation {}", program_id, - nca.GetKeyGeneration()); + LOG_WARNING(Service_LDR, "Skipping program {:016X} with generation {}", program_id, nca.GetKeyGeneration()); return nullptr; } } @@ -90,42 +85,48 @@ std::unique_ptr CreateProcess(Core::System& system, u64 program_id, return CreateProcessImpl(loader, status, system, nca_raw, program_id, 0); } -std::unique_ptr CreateApplicationProcess(std::vector& out_control, - std::unique_ptr& out_loader, - Loader::ResultStatus& out_load_result, - Core::System& system, FileSys::VirtualFile file, - u64 program_id, u64 program_index) { - auto process = - CreateProcessImpl(out_loader, out_load_result, system, file, program_id, program_index); - if (!process) { - return nullptr; - } +std::unique_ptr CreateApplicationProcess(std::vector& out_control, std::unique_ptr& out_loader, Loader::ResultStatus& out_load_result, Core::System& system, FileSys::VirtualFile file, u64 program_id, u64 program_index) { + if (auto process = CreateProcessImpl(out_loader, out_load_result, system, file, program_id, program_index); process) { + FileSys::NACP nacp; + if (out_loader->ReadControlData(nacp) == Loader::ResultStatus::Success) { + out_control = nacp.GetRawBytes(); + } else { + out_control.resize(sizeof(FileSys::RawNACP)); + std::fill(out_control.begin(), out_control.end(), (u8) 0); + } - FileSys::NACP nacp; - if (out_loader->ReadControlData(nacp) == Loader::ResultStatus::Success) { - out_control = nacp.GetRawBytes(); - } else { - out_control.resize(sizeof(FileSys::RawNACP)); - std::fill(out_control.begin(), out_control.end(), (u8) 0); - } + auto& storage = system.GetContentProviderUnion(); + Service::Glue::ApplicationLaunchProperty launch{}; + launch.title_id = process->GetProgramId(); + FileSys::PatchManager pm{launch.title_id, system.GetFileSystemController(), storage}; + launch.version = pm.GetGameVersion().value_or(0); + + // TODO(DarkLordZach): When FSController/Game Card Support is added, if + // current_process_game_card use correct StorageId + launch.base_game_storage_id = GetStorageIdForFrontendSlot(storage.GetSlotForEntry(launch.title_id, FileSys::ContentRecordType::Program)); + launch.update_storage_id = GetStorageIdForFrontendSlot(storage.GetSlotForEntry(FileSys::GetUpdateTitleID(launch.title_id), FileSys::ContentRecordType::Program)); + + system.GetARPManager().Register(launch.title_id, launch, out_control); + return process; + } + return nullptr; +} + +bool ReinitializeProcess(Core::System& system, Process& process, u64 program_id) { auto& storage = system.GetContentProviderUnion(); - Service::Glue::ApplicationLaunchProperty launch{}; - launch.title_id = process->GetProgramId(); + const auto nca_raw = storage.GetEntryRaw(program_id, FileSys::ContentRecordType::Program); + if (!nca_raw) { + return false; + } - FileSys::PatchManager pm{launch.title_id, system.GetFileSystemController(), storage}; - launch.version = pm.GetGameVersion().value_or(0); + auto loader = Loader::GetLoader(system, nca_raw, program_id, 0); + if (!loader) { + return false; + } - // TODO(DarkLordZach): When FSController/Game Card Support is added, if - // current_process_game_card use correct StorageId - launch.base_game_storage_id = GetStorageIdForFrontendSlot( - storage.GetSlotForEntry(launch.title_id, FileSys::ContentRecordType::Program)); - launch.update_storage_id = GetStorageIdForFrontendSlot(storage.GetSlotForEntry( - FileSys::GetUpdateTitleID(launch.title_id), FileSys::ContentRecordType::Program)); - - system.GetARPManager().Register(launch.title_id, launch, out_control); - - return process; + Loader::ResultStatus status{}; + return process.Initialize(*loader, status); } } // namespace Service::AM diff --git a/src/core/hle/service/am/process_creation.h b/src/core/hle/service/am/process_creation.h index 8cfb9e0c9e..20f0743740 100644 --- a/src/core/hle/service/am/process_creation.h +++ b/src/core/hle/service/am/process_creation.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -24,12 +27,9 @@ class Process; namespace Service::AM { -std::unique_ptr CreateProcess(Core::System& system, u64 program_id, - u8 minimum_key_generation, u8 maximum_key_generation); -std::unique_ptr CreateApplicationProcess(std::vector& out_control, - std::unique_ptr& out_loader, - Loader::ResultStatus& out_load_result, - Core::System& system, FileSys::VirtualFile file, - u64 program_id, u64 program_index); +std::unique_ptr CreateProcess(Core::System& system, u64 program_id, u8 minimum_key_generation, u8 maximum_key_generation); +std::unique_ptr CreateApplicationProcess(std::vector& out_control, std::unique_ptr& out_loader, Loader::ResultStatus& out_load_result, Core::System& system, FileSys::VirtualFile file, u64 program_id, u64 program_index); + +bool ReinitializeProcess(Core::System& system, Process& process, u64 program_id); } // namespace Service::AM diff --git a/src/core/hle/service/am/service/application_accessor.cpp b/src/core/hle/service/am/service/application_accessor.cpp index 88a1724fc6..f357871706 100644 --- a/src/core/hle/service/am/service/application_accessor.cpp +++ b/src/core/hle/service/am/service/application_accessor.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project @@ -69,7 +69,7 @@ Result IApplicationAccessor::RequestExit() { std::scoped_lock lk{m_applet->lock}; if (m_applet->exit_locked) { - m_applet->lifecycle_manager.RequestExit(); + m_applet->lifecycle_manager.RequestExit(system.Kernel()); m_applet->UpdateSuspensionStateLocked(true); } else { m_applet->process->Terminate(); diff --git a/src/core/hle/service/am/service/application_creator.cpp b/src/core/hle/service/am/service/application_creator.cpp index 81620a5067..e3bb92c3a4 100644 --- a/src/core/hle/service/am/service/application_creator.cpp +++ b/src/core/hle/service/am/service/application_creator.cpp @@ -21,8 +21,7 @@ namespace Service::AM { namespace { -Result CreateGuestApplication(SharedPointer* out_application_accessor, - Core::System& system, WindowSystem& window_system, u64 program_id) { +Result CreateGuestApplication(SharedPointer* out_application_accessor, Core::System& system, WindowSystem& window_system, u64 program_id) { FileSys::VirtualFile nca_raw{}; // Get the program NCA from storage. @@ -35,8 +34,7 @@ Result CreateGuestApplication(SharedPointer* out_applicati std::vector control; std::unique_ptr loader; Loader::ResultStatus result; - auto process = - CreateApplicationProcess(control, loader, result, system, nca_raw, program_id, 0); + auto process = CreateApplicationProcess(control, loader, result, system, nca_raw, program_id, 0); R_UNLESS(process != nullptr, ResultUnknown); const auto applet = std::make_shared(system, std::move(process), true); @@ -47,8 +45,7 @@ Result CreateGuestApplication(SharedPointer* out_applicati window_system.TrackApplet(applet, true); - *out_application_accessor = - std::make_shared(system, applet, window_system); + *out_application_accessor = std::make_shared(system, applet, window_system); R_SUCCEED(); } @@ -90,9 +87,7 @@ Result IApplicationCreator::CreateSystemApplication( std::vector control; std::unique_ptr loader; - - auto process = - CreateProcess(system, application_id, 1, 22); + auto process = CreateProcess(system, application_id, 1, 22); R_UNLESS(process != nullptr, ResultUnknown); const auto applet = std::make_shared(system, std::move(process), true); @@ -103,8 +98,7 @@ Result IApplicationCreator::CreateSystemApplication( m_window_system.TrackApplet(applet, true); - *out_application_accessor = - std::make_shared(system, applet, m_window_system); + *out_application_accessor = std::make_shared(system, applet, m_window_system); Core::LaunchTimestampCache::SaveLaunchTimestamp(application_id); R_SUCCEED(); } diff --git a/src/core/hle/service/am/service/application_functions.cpp b/src/core/hle/service/am/service/application_functions.cpp index 9ab343e59e..787c88f52d 100644 --- a/src/core/hle/service/am/service/application_functions.cpp +++ b/src/core/hle/service/am/service/application_functions.cpp @@ -11,6 +11,7 @@ #include "core/file_sys/registered_cache.h" #include "core/file_sys/savedata_factory.h" #include "core/hle/kernel/k_transfer_memory.h" +#include "core/hle/result.h" #include "core/hle/service/am/am_results.h" #include "core/hle/service/am/applet.h" #include "core/hle/service/am/service/application_functions.h" @@ -56,7 +57,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_, std::shared_ {37, nullptr, "GetLimitedApplicationLicenseUpgradableEvent"}, {40, D<&IApplicationFunctions::NotifyRunning>, "NotifyRunning"}, {50, D<&IApplicationFunctions::GetPseudoDeviceId>, "GetPseudoDeviceId"}, - {60, nullptr, "SetMediaPlaybackStateForApplication"}, + {60, D<&IApplicationFunctions::SetMediaPlaybackStateForApplication>, "SetMediaPlaybackStateForApplication"}, {65, D<&IApplicationFunctions::IsGamePlayRecordingSupported>, "IsGamePlayRecordingSupported"}, {66, D<&IApplicationFunctions::InitializeGamePlayRecording>, "InitializeGamePlayRecording"}, {67, D<&IApplicationFunctions::SetGamePlayRecordingState>, "SetGamePlayRecordingState"}, @@ -364,6 +365,13 @@ Result IApplicationFunctions::InitializeGamePlayRecording( R_SUCCEED(); } +Result IApplicationFunctions::SetMediaPlaybackStateForApplication(bool enabled) { + LOG_WARNING(Service_AM, "(stubbed) {}", enabled); + std::scoped_lock lk{m_applet->lock}; + m_applet->media_playback_state = enabled; + R_SUCCEED(); +} + Result IApplicationFunctions::SetGamePlayRecordingState( GamePlayRecordingState game_play_recording_state) { LOG_WARNING(Service_AM, "(STUBBED) called"); diff --git a/src/core/hle/service/am/service/application_functions.h b/src/core/hle/service/am/service/application_functions.h index abee2f9d47..b97ed7e25e 100644 --- a/src/core/hle/service/am/service/application_functions.h +++ b/src/core/hle/service/am/service/application_functions.h @@ -53,6 +53,7 @@ private: Result IsGamePlayRecordingSupported(Out out_is_game_play_recording_supported); Result InitializeGamePlayRecording( u64 transfer_memory_size, InCopyHandle transfer_memory_handle); + Result SetMediaPlaybackStateForApplication(bool enabled); Result SetGamePlayRecordingState(GamePlayRecordingState game_play_recording_state); Result EnableApplicationCrashReport(bool enabled); Result InitializeApplicationCopyrightFrameBuffer( diff --git a/src/core/hle/service/am/service/common_state_getter.cpp b/src/core/hle/service/am/service/common_state_getter.cpp index 5b5e21b8e0..23f67dcf17 100644 --- a/src/core/hle/service/am/service/common_state_getter.cpp +++ b/src/core/hle/service/am/service/common_state_getter.cpp @@ -99,7 +99,7 @@ Result ICommonStateGetter::GetEventHandle(OutCopyHandle Result ICommonStateGetter::ReceiveMessage(Out out_applet_message) { LOG_DEBUG(Service_AM, "called"); - if (!m_applet->lifecycle_manager.PopMessage(out_applet_message)) { + if (!m_applet->lifecycle_manager.PopMessage(system.Kernel(), out_applet_message)) { LOG_ERROR(Service_AM, "Tried to pop message but none was available!"); R_THROW(AM::ResultNoMessages); } @@ -123,21 +123,21 @@ Result ICommonStateGetter::RequestToAcquireSleepLock() { LOG_WARNING(Service_AM, "(STUBBED) called"); // Sleep lock is acquired immediately. - m_applet->sleep_lock_event.Signal(); + m_applet->sleep_lock_event.Signal(system.Kernel()); R_SUCCEED(); } Result ICommonStateGetter::ReleaseSleepLock() { LOG_WARNING(Service_AM, "(STUBBED) called"); - m_applet->sleep_lock_event.Clear(); + m_applet->sleep_lock_event.Clear(system.Kernel()); R_SUCCEED(); } Result ICommonStateGetter::ReleaseSleepLockTransiently() { LOG_WARNING(Service_AM, "(STUBBED) called"); - m_applet->sleep_lock_event.Clear(); + m_applet->sleep_lock_event.Clear(system.Kernel()); R_SUCCEED(); } @@ -281,25 +281,25 @@ Result ICommonStateGetter::PerformSystemButtonPressingIfInFocus(SystemButtonType switch (type) { case SystemButtonType::HomeButtonShortPressing: if (!m_applet->home_button_short_pressed_blocked) { - m_applet->lifecycle_manager.PushUnorderedMessage( + m_applet->lifecycle_manager.PushUnorderedMessage(system.Kernel(), AppletMessage::DetectShortPressingHomeButton); } break; case SystemButtonType::HomeButtonLongPressing: if (!m_applet->home_button_long_pressed_blocked) { - m_applet->lifecycle_manager.PushUnorderedMessage( + m_applet->lifecycle_manager.PushUnorderedMessage(system.Kernel(), AppletMessage::DetectLongPressingHomeButton); } break; case SystemButtonType::CaptureButtonShortPressing: if (m_applet->handling_capture_button_short_pressed_message_enabled_for_applet) { - m_applet->lifecycle_manager.PushUnorderedMessage( + m_applet->lifecycle_manager.PushUnorderedMessage(system.Kernel(), AppletMessage::DetectShortPressingCaptureButton); } break; case SystemButtonType::CaptureButtonLongPressing: if (m_applet->handling_capture_button_long_pressed_message_enabled_for_applet) { - m_applet->lifecycle_manager.PushUnorderedMessage( + m_applet->lifecycle_manager.PushUnorderedMessage(system.Kernel(), AppletMessage::DetectLongPressingCaptureButton); } break; diff --git a/src/core/hle/service/am/service/library_applet_accessor.cpp b/src/core/hle/service/am/service/library_applet_accessor.cpp index ef91f69c23..c49b1f3f05 100644 --- a/src/core/hle/service/am/service/library_applet_accessor.cpp +++ b/src/core/hle/service/am/service/library_applet_accessor.cpp @@ -75,7 +75,7 @@ ILibraryAppletAccessor::ILibraryAppletAccessor(Core::System& system_, {105, D<&ILibraryAppletAccessor::GetPopOutDataEvent>, "GetPopOutDataEvent"}, {106, D<&ILibraryAppletAccessor::GetPopInteractiveOutDataEvent>, "GetPopInteractiveOutDataEvent"}, {110, nullptr, "NeedsToExitProcess"}, - {120, nullptr, "GetLibraryAppletInfo"}, + {120, D<&ILibraryAppletAccessor::GetLibraryAppletInfo>, "GetLibraryAppletInfo"}, {150, nullptr, "RequestForAppletToGetForeground"}, {160, D<&ILibraryAppletAccessor::GetIndirectLayerConsumerHandle>, "GetIndirectLayerConsumerHandle"}, //2.0.0+ {170, D<&ILibraryAppletAccessor::Unknown170>, "Unknown170"}, //22.0.0+ @@ -123,7 +123,7 @@ Result ILibraryAppletAccessor::RequestExit() { LOG_DEBUG(Service_AM, "called"); { std::scoped_lock lk{m_applet->lock}; - m_applet->lifecycle_manager.RequestExit(); + m_applet->lifecycle_manager.RequestExit(system.Kernel()); } FrontendRequestExit(); R_SUCCEED(); @@ -157,7 +157,7 @@ Result ILibraryAppletAccessor::PushInData(SharedPointer storage) { } } - m_broker->GetInData().Push(storage); + m_broker->GetInData().Push(system.Kernel(), storage); R_SUCCEED(); } @@ -165,13 +165,13 @@ Result ILibraryAppletAccessor::PopOutData(Out> out_stora LOG_DEBUG(Service_AM, "called"); if (auto caller_applet = m_applet->caller_applet.lock(); caller_applet) { - caller_applet->lifecycle_manager.GetSystemEvent().Signal(); + caller_applet->lifecycle_manager.GetSystemEvent().Signal(system.Kernel()); caller_applet->lifecycle_manager.RequestResumeNotification(); - caller_applet->lifecycle_manager.GetSystemEvent().Clear(); + caller_applet->lifecycle_manager.GetSystemEvent().Clear(system.Kernel()); caller_applet->lifecycle_manager.UpdateRequestedFocusState(); } - R_TRY(m_broker->GetOutData().Pop(out_storage.Get())); + R_TRY(m_broker->GetOutData().Pop(system.Kernel(), out_storage.Get())); if (m_applet->applet_id == AppletId::ProfileSelect && *out_storage) { auto impl = (*out_storage)->GetImpl(); @@ -186,14 +186,14 @@ Result ILibraryAppletAccessor::PopOutData(Out> out_stora Result ILibraryAppletAccessor::PushInteractiveInData(SharedPointer storage) { LOG_DEBUG(Service_AM, "called"); - m_broker->GetInteractiveInData().Push(storage); + m_broker->GetInteractiveInData().Push(system.Kernel(), storage); FrontendExecuteInteractive(); R_SUCCEED(); } Result ILibraryAppletAccessor::PopInteractiveOutData(Out> out_storage) { LOG_DEBUG(Service_AM, "called"); - R_RETURN(m_broker->GetInteractiveOutData().Pop(out_storage.Get())); + R_RETURN(m_broker->GetInteractiveOutData().Pop(system.Kernel(), out_storage.Get())); } Result ILibraryAppletAccessor::GetPopOutDataEvent(OutCopyHandle out_event) { @@ -218,6 +218,16 @@ Result ILibraryAppletAccessor::GetIndirectLayerConsumerHandle(Out out_handl R_SUCCEED(); } +Result ILibraryAppletAccessor::GetLibraryAppletInfo( + Out out_library_applet_info) { + LOG_INFO(Service_AM, "called"); + *out_library_applet_info = { + .applet_id = m_applet->applet_id, + .library_applet_mode = m_applet->library_applet_mode, + }; + R_SUCCEED(); +} + Result ILibraryAppletAccessor::Unknown170(OutCopyHandle out_event) { LOG_WARNING(Service_AM, "(STUBBED) called"); *out_event = m_applet->unknown_event.GetHandle(); diff --git a/src/core/hle/service/am/service/library_applet_accessor.h b/src/core/hle/service/am/service/library_applet_accessor.h index 02fcd07fd3..ad40bc76c7 100644 --- a/src/core/hle/service/am/service/library_applet_accessor.h +++ b/src/core/hle/service/am/service/library_applet_accessor.h @@ -6,6 +6,7 @@ #pragma once +#include "core/hle/service/am/service/library_applet_self_accessor.h" #include "core/hle/service/cmif_types.h" #include "core/hle/service/service.h" @@ -21,6 +22,10 @@ public: std::shared_ptr applet); ~ILibraryAppletAccessor(); + std::shared_ptr GetApplet() const { + return m_applet; + } + private: Result GetAppletStateChangedEvent(OutCopyHandle out_event); Result IsCompleted(Out out_is_completed); @@ -37,6 +42,7 @@ private: Result GetPopOutDataEvent(OutCopyHandle out_event); Result GetPopInteractiveOutDataEvent(OutCopyHandle out_event); Result GetIndirectLayerConsumerHandle(Out out_handle); + Result GetLibraryAppletInfo(Out out_library_applet_info); Result Unknown170(OutCopyHandle out_event); void FrontendExecute(); diff --git a/src/core/hle/service/am/service/library_applet_creator.cpp b/src/core/hle/service/am/service/library_applet_creator.cpp index 3de5740237..c20a77f8c4 100644 --- a/src/core/hle/service/am/service/library_applet_creator.cpp +++ b/src/core/hle/service/am/service/library_applet_creator.cpp @@ -122,26 +122,23 @@ std::shared_ptr CreateGuestApplet(Core::System& system, }; auto process = CreateProcess(system, program_id, Firmware1400, Firmware2200); - if (!process) { - // Couldn't initialize the guest process - return {}; + if (process) { + const auto applet = std::make_shared(system, std::move(process), false); + applet->program_id = program_id; + applet->applet_id = applet_id; + applet->type = AppletType::LibraryApplet; + applet->library_applet_mode = mode; + applet->window_visible = mode != LibraryAppletMode::AllForegroundInitiallyHidden; + + auto broker = std::make_shared(system); + applet->caller_applet = caller_applet; + applet->caller_applet_broker = broker; + caller_applet->child_applets.push_back(applet); + window_system.TrackApplet(applet, false); + return std::make_shared(system, broker, applet); } - - const auto applet = std::make_shared(system, std::move(process), false); - applet->program_id = program_id; - applet->applet_id = applet_id; - applet->type = AppletType::LibraryApplet; - applet->library_applet_mode = mode; - applet->window_visible = mode != LibraryAppletMode::AllForegroundInitiallyHidden; - - auto broker = std::make_shared(system); - applet->caller_applet = caller_applet; - applet->caller_applet_broker = broker; - caller_applet->child_applets.push_back(applet); - - window_system.TrackApplet(applet, false); - - return std::make_shared(system, broker, applet); + // Couldn't initialize the guest process + return {}; } std::shared_ptr CreateFrontendApplet(Core::System& system, @@ -210,7 +207,7 @@ Result ILibraryAppletCreator::CreateLibraryApplet( } // Applet is created, can now be launched. - m_applet->library_applet_launchable_event.Signal(); + m_applet->library_applet_launchable_event.Signal(system.Kernel()); *out_library_applet_accessor = library_applet; R_SUCCEED(); } @@ -236,7 +233,7 @@ Result ILibraryAppletCreator::CreateLibraryAppletEx( } // Applet is created, can now be launched. - m_applet->library_applet_launchable_event.Signal(); + m_applet->library_applet_launchable_event.Signal(system.Kernel()); *out_library_applet_accessor = library_applet; R_SUCCEED(); } @@ -269,7 +266,7 @@ Result ILibraryAppletCreator::CreateTransferMemoryStorage( } *out_storage = std::make_shared( - system, AM::CreateTransferMemoryStorage(transfer_memory_handle->GetOwner()->GetMemory(), + system, AM::CreateTransferMemoryStorage(system.Kernel(), transfer_memory_handle->GetOwner()->GetMemory(), transfer_memory_handle.Get(), is_writable, size)); R_SUCCEED(); } @@ -290,7 +287,7 @@ Result ILibraryAppletCreator::CreateHandleStorage( } *out_storage = std::make_shared( - system, AM::CreateHandleStorage(transfer_memory_handle->GetOwner()->GetMemory(), + system, AM::CreateHandleStorage(system.Kernel(), transfer_memory_handle->GetOwner()->GetMemory(), transfer_memory_handle.Get(), size)); R_SUCCEED(); } diff --git a/src/core/hle/service/am/service/library_applet_self_accessor.cpp b/src/core/hle/service/am/service/library_applet_self_accessor.cpp index 4a1d1f97d3..b16bf87979 100644 --- a/src/core/hle/service/am/service/library_applet_self_accessor.cpp +++ b/src/core/hle/service/am/service/library_applet_self_accessor.cpp @@ -93,23 +93,23 @@ ILibraryAppletSelfAccessor::~ILibraryAppletSelfAccessor() = default; Result ILibraryAppletSelfAccessor::PopInData(Out> out_storage) { LOG_INFO(Service_AM, "called"); - R_RETURN(m_broker->GetInData().Pop(out_storage)); + R_RETURN(m_broker->GetInData().Pop(system.Kernel(), out_storage)); } Result ILibraryAppletSelfAccessor::PushOutData(SharedPointer storage) { LOG_INFO(Service_AM, "called"); - m_broker->GetOutData().Push(storage); + m_broker->GetOutData().Push(system.Kernel(), storage); R_SUCCEED(); } Result ILibraryAppletSelfAccessor::PopInteractiveInData(Out> out_storage) { LOG_INFO(Service_AM, "called"); - R_RETURN(m_broker->GetInteractiveInData().Pop(out_storage)); + R_RETURN(m_broker->GetInteractiveInData().Pop(system.Kernel(), out_storage)); } Result ILibraryAppletSelfAccessor::PushInteractiveOutData(SharedPointer storage) { LOG_INFO(Service_AM, "called"); - m_broker->GetInteractiveOutData().Push(storage); + m_broker->GetInteractiveOutData().Push(system.Kernel(), storage); R_SUCCEED(); } @@ -233,8 +233,9 @@ Result ILibraryAppletSelfAccessor::ReportVisibleErrorWithErrorContext( R_SUCCEED(); } -Result ILibraryAppletSelfAccessor::UnpopInData() { - LOG_WARNING(Service_AM, "(STUBBED) called"); +Result ILibraryAppletSelfAccessor::UnpopInData(SharedPointer storage) { + LOG_INFO(Service_AM, "called"); + m_broker->GetInData().Unpop(system.Kernel(), storage); R_SUCCEED(); } diff --git a/src/core/hle/service/am/service/library_applet_self_accessor.h b/src/core/hle/service/am/service/library_applet_self_accessor.h index 78864a94eb..d287f6b403 100644 --- a/src/core/hle/service/am/service/library_applet_self_accessor.h +++ b/src/core/hle/service/am/service/library_applet_self_accessor.h @@ -72,7 +72,7 @@ private: Result ReportVisibleError(ErrorCode error_code); Result ReportVisibleErrorWithErrorContext( ErrorCode error_code, InLargeData error_context); - Result UnpopInData(); + Result UnpopInData(SharedPointer storage); Result GetMainAppletApplicationDesiredLanguage(Out out_desired_language); Result GetCurrentApplicationId(Out out_application_id); Result GetMainAppletAvailableUsers(Out out_can_select_any_user, Out out_users_count, diff --git a/src/core/hle/service/am/service/lock_accessor.cpp b/src/core/hle/service/am/service/lock_accessor.cpp index 8e556fdd68..0341254449 100644 --- a/src/core/hle/service/am/service/lock_accessor.cpp +++ b/src/core/hle/service/am/service/lock_accessor.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -20,7 +23,7 @@ ILockAccessor::ILockAccessor(Core::System& system_) RegisterHandlers(functions); - m_event.Signal(); + m_event.Signal(system.Kernel()); } ILockAccessor::~ILockAccessor() = default; @@ -55,7 +58,7 @@ Result ILockAccessor::Unlock() { m_is_locked = false; } - m_event.Signal(); + m_event.Signal(system.Kernel()); R_SUCCEED(); } diff --git a/src/core/hle/service/am/service/process_winding_controller.cpp b/src/core/hle/service/am/service/process_winding_controller.cpp index 30529de550..e284aa2d89 100644 --- a/src/core/hle/service/am/service/process_winding_controller.cpp +++ b/src/core/hle/service/am/service/process_winding_controller.cpp @@ -1,9 +1,11 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "core/core.h" +#include "core/hle/service/am/applet.h" #include "core/hle/service/am/frontend/applets.h" #include "core/hle/service/am/service/library_applet_accessor.h" #include "core/hle/service/am/service/process_winding_controller.h" @@ -43,6 +45,18 @@ Result IProcessWindingController::OpenCallingLibraryApplet( Out> out_calling_library_applet) { LOG_INFO(Service_AM, "called"); + std::shared_ptr reserved_applet; + { + std::scoped_lock lk{m_applet->lock}; + reserved_applet = std::move(m_applet->reserved_applet); + } + + if (reserved_applet != nullptr) { + *out_calling_library_applet = std::make_shared( + system, reserved_applet->caller_applet_broker, reserved_applet); + R_SUCCEED(); + } + const auto caller_applet = m_applet->caller_applet.lock(); if (caller_applet == nullptr) { LOG_ERROR(Service_AM, "No caller applet available"); @@ -74,22 +88,82 @@ Result IProcessWindingController::PopContext(Out> out_co } Result IProcessWindingController::CancelWindingReservation() { - LOG_WARNING(Service_AM, "STUBBED"); + LOG_INFO(Service_AM, "called"); + + std::scoped_lock lk{m_applet->lock}; + m_applet->reserved_applet.reset(); + m_applet->unwind_after_reserved = false; + R_SUCCEED(); } Result IProcessWindingController::WindAndDoReserved() { - LOG_WARNING(Service_AM, "STUBBED"); + LOG_INFO(Service_AM, "called"); + + std::shared_ptr reserved_applet; + { + std::scoped_lock lk{m_applet->lock}; + + reserved_applet = m_applet->reserved_applet; + m_applet->display_layer_manager.SetWindowVisibility(false); + m_applet->exit_locked = false; + system.SetExitLocked(false); + } + + if (reserved_applet) { + { + std::scoped_lock lk{m_applet->lock}; + m_applet->is_winding = true; + } + + { + std::scoped_lock lk{reserved_applet->lock}; + reserved_applet->window_visible = true; + reserved_applet->process->Run(); + } + + if (reserved_applet->frontend) { + reserved_applet->frontend->Initialize(); + reserved_applet->frontend->Execute(); + } + } else { + LOG_WARNING(Service_AM, "called without a reserved applet to start"); + } + + m_applet->process->Terminate(); + R_SUCCEED(); } -Result IProcessWindingController::ReserveToStartAndWaitAndUnwindThis() { - LOG_WARNING(Service_AM, "STUBBED"); +Result IProcessWindingController::ReserveToStartAndWaitAndUnwindThis( + SharedPointer reserved_applet_accessor) { + LOG_INFO(Service_AM, "called"); + + if (reserved_applet_accessor == nullptr) { + LOG_ERROR(Service_AM, "No applet accessor provided"); + R_THROW(ResultUnknown); + } + + std::scoped_lock lk{m_applet->lock}; + m_applet->reserved_applet = reserved_applet_accessor->GetApplet(); + m_applet->unwind_after_reserved = true; + R_SUCCEED(); } -Result IProcessWindingController::ReserveToStartAndWait() { - LOG_WARNING(Service_AM, "STUBBED"); +Result IProcessWindingController::ReserveToStartAndWait( + SharedPointer reserved_applet_accessor) { + LOG_INFO(Service_AM, "called"); + + if (reserved_applet_accessor == nullptr) { + LOG_ERROR(Service_AM, "No applet accessor provided"); + R_THROW(ResultUnknown); + } + + std::scoped_lock lk{m_applet->lock}; + m_applet->reserved_applet = reserved_applet_accessor->GetApplet(); + m_applet->unwind_after_reserved = false; + R_SUCCEED(); } diff --git a/src/core/hle/service/am/service/process_winding_controller.h b/src/core/hle/service/am/service/process_winding_controller.h index 0d53223033..f3417bcd97 100644 --- a/src/core/hle/service/am/service/process_winding_controller.h +++ b/src/core/hle/service/am/service/process_winding_controller.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project @@ -29,8 +29,9 @@ private: Result PopContext(Out> out_context); Result CancelWindingReservation(); Result WindAndDoReserved(); - Result ReserveToStartAndWaitAndUnwindThis(); - Result ReserveToStartAndWait(); + Result ReserveToStartAndWaitAndUnwindThis( + SharedPointer reserved_applet_accessor); + Result ReserveToStartAndWait(SharedPointer reserved_applet_accessor); const std::shared_ptr m_applet; }; diff --git a/src/core/hle/service/am/service/self_controller.cpp b/src/core/hle/service/am/service/self_controller.cpp index 89e393bb97..4279483727 100644 --- a/src/core/hle/service/am/service/self_controller.cpp +++ b/src/core/hle/service/am/service/self_controller.cpp @@ -151,7 +151,7 @@ Result ISelfController::GetLibraryAppletLaunchableEvent( OutCopyHandle out_event) { LOG_WARNING(Service_AM, "(STUBBED) called"); - m_applet->library_applet_launchable_event.Signal(); + m_applet->library_applet_launchable_event.Signal(system.Kernel()); *out_event = m_applet->library_applet_launchable_event.GetHandle(); R_SUCCEED(); @@ -170,7 +170,7 @@ Result ISelfController::SetOperationModeChangedNotification(bool enabled) { LOG_INFO(Service_AM, "called, enabled={}", enabled); std::scoped_lock lk{m_applet->lock}; - m_applet->lifecycle_manager.SetOperationModeChangedNotificationEnabled(enabled); + m_applet->lifecycle_manager.SetOperationModeChangedNotificationEnabled(system.Kernel(), enabled); R_SUCCEED(); } @@ -179,7 +179,7 @@ Result ISelfController::SetPerformanceModeChangedNotification(bool enabled) { LOG_INFO(Service_AM, "called, enabled={}", enabled); std::scoped_lock lk{m_applet->lock}; - m_applet->lifecycle_manager.SetPerformanceModeChangedNotificationEnabled(enabled); + m_applet->lifecycle_manager.SetPerformanceModeChangedNotificationEnabled(system.Kernel(), enabled); R_SUCCEED(); } @@ -188,7 +188,7 @@ Result ISelfController::SetFocusHandlingMode(bool notify, bool background, bool LOG_INFO(Service_AM, "called, notify={} background={} suspend={}", notify, background, suspend); std::scoped_lock lk{m_applet->lock}; - m_applet->lifecycle_manager.SetFocusStateChangedNotificationEnabled(notify); + m_applet->lifecycle_manager.SetFocusStateChangedNotificationEnabled(system.Kernel(), notify); m_applet->lifecycle_manager.SetFocusHandlingMode(suspend); m_applet->UpdateSuspensionStateLocked(true); diff --git a/src/core/hle/service/am/window_system.cpp b/src/core/hle/service/am/window_system.cpp index 5f20a98322..bbaea8ad12 100644 --- a/src/core/hle/service/am/window_system.cpp +++ b/src/core/hle/service/am/window_system.cpp @@ -9,6 +9,7 @@ #include "core/hle/service/am/applet.h" #include "core/hle/service/am/applet_manager.h" #include "core/hle/service/am/event_observer.h" +#include "core/hle/service/am/process_creation.h" #include "core/hle/service/am/window_system.h" namespace Service::AM { @@ -154,7 +155,7 @@ void WindowSystem::OnOperationModeChanged() { for (const auto& [aruid, applet] : m_applets) { std::scoped_lock lk2{applet->lock}; - applet->lifecycle_manager.OnOperationAndPerformanceModeChanged(); + applet->lifecycle_manager.OnOperationAndPerformanceModeChanged(m_system.Kernel()); } } @@ -163,22 +164,22 @@ void WindowSystem::OnExitRequested() { for (const auto& [aruid, applet] : m_applets) { std::scoped_lock lk2{applet->lock}; - applet->lifecycle_manager.RequestExit(); + applet->lifecycle_manager.RequestExit(m_system.Kernel()); } } void WindowSystem::SendButtonAppletMessageLocked(AppletMessage message) { if (m_home_menu) { std::scoped_lock lk_home{m_home_menu->lock}; - m_home_menu->lifecycle_manager.PushUnorderedMessage(message); + m_home_menu->lifecycle_manager.PushUnorderedMessage(m_system.Kernel(), message); } if (m_overlay_display) { std::scoped_lock lk_overlay{m_overlay_display->lock}; - m_overlay_display->lifecycle_manager.PushUnorderedMessage(message); + m_overlay_display->lifecycle_manager.PushUnorderedMessage(m_system.Kernel(), message); } if (m_application) { std::scoped_lock lk_application{m_application->lock}; - m_application->lifecycle_manager.PushUnorderedMessage(message); + m_application->lifecycle_manager.PushUnorderedMessage(m_system.Kernel(), message); } if (m_event_observer) { m_event_observer->RequestUpdate(); @@ -240,6 +241,37 @@ void WindowSystem::PruneTerminatedAppletsLocked() { continue; } + // A winding applet has had its own process killed but is kept alive as a transparent slot + // while the reserved applet running in its place finishes (see WindAndDoReserved()). + if (applet->is_winding) { + if (!applet->child_applets.empty()) { + it = std::next(it); + continue; + } + + const bool unwind = applet->unwind_after_reserved; + applet->is_winding = false; + applet->unwind_after_reserved = false; + + if (unwind && this->RestartAppletProcessLocked(applet.get())) { + const u64 new_aruid = applet->aruid.pid; + const auto next = std::next(it); + + if (new_aruid != aruid) { + auto node = m_applets.extract(it); + node.key() = new_aruid; + m_applets.insert(std::move(node)); + } + + applet->process->Run(); + m_event_observer->RequestUpdate(); + it = next; + continue; + } + + applet->reserved_applet.reset(); + } + // Terminated, so ensure all child applets are terminated. if (!applet->child_applets.empty()) { this->TerminateChildAppletsLocked(applet.get()); @@ -277,8 +309,7 @@ void WindowSystem::PruneTerminatedAppletsLocked() { // If we have a home menu, send it the application exited message. if (m_home_menu) { - m_home_menu->lifecycle_manager.PushUnorderedMessage( - AppletMessage::ApplicationExited); + m_home_menu->lifecycle_manager.PushUnorderedMessage(m_system.Kernel(), AppletMessage::ApplicationExited); } } @@ -302,6 +333,28 @@ void WindowSystem::PruneTerminatedAppletsLocked() { } } +bool WindowSystem::RestartAppletProcessLocked(Applet* applet) { + if (!ReinitializeProcess(m_system, *applet->process, applet->program_id)) { + LOG_ERROR(Service_AM, "Failed to restart winding applet_id={}", + static_cast(applet->applet_id)); + return false; + } + + applet->aruid.pid = applet->process->GetProcessId(); + applet->is_process_running = false; + applet->is_completed = false; + + applet->hid_registration.RegisterCurrentProcess(); + + applet->lifecycle_manager.ResetForRelaunch(); + applet->is_activity_runnable = false; + + applet->launch_reason.flag = 1; + + m_event_observer->TrackAppletProcess(*applet); + return true; +} + bool WindowSystem::LockHomeMenuIntoForegroundLocked() { // If the home menu is not locked into foreground, then there's nothing to do. if (m_home_menu == nullptr || !m_home_menu_foreground_locked) { @@ -353,6 +406,9 @@ void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground, b const bool has_obscuring_child_applets = [&] { for (const auto& child_applet : applet->child_applets) { std::scoped_lock lk2{child_applet->lock}; + if (child_applet->is_winding) { + return true; + } const auto mode = child_applet->library_applet_mode; if (child_applet->is_process_running && child_applet->window_visible && (mode == LibraryAppletMode::AllForeground || diff --git a/src/core/hle/service/am/window_system.h b/src/core/hle/service/am/window_system.h index c331d10c92..485e57f902 100644 --- a/src/core/hle/service/am/window_system.h +++ b/src/core/hle/service/am/window_system.h @@ -61,6 +61,7 @@ public: private: void PruneTerminatedAppletsLocked(); + bool RestartAppletProcessLocked(Applet* applet); bool LockHomeMenuIntoForegroundLocked(); void TerminateChildAppletsLocked(Applet* applet); void UpdateAppletStateLocked(Applet* applet, bool is_foreground, bool overlay_blocking = false); diff --git a/src/core/hle/service/apm/apm.cpp b/src/core/hle/service/apm/apm.cpp index c23ff293d3..d73d08cb72 100644 --- a/src/core/hle/service/apm/apm.cpp +++ b/src/core/hle/service/apm/apm.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -15,12 +18,11 @@ void LoopProcess(Core::System& system) { auto module = std::make_shared(); auto server_manager = std::make_unique(system); - server_manager->RegisterNamedService( - "apm", std::make_shared(system, module, system.GetAPMController(), "apm")); - server_manager->RegisterNamedService( - "apm:am", std::make_shared(system, module, system.GetAPMController(), "apm:am")); - server_manager->RegisterNamedService( - "apm:sys", std::make_shared(system, system.GetAPMController())); + server_manager->RegisterNamedService("apm", std::make_shared(system, module, system.GetAPMController(), "apm")); + server_manager->RegisterNamedService("apm:am", std::make_shared(system, module, system.GetAPMController(), "apm:am")); + // Removed on [+8.0.0] but kept for compatibility + server_manager->RegisterNamedService("apm:p", std::make_shared(system, module, system.GetAPMController(), "apm:p")); + server_manager->RegisterNamedService("apm:sys", std::make_shared(system, system.GetAPMController())); ServerManager::RunServer(std::move(server_manager)); } diff --git a/src/core/hle/service/audio/audio_device.cpp b/src/core/hle/service/audio/audio_device.cpp index eb64892ed8..e9f7e074a1 100644 --- a/src/core/hle/service/audio/audio_device.cpp +++ b/src/core/hle/service/audio/audio_device.cpp @@ -42,7 +42,7 @@ IAudioDevice::IAudioDevice(Core::System& system_, u64 applet_resource_user_id, u }; RegisterHandlers(functions); - event->Signal(); + event->Signal(system.Kernel()); } IAudioDevice::~IAudioDevice() { @@ -128,7 +128,7 @@ Result IAudioDevice::GetActiveAudioDeviceNameAuto( Result IAudioDevice::QueryAudioDeviceSystemEvent(OutCopyHandle out_event) { LOG_DEBUG(Service_Audio, "(STUBBED) called"); - event->Signal(); + event->Signal(system.Kernel()); *out_event = &event->GetReadableEvent(); R_SUCCEED(); } diff --git a/src/core/hle/service/audio/audio_in.cpp b/src/core/hle/service/audio/audio_in.cpp index 416803acc3..6208c2d104 100644 --- a/src/core/hle/service/audio/audio_in.cpp +++ b/src/core/hle/service/audio/audio_in.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -37,7 +40,7 @@ IAudioIn::IAudioIn(Core::System& system_, Manager& manager, size_t session_id, RegisterHandlers(functions); - process->Open(); + process->Open(system.Kernel()); if (impl->GetSystem() .Initialize(device_name, in_params, handle, applet_resource_user_id) @@ -49,7 +52,7 @@ IAudioIn::IAudioIn(Core::System& system_, Manager& manager, size_t session_id, IAudioIn::~IAudioIn() { impl->Free(); service_context.CloseEvent(event); - process->Close(); + process->Close(system.Kernel()); } Result IAudioIn::GetAudioInState(Out out_state) { diff --git a/src/core/hle/service/audio/audio_out.cpp b/src/core/hle/service/audio/audio_out.cpp index 53009d5d74..996eeede58 100644 --- a/src/core/hle/service/audio/audio_out.cpp +++ b/src/core/hle/service/audio/audio_out.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -39,13 +42,13 @@ IAudioOut::IAudioOut(Core::System& system_, Manager& manager, size_t session_id, // clang-format on RegisterHandlers(functions); - process->Open(); + process->Open(system.Kernel()); } IAudioOut::~IAudioOut() { impl->Free(); service_context.CloseEvent(event); - process->Close(); + process->Close(system.Kernel()); } Result IAudioOut::GetAudioOutState(Out out_state) { diff --git a/src/core/hle/service/audio/audio_out_manager.cpp b/src/core/hle/service/audio/audio_out_manager.cpp index 0a8e1ec256..3b2087932c 100644 --- a/src/core/hle/service/audio/audio_out_manager.cpp +++ b/src/core/hle/service/audio/audio_out_manager.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project @@ -14,7 +14,9 @@ namespace Service::Audio { using namespace AudioCore::AudioOut; IAudioOutManager::IAudioOutManager(Core::System& system_) - : ServiceFramework{system_, "audout:u"}, impl{std::make_unique(system_)} { + : ServiceFramework{system_, "audout:u"} + , impl(system_) +{ // clang-format off static const FunctionInfo functions[] = { {0, D<&IAudioOutManager::ListAudioOuts>, "ListAudioOuts"}, diff --git a/src/core/hle/service/audio/audio_out_manager.h b/src/core/hle/service/audio/audio_out_manager.h index 791274d5e9..ec974aaba4 100644 --- a/src/core/hle/service/audio/audio_out_manager.h +++ b/src/core/hle/service/audio/audio_out_manager.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project @@ -43,7 +43,7 @@ private: AudioCore::AudioOut::AudioOutParameter parameter, InCopyHandle process_handle, ClientAppletResourceUserId aruid); - std::unique_ptr impl; + std::optional impl; }; } // namespace Service::Audio diff --git a/src/core/hle/service/audio/audio_renderer.cpp b/src/core/hle/service/audio/audio_renderer.cpp index 21cc742b69..49c6083109 100644 --- a/src/core/hle/service/audio/audio_renderer.cpp +++ b/src/core/hle/service/audio/audio_renderer.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project @@ -39,7 +39,7 @@ IAudioRenderer::IAudioRenderer(Core::System& system_, Manager& manager_, // clang-format on RegisterHandlers(functions); - process_handle->Open(); + process_handle->Open(system_.Kernel()); impl->Initialize(params, transfer_memory, transfer_memory_size, process_handle, applet_resource_user_id, session_id); } @@ -47,7 +47,7 @@ IAudioRenderer::IAudioRenderer(Core::System& system_, Manager& manager_, IAudioRenderer::~IAudioRenderer() { impl->Finalize(); service_context.CloseEvent(rendered_event); - process_handle->Close(); + process_handle->Close(system.Kernel()); } Result IAudioRenderer::GetSampleRate(Out out_sample_rate) { diff --git a/src/core/hle/service/audio/audio_renderer_manager.cpp b/src/core/hle/service/audio/audio_renderer_manager.cpp index 6a1345c074..972e930a89 100644 --- a/src/core/hle/service/audio/audio_renderer_manager.cpp +++ b/src/core/hle/service/audio/audio_renderer_manager.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -15,7 +18,9 @@ namespace Service::Audio { using namespace AudioCore::Renderer; IAudioRendererManager::IAudioRendererManager(Core::System& system_) - : ServiceFramework{system_, "audren:u"}, impl{std::make_unique(system_)} { + : ServiceFramework{system_, "audren:u"} + , impl(system_) +{ // clang-format off static const FunctionInfo functions[] = { {0, D<&IAudioRendererManager::OpenAudioRenderer>, "OpenAudioRenderer"}, diff --git a/src/core/hle/service/audio/audio_renderer_manager.h b/src/core/hle/service/audio/audio_renderer_manager.h index 69eee664c3..fdce8b6ffa 100644 --- a/src/core/hle/service/audio/audio_renderer_manager.h +++ b/src/core/hle/service/audio/audio_renderer_manager.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -30,7 +33,7 @@ private: Result GetAudioDeviceServiceWithRevisionInfo(Out> out_audio_device, u32 revision, ClientAppletResourceUserId aruid); - std::unique_ptr impl; + std::optional impl; u32 num_audio_devices{0}; }; diff --git a/src/core/hle/service/bcat/backend/backend.cpp b/src/core/hle/service/bcat/backend/backend.cpp index 491416254c..3ef70aaacc 100644 --- a/src/core/hle/service/bcat/backend/backend.cpp +++ b/src/core/hle/service/bcat/backend/backend.cpp @@ -13,9 +13,9 @@ namespace Service::BCAT { ProgressServiceBackend::ProgressServiceBackend(Core::System& system, std::string_view event_name) - : service_context{system, "ProgressServiceBackend"} { - update_event = service_context.CreateEvent("ProgressServiceBackend:UpdateEvent:" + - std::string(event_name)); + : service_context{system, "ProgressServiceBackend"} +{ + update_event = service_context.CreateEvent("ProgressServiceBackend:UpdateEvent:" + std::string(event_name)); } ProgressServiceBackend::~ProgressServiceBackend() { @@ -30,103 +30,90 @@ DeliveryCacheProgressImpl& ProgressServiceBackend::GetImpl() { return impl; } -void ProgressServiceBackend::SetTotalSize(u64 size) { +void ProgressServiceBackend::SetTotalSize(Kernel::KernelCore& kernel, u64 size) { impl.total_bytes = size; - SignalUpdate(); + SignalUpdate(kernel); } -void ProgressServiceBackend::StartConnecting() { +void ProgressServiceBackend::StartConnecting(Kernel::KernelCore& kernel) { impl.status = DeliveryCacheProgressStatus::Connecting; - SignalUpdate(); + SignalUpdate(kernel); } -void ProgressServiceBackend::StartProcessingDataList() { +void ProgressServiceBackend::StartProcessingDataList(Kernel::KernelCore& kernel) { impl.status = DeliveryCacheProgressStatus::ProcessingDataList; - SignalUpdate(); + SignalUpdate(kernel); } -void ProgressServiceBackend::StartDownloadingFile(std::string_view dir_name, - std::string_view file_name, u64 file_size) { +void ProgressServiceBackend::StartDownloadingFile(Kernel::KernelCore& kernel, std::string_view dir_name, std::string_view file_name, u64 file_size) { impl.status = DeliveryCacheProgressStatus::Downloading; impl.current_downloaded_bytes = 0; impl.current_total_bytes = file_size; - std::memcpy(impl.current_directory.data(), dir_name.data(), - std::min(dir_name.size(), 0x31ull)); - std::memcpy(impl.current_file.data(), file_name.data(), - std::min(file_name.size(), 0x31ull)); - SignalUpdate(); + std::memcpy(impl.current_directory.data(), dir_name.data(), std::min(dir_name.size(), 0x31ull)); + std::memcpy(impl.current_file.data(), file_name.data(), std::min(file_name.size(), 0x31ull)); + SignalUpdate(kernel); } -void ProgressServiceBackend::UpdateFileProgress(u64 downloaded) { +void ProgressServiceBackend::UpdateFileProgress(Kernel::KernelCore& kernel, u64 downloaded) { impl.current_downloaded_bytes = downloaded; - SignalUpdate(); + SignalUpdate(kernel); } -void ProgressServiceBackend::FinishDownloadingFile() { +void ProgressServiceBackend::FinishDownloadingFile(Kernel::KernelCore& kernel) { impl.total_downloaded_bytes += impl.current_total_bytes; - SignalUpdate(); + SignalUpdate(kernel); } -void ProgressServiceBackend::CommitDirectory(std::string_view dir_name) { +void ProgressServiceBackend::CommitDirectory(Kernel::KernelCore& kernel, std::string_view dir_name) { impl.status = DeliveryCacheProgressStatus::Committing; impl.current_file.fill(0); impl.current_downloaded_bytes = 0; impl.current_total_bytes = 0; - std::memcpy(impl.current_directory.data(), dir_name.data(), - std::min(dir_name.size(), 0x31ull)); - SignalUpdate(); + std::memcpy(impl.current_directory.data(), dir_name.data(), std::min(dir_name.size(), 0x31ull)); + SignalUpdate(kernel); } -void ProgressServiceBackend::FinishDownload(Result result) { +void ProgressServiceBackend::FinishDownload(Kernel::KernelCore& kernel, Result result) { impl.total_downloaded_bytes = impl.total_bytes; impl.status = DeliveryCacheProgressStatus::Done; impl.result = result; - SignalUpdate(); + SignalUpdate(kernel); } -void ProgressServiceBackend::SignalUpdate() { - update_event->Signal(); +void ProgressServiceBackend::SignalUpdate(Kernel::KernelCore& kernel) { + update_event->Signal(kernel); } BcatBackend::BcatBackend(DirectoryGetter getter) : dir_getter(std::move(getter)) {} - BcatBackend::~BcatBackend() = default; NullBcatBackend::NullBcatBackend(DirectoryGetter getter) : BcatBackend(std::move(getter)) {} - NullBcatBackend::~NullBcatBackend() = default; -bool NullBcatBackend::Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) { - LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}", title.title_id, - title.build_id); - - progress.FinishDownload(ResultSuccess); +bool NullBcatBackend::Synchronize(Kernel::KernelCore& kernel, TitleIDVersion title, ProgressServiceBackend& progress) { + LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}", title.title_id, title.build_id); + progress.FinishDownload(kernel, ResultSuccess); return true; } -bool NullBcatBackend::SynchronizeDirectory(TitleIDVersion title, std::string name, - ProgressServiceBackend& progress) { - LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}, name={}", title.title_id, - title.build_id, name); - - progress.FinishDownload(ResultSuccess); +bool NullBcatBackend::SynchronizeDirectory(Kernel::KernelCore& kernel, TitleIDVersion title, std::string name, ProgressServiceBackend& progress) { + LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}, name={}", title.title_id, title.build_id, name); + progress.FinishDownload(kernel, ResultSuccess); return true; } -bool NullBcatBackend::Clear(u64 title_id) { +bool NullBcatBackend::Clear(Kernel::KernelCore& kernel, u64 title_id) { LOG_DEBUG(Service_BCAT, "called, title_id={:016X}", title_id); return true; } -void NullBcatBackend::SetPassphrase(u64 title_id, const Passphrase& passphrase) { - LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, passphrase={}", title_id, - Common::HexToString(passphrase)); +void NullBcatBackend::SetPassphrase(Kernel::KernelCore& kernel, u64 title_id, const Passphrase& passphrase) { + LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, passphrase={}", title_id, Common::HexToString(passphrase)); } -std::optional> NullBcatBackend::GetLaunchParameter(TitleIDVersion title) { - LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}", title.title_id, - title.build_id); +std::optional> NullBcatBackend::GetLaunchParameter(Kernel::KernelCore& kernel, TitleIDVersion title) { + LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}", title.title_id, title.build_id); return std::nullopt; } diff --git a/src/core/hle/service/bcat/backend/backend.h b/src/core/hle/service/bcat/backend/backend.h index 3680f6c9c8..8419399a62 100644 --- a/src/core/hle/service/bcat/backend/backend.h +++ b/src/core/hle/service/bcat/backend/backend.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -34,26 +37,26 @@ public: ~ProgressServiceBackend(); // Sets the number of bytes total in the entire download. - void SetTotalSize(u64 size); + void SetTotalSize(Kernel::KernelCore& kernel, u64 size); // Notifies the application that the backend has started connecting to the server. - void StartConnecting(); + void StartConnecting(Kernel::KernelCore& kernel); // Notifies the application that the backend has begun accumulating and processing metadata. - void StartProcessingDataList(); + void StartProcessingDataList(Kernel::KernelCore& kernel); // Notifies the application that a file is starting to be downloaded. - void StartDownloadingFile(std::string_view dir_name, std::string_view file_name, u64 file_size); + void StartDownloadingFile(Kernel::KernelCore& kernel, std::string_view dir_name, std::string_view file_name, u64 file_size); // Updates the progress of the current file to the size passed. - void UpdateFileProgress(u64 downloaded); + void UpdateFileProgress(Kernel::KernelCore& kernel, u64 downloaded); // Notifies the application that the current file has completed download. - void FinishDownloadingFile(); + void FinishDownloadingFile(Kernel::KernelCore& kernel); // Notifies the application that all files in this directory have completed and are being // finalized. - void CommitDirectory(std::string_view dir_name); + void CommitDirectory(Kernel::KernelCore& kernel, std::string_view dir_name); // Notifies the application that the operation completed with result code result. - void FinishDownload(Result result); + void FinishDownload(Kernel::KernelCore& kernel, Result result); private: explicit ProgressServiceBackend(Core::System& system, std::string_view event_name); @@ -61,7 +64,7 @@ private: Kernel::KReadableEvent& GetEvent(); DeliveryCacheProgressImpl& GetImpl(); - void SignalUpdate(); + void SignalUpdate(Kernel::KernelCore& kernel); KernelHelpers::ServiceContext service_context; @@ -74,25 +77,19 @@ class BcatBackend { public: explicit BcatBackend(DirectoryGetter getter); virtual ~BcatBackend(); - // Called when the backend is needed to synchronize the data for the game with title ID and // version in title. A ProgressServiceBackend object is provided to alert the application of // status. - virtual bool Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) = 0; + virtual bool Synchronize(Kernel::KernelCore& kernel, TitleIDVersion title, ProgressServiceBackend& progress) = 0; // Very similar to Synchronize, but only for the directory provided. Backends should not alter // the data for any other directories. - virtual bool SynchronizeDirectory(TitleIDVersion title, std::string name, - ProgressServiceBackend& progress) = 0; - + virtual bool SynchronizeDirectory(Kernel::KernelCore& kernel, TitleIDVersion title, std::string name, ProgressServiceBackend& progress) = 0; // Removes all cached data associated with title id provided. - virtual bool Clear(u64 title_id) = 0; - + virtual bool Clear(Kernel::KernelCore& kernel, u64 title_id) = 0; // Sets the BCAT Passphrase to be used with the associated title ID. - virtual void SetPassphrase(u64 title_id, const Passphrase& passphrase) = 0; - + virtual void SetPassphrase(Kernel::KernelCore& kernel, u64 title_id, const Passphrase& passphrase) = 0; // Gets the launch parameter used by AM associated with the title ID and version provided. - virtual std::optional> GetLaunchParameter(TitleIDVersion title) = 0; - + virtual std::optional> GetLaunchParameter(Kernel::KernelCore& kernel, TitleIDVersion title) = 0; protected: DirectoryGetter dir_getter; }; @@ -103,18 +100,13 @@ public: explicit NullBcatBackend(DirectoryGetter getter); ~NullBcatBackend() override; - bool Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) override; - bool SynchronizeDirectory(TitleIDVersion title, std::string name, - ProgressServiceBackend& progress) override; - - bool Clear(u64 title_id) override; - - void SetPassphrase(u64 title_id, const Passphrase& passphrase) override; - - std::optional> GetLaunchParameter(TitleIDVersion title) override; + bool Synchronize(Kernel::KernelCore& kernel, TitleIDVersion title, ProgressServiceBackend& progress) override; + bool SynchronizeDirectory(Kernel::KernelCore& kernel, TitleIDVersion title, std::string name, ProgressServiceBackend& progress) override; + bool Clear(Kernel::KernelCore& kernel, u64 title_id) override; + void SetPassphrase(Kernel::KernelCore& kernel, u64 title_id, const Passphrase& passphrase) override; + std::optional> GetLaunchParameter(Kernel::KernelCore& kernel, TitleIDVersion title) override; }; -std::unique_ptr CreateBackendFromSettings(Core::System& system, - DirectoryGetter getter); +std::unique_ptr CreateBackendFromSettings(Core::System& system, DirectoryGetter getter); } // namespace Service::BCAT diff --git a/src/core/hle/service/bcat/bcat_service.cpp b/src/core/hle/service/bcat/bcat_service.cpp index 849bf8d0d9..210fd77094 100644 --- a/src/core/hle/service/bcat/bcat_service.cpp +++ b/src/core/hle/service/bcat/bcat_service.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project @@ -70,7 +70,7 @@ Result IBcatService::RequestSyncDeliveryCache( LOG_DEBUG(Service_BCAT, "called"); auto& progress_backend{GetProgressBackend(SyncType::Normal)}; - backend.Synchronize({system.GetApplicationProcessProgramID(), + backend.Synchronize(system.Kernel(), {system.GetApplicationProcessProgramID(), GetCurrentBuildID(system.GetApplicationProcessBuildID())}, GetProgressBackend(SyncType::Normal)); @@ -86,7 +86,7 @@ Result IBcatService::RequestSyncDeliveryCacheWithDirectoryName( LOG_DEBUG(Service_BCAT, "called, name={}", name); auto& progress_backend{GetProgressBackend(SyncType::Directory)}; - backend.SynchronizeDirectory({system.GetApplicationProcessProgramID(), + backend.SynchronizeDirectory(system.Kernel(), {system.GetApplicationProcessProgramID(), GetCurrentBuildID(system.GetApplicationProcessBuildID())}, name, progress_backend); @@ -107,7 +107,7 @@ Result IBcatService::SetPassphrase(u64 application_id, std::memcpy(passphrase.data(), passphrase_buffer.data(), (std::min)(passphrase.size(), passphrase_buffer.size())); - backend.SetPassphrase(application_id, passphrase); + backend.SetPassphrase(system.Kernel(), application_id, passphrase); R_SUCCEED(); } @@ -120,7 +120,7 @@ Result IBcatService::ClearDeliveryCacheStorage(u64 application_id) { LOG_DEBUG(Service_BCAT, "called, title_id={:016X}", application_id); R_UNLESS(application_id != 0, ResultInvalidArgument); - R_UNLESS(backend.Clear(application_id), FileSys::ResultPermissionDenied); + R_UNLESS(backend.Clear(system.Kernel(), application_id), FileSys::ResultPermissionDenied); R_SUCCEED(); } diff --git a/src/core/hle/service/btm/btm_system_core.cpp b/src/core/hle/service/btm/btm_system_core.cpp index f1e623be16..318657fc32 100644 --- a/src/core/hle/service/btm/btm_system_core.cpp +++ b/src/core/hle/service/btm/btm_system_core.cpp @@ -29,7 +29,7 @@ IBtmSystemCore::IBtmSystemCore(Core::System& system_) {10, nullptr, "StartAudioDeviceDiscovery"}, {11, nullptr, "StopAudioDeviceDiscovery"}, {12, nullptr, "IsDiscoveryingAudioDevice"}, - {13, nullptr, "GetDiscoveredAudioDevice"}, + {13, C<&IBtmSystemCore::GetDiscoveredAudioDevice>, "GetDiscoveredAudioDevice"}, {14, C<&IBtmSystemCore::AcquireAudioDeviceConnectionEvent>, "AcquireAudioDeviceConnectionEvent"}, {15, nullptr, "ConnectAudioDevice"}, {16, nullptr, "IsConnectingAudioDevice"}, @@ -93,6 +93,11 @@ Result IBtmSystemCore::AcquireRadioEvent(Out out_is_valid, R_SUCCEED(); } +Result IBtmSystemCore::GetDiscoveredAudioDevice(OutArray, BufferAttr_HipcPointer> out_audio_devices, s32 count, Out out_total) { + LOG_WARNING(Service_BTM, "(STUBBED) called"); + R_SUCCEED(); +} + Result IBtmSystemCore::AcquireAudioDeviceConnectionEvent( OutCopyHandle out_event) { LOG_WARNING(Service_BTM, "(STUBBED) called"); diff --git a/src/core/hle/service/btm/btm_system_core.h b/src/core/hle/service/btm/btm_system_core.h index 06498b21ea..3fc4c0d56c 100644 --- a/src/core/hle/service/btm/btm_system_core.h +++ b/src/core/hle/service/btm/btm_system_core.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later @@ -34,9 +37,8 @@ private: Result DisableRadio(); Result IsRadioEnabled(Out out_is_enabled); - Result AcquireRadioEvent(Out out_is_valid, - OutCopyHandle out_event); - + Result AcquireRadioEvent(Out out_is_valid, OutCopyHandle out_event); + Result GetDiscoveredAudioDevice(OutArray, BufferAttr_HipcPointer> out_audio_devices, s32 count, Out out_total); Result AcquireAudioDeviceConnectionEvent(OutCopyHandle out_event); Result GetConnectedAudioDevices( diff --git a/src/core/hle/service/filesystem/fsp/fs_i_filesystem.cpp b/src/core/hle/service/filesystem/fsp/fs_i_filesystem.cpp index 0638111ae9..cbc3777da7 100644 --- a/src/core/hle/service/filesystem/fsp/fs_i_filesystem.cpp +++ b/src/core/hle/service/filesystem/fsp/fs_i_filesystem.cpp @@ -151,7 +151,7 @@ Result IFileSystem::GetTotalSpaceSize( Result IFileSystem::GetFileTimeStampRaw( Out out_timestamp, const InLargeData path) { - LOG_WARNING(Service_FS, "(Partial Implementation) called. file={}", path->str); + LOG_DEBUG(Service_FS, "(Partial Implementation) called. file={}", path->str); FileSys::FileTimeStampRaw vfs_timestamp{}; R_TRY(backend->GetFileTimeStampRaw(&vfs_timestamp, FileSys::Path(path->str))); diff --git a/src/core/hle/service/filesystem/fsp/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp/fsp_srv.cpp index 0ee68c0332..78a1dac81b 100644 --- a/src/core/hle/service/filesystem/fsp/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp/fsp_srv.cpp @@ -284,9 +284,17 @@ Result FSP_SRV::OpenSaveDataFileSystem(OutInterface out_interface, id = FileSys::StorageId::NandSystem; break; case FileSys::SaveDataSpaceId::Temporary: + // ok this is definitely wrong. ASSERT(false) here just kills the whole game the first + // time it opens cache storage, and plenty of games do that (TOTK for one). there is + // user-space scratch storage so it belongs on user nand. map it, do not crash. + id = FileSys::StorageId::NandUser; + break; case FileSys::SaveDataSpaceId::ProperSystem: case FileSys::SaveDataSpaceId::SafeMode: - ASSERT(false); + // same deal for these two. they are system-level spaces so they go on system nand. + // way better than nuking the title over a save-space id we just did not list out. + id = FileSys::StorageId::NandSystem; + break; } *out_interface = @@ -324,9 +332,15 @@ Result FSP_SRV::OpenSaveDataFileSystemBySystemSaveDataId(OutInterface system nand. handled, not crashed. + id = FileSys::StorageId::NandSystem; + break; } *out_interface = diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp index 79578956b1..6885a7be7d 100644 --- a/src/core/hle/service/friend/friend.cpp +++ b/src/core/hle/service/friend/friend.cpp @@ -183,7 +183,7 @@ private: auto& readable_event = completion_event->GetReadableEvent(); IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(readable_event.Signal()); + rb.Push(readable_event.Signal(system.Kernel())); rb.PushCopyObjects(readable_event); } diff --git a/src/core/hle/service/glue/time/alarm_worker.cpp b/src/core/hle/service/glue/time/alarm_worker.cpp index 3ff071f4a0..770dc62f5c 100644 --- a/src/core/hle/service/glue/time/alarm_worker.cpp +++ b/src/core/hle/service/glue/time/alarm_worker.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -28,7 +31,7 @@ void AlarmWorker::Initialize(std::shared_ptr "Glue:AlarmWorker::AlarmTimer", [this](s64 time, std::chrono::nanoseconds ns_late) -> std::optional { - m_timer_event->Signal(); + m_timer_event->Signal(m_system.Kernel()); return std::nullopt; }); @@ -57,7 +60,7 @@ void AlarmWorker::OnPowerStateChanged() { s64 closest_time{}; if (!GetClosestAlarmInfo(closest_alarm_info, closest_time)) { m_system.CoreTiming().UnscheduleEvent(m_timer_timing_event); - m_timer_event->Clear(); + m_timer_event->Clear(m_system.Kernel()); return; } @@ -67,7 +70,7 @@ void AlarmWorker::OnPowerStateChanged() { auto next_time{closest_alarm_info.alert_time - closest_time}; m_system.CoreTiming().UnscheduleEvent(m_timer_timing_event); - m_timer_event->Clear(); + m_timer_event->Clear(m_system.Kernel()); m_system.CoreTiming().ScheduleEvent(std::chrono::nanoseconds(next_time), m_timer_timing_event); diff --git a/src/core/hle/service/glue/time/time_zone.cpp b/src/core/hle/service/glue/time/time_zone.cpp index c358cfbf28..3543e1938b 100644 --- a/src/core/hle/service/glue/time/time_zone.cpp +++ b/src/core/hle/service/glue/time/time_zone.cpp @@ -89,7 +89,7 @@ Result TimeZoneService::SetDeviceLocationName( std::scoped_lock m{m_list_mutex}; for (auto& operation_event : m_list_nodes) { - operation_event.m_event->Signal(); + operation_event.m_event->Signal(m_system.Kernel()); } R_SUCCEED(); } diff --git a/src/core/hle/service/glue/time/worker.cpp b/src/core/hle/service/glue/time/worker.cpp index 1dab3e9dcb..5025efc717 100644 --- a/src/core/hle/service/glue/time/worker.cpp +++ b/src/core/hle/service/glue/time/worker.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -30,7 +33,7 @@ TimeWorker::TimeWorker(Core::System& system, StandardSteadyClockResource& steady "Time::SteadyClockEvent", [this](s64 time, std::chrono::nanoseconds ns_late) -> std::optional { - m_timer_steady_clock->Signal(); + m_timer_steady_clock->Signal(m_system.Kernel()); return std::nullopt; }); @@ -38,19 +41,19 @@ TimeWorker::TimeWorker(Core::System& system, StandardSteadyClockResource& steady "Time::SteadyClockEvent", [this](s64 time, std::chrono::nanoseconds ns_late) -> std::optional { - m_timer_file_system->Signal(); + m_timer_file_system->Signal(m_system.Kernel()); return std::nullopt; }); } TimeWorker::~TimeWorker() { - m_local_clock_event->Signal(); - m_network_clock_event->Signal(); - m_ephemeral_clock_event->Signal(); + m_local_clock_event->Signal(m_system.Kernel()); + m_network_clock_event->Signal(m_system.Kernel()); + m_ephemeral_clock_event->Signal(m_system.Kernel()); std::this_thread::sleep_for(std::chrono::milliseconds(16)); m_thread.request_stop(); - m_event->Signal(); + m_event->Signal(m_system.Kernel()); m_thread.join(); m_ctx.CloseEvent(m_event); @@ -167,19 +170,19 @@ void TimeWorker::ThreadFunc(std::stop_token stop_token) { return; case EventType::PowerStateChange: - m_alarm_worker.GetEvent().Clear(); + m_alarm_worker.GetEvent().Clear(m_system.Kernel()); if (m_pm_state_change_handler.m_priority <= 1) { m_alarm_worker.OnPowerStateChanged(); } break; case EventType::SignalAlarms: - m_alarm_worker.GetTimerEvent().Clear(); + m_alarm_worker.GetTimerEvent().Clear(m_system.Kernel()); m_time_m->CheckAndSignalAlarms(); break; case EventType::UpdateLocalSystemClock: { - m_local_clock_event->Clear(); + m_local_clock_event->Clear(m_system.Kernel()); Service::PSC::Time::SystemClockContext context{}; R_ASSERT(m_local_clock->GetSystemClockContext(&context)); @@ -190,7 +193,7 @@ void TimeWorker::ThreadFunc(std::stop_token stop_token) { } case EventType::UpdateNetworkSystemClock: { - m_network_clock_event->Clear(); + m_network_clock_event->Clear(m_system.Kernel()); Service::PSC::Time::SystemClockContext context{}; R_ASSERT(m_network_clock->GetSystemClockContext(&context)); @@ -218,7 +221,7 @@ void TimeWorker::ThreadFunc(std::stop_token stop_token) { } case EventType::UpdateEphemeralSystemClock: { - m_ephemeral_clock_event->Clear(); + m_ephemeral_clock_event->Clear(m_system.Kernel()); Service::PSC::Time::SystemClockContext context{}; auto res = m_ephemeral_clock->GetSystemClockContext(&context); @@ -247,20 +250,20 @@ void TimeWorker::ThreadFunc(std::stop_token stop_token) { } case EventType::UpdateSteadyClock: - m_timer_steady_clock->Clear(); + m_timer_steady_clock->Clear(m_system.Kernel()); m_steady_clock_resource.UpdateTime(); m_time_m->SetStandardSteadyClockBaseTime(m_steady_clock_resource.GetTime()); break; case EventType::UpdateFileTimestamp: - m_timer_file_system->Clear(); + m_timer_file_system->Clear(m_system.Kernel()); m_file_timestamp_worker.SetFilesystemPosixTime(); break; case EventType::AutoCorrect: { - m_standard_user_auto_correct_clock_event->Clear(); + m_standard_user_auto_correct_clock_event->Clear(m_system.Kernel()); bool automatic_correction{}; R_ASSERT(m_time_sm->IsStandardUserSystemClockAutomaticCorrectionEnabled( diff --git a/src/core/hle/service/gpio/gpio.cpp b/src/core/hle/service/gpio/gpio.cpp new file mode 100644 index 0000000000..8b4aa0f5ab --- /dev/null +++ b/src/core/hle/service/gpio/gpio.cpp @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/service/gpio/gpio.h" +#include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/service.h" + +namespace Service::GPIO { + +class GPIO final : public ServiceFramework { +public: + explicit GPIO(Core::System& system_) + : ServiceFramework{system_, "gpio"} + { + static const FunctionInfo functions[] = { + {0, nullptr, "Cmd0"}, + }; + RegisterHandlers(functions); + } + ~GPIO() override = default; +}; + +void LoopProcess(Core::System& system) { + auto server_manager = std::make_unique(system); + server_manager->RegisterNamedService("gpio", std::make_shared(system)); + ServerManager::RunServer(std::move(server_manager)); +} + +} // namespace Service::GPIO diff --git a/src/core/hle/service/gpio/gpio.h b/src/core/hle/service/gpio/gpio.h new file mode 100644 index 0000000000..57f9a64e56 --- /dev/null +++ b/src/core/hle/service/gpio/gpio.h @@ -0,0 +1,15 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +namespace Core { +class System; +} + +namespace Service::GPIO { +void LoopProcess(Core::System& system); +} // namespace Service::GPIO diff --git a/src/core/hle/service/hid/hid_server.cpp b/src/core/hle/service/hid/hid_server.cpp index f29cf4e3d7..cd69829472 100644 --- a/src/core/hle/service/hid/hid_server.cpp +++ b/src/core/hle/service/hid/hid_server.cpp @@ -650,8 +650,7 @@ Result IHidServer::SetSupportedNpadStyleSet(Core::HID::NpadStyleSet supported_st LOG_DEBUG(Service_HID, "called, supported_style_set={}, applet_resource_user_id={}", supported_style_set, aruid.pid); - R_TRY( - GetResourceManager()->GetNpad()->SetSupportedNpadStyleSet(aruid.pid, supported_style_set)); + R_TRY(GetResourceManager()->GetNpad()->SetSupportedNpadStyleSet(system.Kernel(), aruid.pid, supported_style_set)); Core::HID::NpadStyleTag style_tag{supported_style_set}; const auto revision = GetResourceManager()->GetNpad()->GetRevision(aruid.pid); @@ -667,7 +666,7 @@ Result IHidServer::GetSupportedNpadStyleSet(Out out_sup ClientAppletResourceUserId aruid) { LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", aruid.pid); - R_RETURN(GetResourceManager()->GetNpad()->GetSupportedNpadStyleSet(aruid.pid, + R_RETURN(GetResourceManager()->GetNpad()->GetSupportedNpadStyleSet(system.Kernel(), aruid.pid, *out_supported_style_set)); } @@ -677,7 +676,7 @@ Result IHidServer::SetSupportedNpadIdType( LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", aruid.pid); R_RETURN( - GetResourceManager()->GetNpad()->SetSupportedNpadIdType(aruid.pid, supported_npad_list)); + GetResourceManager()->GetNpad()->SetSupportedNpadIdType(system.Kernel(), aruid.pid, supported_npad_list)); } Result IHidServer::ActivateNpad(ClientAppletResourceUserId aruid) { @@ -702,14 +701,13 @@ Result IHidServer::AcquireNpadStyleSetUpdateEventHandle( LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}", npad_id, aruid.pid, unknown); - R_RETURN(GetResourceManager()->GetNpad()->AcquireNpadStyleSetUpdateEventHandle( - aruid.pid, out_event, npad_id)); + R_RETURN(GetResourceManager()->GetNpad()->AcquireNpadStyleSetUpdateEventHandle(system.Kernel(), aruid.pid, out_event, npad_id)); } Result IHidServer::DisconnectNpad(Core::HID::NpadIdType npad_id, ClientAppletResourceUserId aruid) { LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", npad_id, aruid.pid); - R_RETURN(GetResourceManager()->GetNpad()->DisconnectNpad(aruid.pid, npad_id)); + R_RETURN(GetResourceManager()->GetNpad()->DisconnectNpad(system.Kernel(), aruid.pid, npad_id)); } Result IHidServer::GetPlayerLedPattern(Out out_led_pattern, @@ -779,7 +777,7 @@ Result IHidServer::SetNpadJoyAssignmentModeSingleByDefault(Core::HID::NpadIdType LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", npad_id, aruid.pid); Core::HID::NpadIdType new_npad_id{}; - GetResourceManager()->GetNpad()->SetNpadMode( + GetResourceManager()->GetNpad()->SetNpadMode(system.Kernel(), aruid.pid, new_npad_id, npad_id, NpadJoyDeviceType::Left, NpadJoyAssignmentMode::Single); R_SUCCEED(); } @@ -791,7 +789,7 @@ Result IHidServer::SetNpadJoyAssignmentModeSingle(Core::HID::NpadIdType npad_id, npad_id, aruid.pid, npad_joy_device_type); Core::HID::NpadIdType new_npad_id{}; - GetResourceManager()->GetNpad()->SetNpadMode( + GetResourceManager()->GetNpad()->SetNpadMode(system.Kernel(), aruid.pid, new_npad_id, npad_id, npad_joy_device_type, NpadJoyAssignmentMode::Single); R_SUCCEED(); } @@ -801,7 +799,7 @@ Result IHidServer::SetNpadJoyAssignmentModeDual(Core::HID::NpadIdType npad_id, LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", npad_id, aruid.pid); Core::HID::NpadIdType new_npad_id{}; - GetResourceManager()->GetNpad()->SetNpadMode(aruid.pid, new_npad_id, npad_id, {}, + GetResourceManager()->GetNpad()->SetNpadMode(system.Kernel(), aruid.pid, new_npad_id, npad_id, {}, NpadJoyAssignmentMode::Dual); R_SUCCEED(); } @@ -813,7 +811,7 @@ Result IHidServer::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1, npad_id_1, npad_id_2, aruid.pid); R_RETURN( - GetResourceManager()->GetNpad()->MergeSingleJoyAsDualJoy(aruid.pid, npad_id_1, npad_id_2)); + GetResourceManager()->GetNpad()->MergeSingleJoyAsDualJoy(system.Kernel(), aruid.pid, npad_id_1, npad_id_2)); } Result IHidServer::StartLrAssignmentMode(ClientAppletResourceUserId aruid) { @@ -840,67 +838,40 @@ Result IHidServer::SetNpadHandheldActivationMode(ClientAppletResourceUserId arui ASSERT_MSG(false, "Activation mode should be always None, Single or Dual"); R_SUCCEED(); } - - R_RETURN( - GetResourceManager()->GetNpad()->SetNpadHandheldActivationMode(aruid.pid, activation_mode)); + R_RETURN(GetResourceManager()->GetNpad()->SetNpadHandheldActivationMode(system.Kernel(), aruid.pid, activation_mode)); } Result IHidServer::GetNpadHandheldActivationMode( Out out_activation_mode, ClientAppletResourceUserId aruid) { LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", aruid.pid); - - R_RETURN(GetResourceManager()->GetNpad()->GetNpadHandheldActivationMode(aruid.pid, - *out_activation_mode)); + R_RETURN(GetResourceManager()->GetNpad()->GetNpadHandheldActivationMode(system.Kernel(), aruid.pid, *out_activation_mode)); } -Result IHidServer::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, - Core::HID::NpadIdType npad_id_2, - ClientAppletResourceUserId aruid) { - LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}", - npad_id_1, npad_id_2, aruid.pid); - - R_RETURN(GetResourceManager()->GetNpad()->SwapNpadAssignment(aruid.pid, npad_id_1, npad_id_2)) +Result IHidServer::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2, ClientAppletResourceUserId aruid) { + LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}", npad_id_1, npad_id_2, aruid.pid); + R_RETURN(GetResourceManager()->GetNpad()->SwapNpadAssignment(system.Kernel(), aruid.pid, npad_id_1, npad_id_2)) } -Result IHidServer::IsUnintendedHomeButtonInputProtectionEnabled(Out out_is_enabled, - Core::HID::NpadIdType npad_id, - ClientAppletResourceUserId aruid) { +Result IHidServer::IsUnintendedHomeButtonInputProtectionEnabled(Out out_is_enabled, Core::HID::NpadIdType npad_id, ClientAppletResourceUserId aruid) { LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", npad_id, aruid.pid); - R_UNLESS(IsNpadIdValid(npad_id), ResultInvalidNpadId); - R_RETURN(GetResourceManager()->GetNpad()->IsUnintendedHomeButtonInputProtectionEnabled( - *out_is_enabled, aruid.pid, npad_id)); + R_RETURN(GetResourceManager()->GetNpad()->IsUnintendedHomeButtonInputProtectionEnabled(*out_is_enabled, aruid.pid, npad_id)); } -Result IHidServer::EnableUnintendedHomeButtonInputProtection(bool is_enabled, - Core::HID::NpadIdType npad_id, - ClientAppletResourceUserId aruid) { - LOG_DEBUG(Service_HID, "called, is_enabled={}, npad_id={}, applet_resource_user_id={}", - is_enabled, npad_id, aruid.pid); - +Result IHidServer::EnableUnintendedHomeButtonInputProtection(bool is_enabled, Core::HID::NpadIdType npad_id, ClientAppletResourceUserId aruid) { + LOG_DEBUG(Service_HID, "called, is_enabled={}, npad_id={}, applet_resource_user_id={}",is_enabled, npad_id, aruid.pid); R_UNLESS(IsNpadIdValid(npad_id), ResultInvalidNpadId); - R_RETURN(GetResourceManager()->GetNpad()->EnableUnintendedHomeButtonInputProtection( - aruid.pid, npad_id, is_enabled)); + R_RETURN(GetResourceManager()->GetNpad()->EnableUnintendedHomeButtonInputProtection(aruid.pid, npad_id, is_enabled)); } -Result IHidServer::SetNpadJoyAssignmentModeSingleWithDestination( - Out out_is_reassigned, Out out_new_npad_id, - Core::HID::NpadIdType npad_id, ClientAppletResourceUserId aruid, - NpadJoyDeviceType npad_joy_device_type) { - LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}", - npad_id, aruid.pid, npad_joy_device_type); - - *out_is_reassigned = GetResourceManager()->GetNpad()->SetNpadMode( - aruid.pid, *out_new_npad_id, npad_id, npad_joy_device_type, NpadJoyAssignmentMode::Single); - +Result IHidServer::SetNpadJoyAssignmentModeSingleWithDestination(Out out_is_reassigned, Out out_new_npad_id, Core::HID::NpadIdType npad_id, ClientAppletResourceUserId aruid, NpadJoyDeviceType npad_joy_device_type) { + LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}", npad_id, aruid.pid, npad_joy_device_type); + *out_is_reassigned = GetResourceManager()->GetNpad()->SetNpadMode(system.Kernel(), aruid.pid, *out_new_npad_id, npad_id, npad_joy_device_type, NpadJoyAssignmentMode::Single); R_SUCCEED(); } -Result IHidServer::SetNpadAnalogStickUseCenterClamp(bool use_center_clamp, - ClientAppletResourceUserId aruid) { - LOG_INFO(Service_HID, "called, use_center_clamp={}, applet_resource_user_id={}", - use_center_clamp, aruid.pid); - +Result IHidServer::SetNpadAnalogStickUseCenterClamp(bool use_center_clamp, ClientAppletResourceUserId aruid) { + LOG_INFO(Service_HID, "called, use_center_clamp={}, applet_resource_user_id={}", use_center_clamp, aruid.pid); GetResourceManager()->GetNpad()->SetNpadAnalogStickUseCenterClamp(aruid.pid, use_center_clamp); R_SUCCEED(); } diff --git a/src/core/hle/service/hid/hid_system_server.cpp b/src/core/hle/service/hid/hid_system_server.cpp index 1de5dd6f28..dbc8310c08 100644 --- a/src/core/hle/service/hid/hid_system_server.cpp +++ b/src/core/hle/service/hid/hid_system_server.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project @@ -281,7 +281,7 @@ void IHidSystemServer::ApplyNpadSystemCommonPolicy(HLERequestContext& ctx) { LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - GetResourceManager()->GetNpad()->ApplyNpadSystemCommonPolicy(applet_resource_user_id); + GetResourceManager()->GetNpad()->ApplyNpadSystemCommonPolicy(system.Kernel(), applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); @@ -328,7 +328,7 @@ void IHidSystemServer::ApplyNpadSystemCommonPolicyFull(HLERequestContext& ctx) { LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - GetResourceManager()->GetNpad()->ApplyNpadSystemCommonPolicyFull(applet_resource_user_id); + GetResourceManager()->GetNpad()->ApplyNpadSystemCommonPolicyFull(system.Kernel(), applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); @@ -358,9 +358,8 @@ void IHidSystemServer::GetMaskedSupportedNpadStyleSet(HLERequestContext& ctx) { LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); Core::HID::NpadStyleSet supported_styleset{}; - const auto& npad = GetResourceManager()->GetNpad(); - const Result result = - npad->GetMaskedSupportedNpadStyleSet(applet_resource_user_id, supported_styleset); + const auto npad = GetResourceManager()->GetNpad(); + const Result result = npad->GetMaskedSupportedNpadStyleSet(system.Kernel(), applet_resource_user_id, supported_styleset); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(result); @@ -373,9 +372,8 @@ void IHidSystemServer::SetSupportedNpadStyleSetAll(HLERequestContext& ctx) { LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - const auto& npad = GetResourceManager()->GetNpad(); - const auto result = - npad->SetSupportedNpadStyleSet(applet_resource_user_id, Core::HID::NpadStyleSet::All); + const auto npad = GetResourceManager()->GetNpad(); + const auto result = npad->SetSupportedNpadStyleSet(system.Kernel(), applet_resource_user_id, Core::HID::NpadStyleSet::All); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(result); diff --git a/src/core/hle/service/hle_ipc.cpp b/src/core/hle/service/hle_ipc.cpp index 8569d2bad8..ce7591656a 100644 --- a/src/core/hle/service/hle_ipc.cpp +++ b/src/core/hle/service/hle_ipc.cpp @@ -128,10 +128,12 @@ Result SessionRequestManager::HandleDomainSyncRequest(Kernel::KServerSession* se return ResultSuccess; } -HLERequestContext::HLERequestContext(Kernel::KernelCore& kernel_, Core::Memory::Memory& memory_, - Kernel::KServerSession* server_session_, - Kernel::KThread* thread_) - : server_session(server_session_), thread(thread_), kernel{kernel_}, memory{memory_} { +HLERequestContext::HLERequestContext(Kernel::KernelCore& kernel_, Core::Memory::Memory& memory_, Kernel::KServerSession* server_session_, Kernel::KThread* thread_) + : server_session(server_session_) + , thread(thread_) + , kernel{kernel_} + , memory{memory_} +{ cmd_buf[0] = 0; } @@ -155,9 +157,6 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) { } if (incoming) { // Populate the object lists with the data in the IPC request. - incoming_copy_handles.reserve(handle_descriptor_header->num_handles_to_copy); - incoming_move_handles.reserve(handle_descriptor_header->num_handles_to_move); - for (u32 handle = 0; handle < handle_descriptor_header->num_handles_to_copy; ++handle) { incoming_copy_handles.push_back(rp.Pop()); } @@ -172,11 +171,6 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) { } } - buffer_x_descriptors.reserve(command_header->num_buf_x_descriptors); - buffer_a_descriptors.reserve(command_header->num_buf_a_descriptors); - buffer_b_descriptors.reserve(command_header->num_buf_b_descriptors); - buffer_w_descriptors.reserve(command_header->num_buf_w_descriptors); - for (u32 i = 0; i < command_header->num_buf_x_descriptors; ++i) { buffer_x_descriptors.push_back(rp.PopRaw()); } @@ -281,17 +275,17 @@ Result HLERequestContext::WriteToOutgoingCommandBuffer() { for (auto& object : outgoing_copy_objects) { Handle handle{}; if (object) { - R_TRY(handle_table.Add(&handle, object)); + R_TRY(handle_table.Add(kernel, &handle, object)); } cmd_buf[current_offset++] = handle; } for (auto& object : outgoing_move_objects) { Handle handle{}; if (object) { - R_TRY(handle_table.Add(&handle, object)); + R_TRY(handle_table.Add(kernel, &handle, object)); // Close our reference to the object, as it is being moved to the caller. - object->Close(); + object->Close(kernel); } cmd_buf[current_offset++] = handle; } @@ -509,11 +503,10 @@ bool HLERequestContext::CanWriteBuffer(std::size_t buffer_index) const { } void HLERequestContext::AddMoveInterface(SessionRequestHandlerPtr s) { - ASSERT(Kernel::GetCurrentProcess(kernel).GetResourceLimit()->Reserve( - Kernel::LimitableResource::SessionCountMax, 1)); + ASSERT(Kernel::GetCurrentProcess(kernel).GetResourceLimit()->Reserve(kernel, Kernel::LimitableResource::SessionCountMax, 1)); auto* session = Kernel::KSession::Create(kernel); - session->Initialize(nullptr, 0); + session->Initialize(kernel, nullptr, 0); Kernel::KSession::Register(kernel, session); auto& server = manager.lock()->GetServerManager(); diff --git a/src/core/hle/service/hle_ipc.h b/src/core/hle/service/hle_ipc.h index c2e0e5e8c4..eeb5bf3547 100644 --- a/src/core/hle/service/hle_ipc.h +++ b/src/core/hle/service/hle_ipc.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -11,6 +14,7 @@ #include #include #include +#include #include "common/assert.h" #include "common/common_types.h" @@ -181,8 +185,7 @@ private: */ class HLERequestContext { public: - explicit HLERequestContext(Kernel::KernelCore& kernel, Core::Memory::Memory& memory, - Kernel::KServerSession* session, Kernel::KThread* thread); + explicit HLERequestContext(Kernel::KernelCore& kernel, Core::Memory::Memory& memory, Kernel::KServerSession* session, Kernel::KThread* thread); ~HLERequestContext(); /// Returns a pointer to the IPC command buffer for this request. @@ -233,19 +236,19 @@ public: return data_payload_offset; } - [[nodiscard]] const std::vector& BufferDescriptorX() const { + [[nodiscard]] const boost::container::static_vector& BufferDescriptorX() const { return buffer_x_descriptors; } - [[nodiscard]] const std::vector& BufferDescriptorA() const { + [[nodiscard]] const boost::container::static_vector& BufferDescriptorA() const { return buffer_a_descriptors; } - [[nodiscard]] const std::vector& BufferDescriptorB() const { + [[nodiscard]] const boost::container::static_vector& BufferDescriptorB() const { return buffer_b_descriptors; } - [[nodiscard]] const std::vector& BufferDescriptorC() const { + [[nodiscard]] const boost::container::static_vector& BufferDescriptorC() const { return buffer_c_descriptors; } @@ -370,11 +373,10 @@ public: template Kernel::KScopedAutoObject GetObjectFromHandle(u32 handle) { - auto obj = client_handle_table->GetObjectForIpc(handle, thread); - if (obj.IsNotNull()) { - return obj->DynamicCast(); - } - return nullptr; + auto obj = client_handle_table->GetObjectForIpc(kernel, handle, thread); + if (obj.IsNotNull()) + return {kernel, obj->DynamicCast()}; + return {kernel, nullptr}; } [[nodiscard]] std::shared_ptr GetManager() const { @@ -399,38 +401,35 @@ private: Kernel::KHandleTable* client_handle_table{}; Kernel::KThread* thread{}; - std::vector incoming_move_handles; - std::vector incoming_copy_handles; + boost::container::static_vector buffer_x_descriptors; + boost::container::static_vector buffer_a_descriptors; + boost::container::static_vector buffer_b_descriptors; + boost::container::static_vector buffer_w_descriptors; + boost::container::static_vector buffer_c_descriptors; + boost::container::static_vector incoming_move_handles; + boost::container::static_vector incoming_copy_handles; + boost::container::static_vector outgoing_move_objects; + boost::container::static_vector outgoing_copy_objects; + boost::container::static_vector outgoing_domain_objects; - std::vector outgoing_move_objects; - std::vector outgoing_copy_objects; - std::vector outgoing_domain_objects; + mutable std::array, 3> read_buffer_data_a{}; + mutable std::array, 3> read_buffer_data_x{}; std::optional command_header; std::optional handle_descriptor_header; std::optional data_payload_header; std::optional domain_message_header; - std::vector buffer_x_descriptors; - std::vector buffer_a_descriptors; - std::vector buffer_b_descriptors; - std::vector buffer_w_descriptors; - std::vector buffer_c_descriptors; + std::weak_ptr manager{}; + Kernel::KernelCore& kernel; + Core::Memory::Memory& memory; - u32_le command{}; u64 pid{}; + u32_le command{}; u32 write_size{}; u32 data_payload_offset{}; u32 handles_offset{}; u32 domain_offset{}; - - std::weak_ptr manager{}; - bool is_deferred{false}; - - Kernel::KernelCore& kernel; - Core::Memory::Memory& memory; - - mutable std::array, 3> read_buffer_data_a{}; - mutable std::array, 3> read_buffer_data_x{}; + bool is_deferred = false; }; } // namespace Service diff --git a/src/core/hle/service/i2c/i2c.cpp b/src/core/hle/service/i2c/i2c.cpp new file mode 100644 index 0000000000..d000487246 --- /dev/null +++ b/src/core/hle/service/i2c/i2c.cpp @@ -0,0 +1,76 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/result.h" +#include "core/hle/service/i2c/i2c.h" +#include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/cmif_serialization.h" +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/service.h" +#include "core/hle/service/server_manager.h" + +namespace Service::I2C { + +class I2CSession final : public ServiceFramework { +public: + explicit I2CSession(Core::System& system_) + : ServiceFramework{system_, "I2CSession"} + { + static const FunctionInfo functions[] = { + {0, nullptr, "SendOld"}, + {1, nullptr, "ReceiveOld"}, + {2, nullptr, "ExecuteCommandListOld"}, + {10, C<&I2CSession::Send>, "Send"}, + {11, nullptr, "Receive"}, + {12, nullptr, "ExecuteCommandList"}, + {13, nullptr, "SetRetryPolicy"}, + }; + RegisterHandlers(functions); + } + ~I2CSession() override = default; + + Result Send(InBuffer in_data, u32 transaction_option) { + LOG_WARNING(Service, "(stubbed) topt={}", transaction_option); + R_THROW(ResultUnknown); + } +}; + +enum class I2CDevice : u32 { + ClassicController, + Ftm3bd56, +}; + +class I2C final : public ServiceFramework { +public: + explicit I2C(Core::System& system_) + : ServiceFramework{system_, "i2c"} + { + static const FunctionInfo functions[] = { + {0, nullptr, "OpenSessionForDev"}, + {1, C<&I2C::OpenSession>, "OpenSession"}, + {2, nullptr, "HasDevice"}, + {3, nullptr, "HasDeviceForDev"}, + {4, nullptr, "OpenSession2"}, + }; + RegisterHandlers(functions); + } + ~I2C() override = default; + + Result OpenSession(I2CDevice device, OutInterface out_session) { + LOG_DEBUG(Service, "(stubbed)"); + *out_session = std::make_shared(system); + R_SUCCEED(); + } +}; + +void LoopProcess(Core::System& system) { + auto server_manager = std::make_unique(system); + server_manager->RegisterNamedService("i2c", std::make_shared(system)); + ServerManager::RunServer(std::move(server_manager)); +} + +} // namespace Service::I2C diff --git a/src/core/hle/service/i2c/i2c.h b/src/core/hle/service/i2c/i2c.h new file mode 100644 index 0000000000..c26df8e86f --- /dev/null +++ b/src/core/hle/service/i2c/i2c.h @@ -0,0 +1,15 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +namespace Core { +class System; +} + +namespace Service::I2C { +void LoopProcess(Core::System& system); +} // namespace Service::I2C diff --git a/src/core/hle/service/ipc_helpers.h b/src/core/hle/service/ipc_helpers.h index 8aee17db8d..c37b0c2e05 100644 --- a/src/core/hle/service/ipc_helpers.h +++ b/src/core/hle/service/ipc_helpers.h @@ -151,11 +151,10 @@ public: if (manager->IsDomain()) { context->AddDomainObject(std::move(iface)); } else { - ASSERT(Kernel::GetCurrentProcess(kernel).GetResourceLimit()->Reserve( - Kernel::LimitableResource::SessionCountMax, 1)); + ASSERT(Kernel::GetCurrentProcess(kernel).GetResourceLimit()->Reserve(kernel, Kernel::LimitableResource::SessionCountMax, 1)); auto* session = Kernel::KSession::Create(kernel); - session->Initialize(nullptr, 0); + session->Initialize(kernel, nullptr, 0); Kernel::KSession::Register(kernel, session); auto next_manager = std::make_shared( diff --git a/src/core/hle/service/jit/jit.cpp b/src/core/hle/service/jit/jit.cpp index 1179e8b61d..626b643c00 100644 --- a/src/core/hle/service/jit/jit.cpp +++ b/src/core/hle/service/jit/jit.cpp @@ -30,12 +30,13 @@ static_assert(sizeof(Struct32) == 32, "Struct32 has wrong size"); class IJitEnvironment final : public ServiceFramework { public: - explicit IJitEnvironment(Core::System& system_, - Kernel::KScopedAutoObject process_, - CodeMemory&& user_rx_, CodeMemory&& user_ro_) - : ServiceFramework{system_, "IJitEnvironment"}, process{std::move(process_)}, - user_rx{std::move(user_rx_)}, user_ro{std::move(user_ro_)}, - context{system_.ApplicationMemory()} { + explicit IJitEnvironment(Core::System& system_, Kernel::KProcess* process_, CodeMemory&& user_rx_, CodeMemory&& user_ro_) + : ServiceFramework{system_, "IJitEnvironment"} + , process{kernel, process_} + , user_rx{std::move(user_rx_)} + , user_ro{std::move(user_ro_)} + , context{system_.ApplicationMemory()} + { // clang-format off static const FunctionInfo functions[] = { @@ -58,6 +59,11 @@ public: configuration.sys_ro_memory = configuration.user_ro_memory; } + ~IJitEnvironment() { + user_rx.Finalize(system.Kernel()); + user_ro.Finalize(system.Kernel()); + } + Result GenerateCode(Out out_return_value, Out out_range0, Out out_range1, OutBuffer out_buffer, u32 data_size, u64 command, CodeRange range0, CodeRange range1, @@ -281,14 +287,9 @@ private: } CodeMemory rx, ro; - - R_TRY(rx.Initialize(*process, *rx_mem, rx_size, Kernel::Svc::MemoryPermission::ReadExecute, - generate_random)); - R_TRY(ro.Initialize(*process, *ro_mem, ro_size, Kernel::Svc::MemoryPermission::Read, - generate_random)); - - *out_jit_environment = - std::make_shared(system, process.Get(), std::move(rx), std::move(ro)); + R_TRY(rx.Initialize(system.Kernel(), *process, *rx_mem, rx_size, Kernel::Svc::MemoryPermission::ReadExecute, generate_random)); + R_TRY(ro.Initialize(system.Kernel(), *process, *ro_mem, ro_size, Kernel::Svc::MemoryPermission::Read, generate_random)); + *out_jit_environment = std::make_shared(system, process.Get(), std::move(rx), std::move(ro)); R_SUCCEED(); } diff --git a/src/core/hle/service/jit/jit_code_memory.cpp b/src/core/hle/service/jit/jit_code_memory.cpp index 2b480488a8..3218512af4 100644 --- a/src/core/hle/service/jit/jit_code_memory.cpp +++ b/src/core/hle/service/jit/jit_code_memory.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -5,12 +8,9 @@ namespace Service::JIT { -Result CodeMemory::Initialize(Kernel::KProcess& process, Kernel::KCodeMemory& code_memory, - size_t size, Kernel::Svc::MemoryPermission perm, - std::mt19937_64& generate_random) { +Result CodeMemory::Initialize(Kernel::KernelCore& kernel, Kernel::KProcess& process, Kernel::KCodeMemory& code_memory, size_t size, Kernel::Svc::MemoryPermission perm, std::mt19937_64& generate_random) { auto& page_table = process.GetPageTable(); - const u64 alias_code_start = - GetInteger(page_table.GetAliasCodeRegionStart()) / Kernel::PageSize; + const u64 alias_code_start = GetInteger(page_table.GetAliasCodeRegionStart()) / Kernel::PageSize; const u64 alias_code_size = page_table.GetAliasCodeRegionSize() / Kernel::PageSize; // NOTE: This will retry indefinitely until mapping the code memory succeeds. @@ -20,7 +20,7 @@ Result CodeMemory::Initialize(Kernel::KProcess& process, Kernel::KCodeMemory& co (alias_code_start + (generate_random() % alias_code_size)) * Kernel::PageSize; // Try to map the address - R_TRY_CATCH(code_memory.MapToOwner(mapped_address, size, perm)) { + R_TRY_CATCH(code_memory.MapToOwner(kernel, mapped_address, size, perm)) { R_CATCH(Kernel::ResultInvalidMemoryRegion) { // If we could not map here, retry. continue; @@ -35,17 +35,17 @@ Result CodeMemory::Initialize(Kernel::KProcess& process, Kernel::KCodeMemory& co m_perm = perm; // Open a new reference to the code memory. - m_code_memory->Open(); + m_code_memory->Open(kernel); // We succeeded. R_SUCCEED(); } } -void CodeMemory::Finalize() { +void CodeMemory::Finalize(Kernel::KernelCore& kernel) { if (m_code_memory) { - R_ASSERT(m_code_memory->UnmapFromOwner(m_address, m_size)); - m_code_memory->Close(); + R_ASSERT(m_code_memory->UnmapFromOwner(kernel, m_address, m_size)); + m_code_memory->Close(kernel); } m_code_memory = nullptr; diff --git a/src/core/hle/service/jit/jit_code_memory.h b/src/core/hle/service/jit/jit_code_memory.h index 6376d4c4eb..d741520aeb 100644 --- a/src/core/hle/service/jit/jit_code_memory.h +++ b/src/core/hle/service/jit/jit_code_memory.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -7,29 +10,16 @@ #include "core/hle/kernel/k_code_memory.h" +namespace Kernel { +class KernelCore; +} + namespace Service::JIT { class CodeMemory { public: - YUZU_NON_COPYABLE(CodeMemory); - - explicit CodeMemory() = default; - - CodeMemory(CodeMemory&& rhs) { - std::swap(m_code_memory, rhs.m_code_memory); - std::swap(m_size, rhs.m_size); - std::swap(m_address, rhs.m_address); - std::swap(m_perm, rhs.m_perm); - } - - ~CodeMemory() { - this->Finalize(); - } - -public: - Result Initialize(Kernel::KProcess& process, Kernel::KCodeMemory& code_memory, size_t size, - Kernel::Svc::MemoryPermission perm, std::mt19937_64& generate_random); - void Finalize(); + Result Initialize(Kernel::KernelCore& kernel, Kernel::KProcess& process, Kernel::KCodeMemory& code_memory, size_t size, Kernel::Svc::MemoryPermission perm, std::mt19937_64& generate_random); + void Finalize(Kernel::KernelCore& kernel); size_t GetSize() const { return m_size; @@ -39,7 +29,6 @@ public: return m_address; } -private: Kernel::KCodeMemory* m_code_memory{}; size_t m_size{}; u64 m_address{}; diff --git a/src/core/hle/service/kernel_helpers.cpp b/src/core/hle/service/kernel_helpers.cpp index f080f7ffa2..f7a5cb7049 100644 --- a/src/core/hle/service/kernel_helpers.cpp +++ b/src/core/hle/service/kernel_helpers.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -14,15 +17,15 @@ namespace Service::KernelHelpers { ServiceContext::ServiceContext(Core::System& system_, std::string name_) - : kernel(system_.Kernel()) { + : kernel(system_.Kernel()) +{ if (process = Kernel::GetCurrentProcessPointer(kernel); process != nullptr) { return; } // Create the process. process = Kernel::KProcess::Create(kernel); - ASSERT(R_SUCCEEDED(process->Initialize(Kernel::Svc::CreateProcessParameter{}, - kernel.GetSystemResourceLimit(), false))); + ASSERT(R_SUCCEEDED(process->Initialize(kernel, Kernel::Svc::CreateProcessParameter{}, kernel.GetSystemResourceLimit(), false))); // Register the process. Kernel::KProcess::Register(kernel, process); @@ -31,14 +34,14 @@ ServiceContext::ServiceContext(Core::System& system_, std::string name_) ServiceContext::~ServiceContext() { if (process_created) { - process->Close(); + process->Close(kernel); process = nullptr; } } Kernel::KEvent* ServiceContext::CreateEvent(std::string&& name) { // Reserve a new event from the process resource limit - Kernel::KScopedResourceReservation event_reservation(process, + Kernel::KScopedResourceReservation event_reservation(kernel, process, Kernel::LimitableResource::EventCountMax); if (!event_reservation.Succeeded()) { LOG_CRITICAL(Service, "Resource limit reached!"); @@ -53,7 +56,7 @@ Kernel::KEvent* ServiceContext::CreateEvent(std::string&& name) { } // Initialize the event. - event->Initialize(process); + event->Initialize(kernel, process); // Commit the thread reservation. event_reservation.Commit(); @@ -65,11 +68,10 @@ Kernel::KEvent* ServiceContext::CreateEvent(std::string&& name) { } void ServiceContext::CloseEvent(Kernel::KEvent* event) { - if (!event) { - return; + if (event) { + event->GetReadableEvent().Close(kernel); + event->Close(kernel); } - event->GetReadableEvent().Close(); - event->Close(); } } // namespace Service::KernelHelpers diff --git a/src/core/hle/service/kernel_helpers.h b/src/core/hle/service/kernel_helpers.h index eca9aefb52..4e21c8857f 100644 --- a/src/core/hle/service/kernel_helpers.h +++ b/src/core/hle/service/kernel_helpers.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -26,7 +29,6 @@ public: void CloseEvent(Kernel::KEvent* event); -private: Kernel::KernelCore& kernel; Kernel::KProcess* process{}; bool process_created{false}; diff --git a/src/core/hle/service/ldn/user_local_communication_service.cpp b/src/core/hle/service/ldn/user_local_communication_service.cpp index 1e984a9782..572aea6314 100644 --- a/src/core/hle/service/ldn/user_local_communication_service.cpp +++ b/src/core/hle/service/ldn/user_local_communication_service.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later @@ -322,7 +322,7 @@ void IUserLocalCommunicationService::OnLDNPacketReceived(const Network::LDNPacke } void IUserLocalCommunicationService::OnEventFired() { - state_change_event->Signal(); + state_change_event->Signal(system.Kernel()); } } // namespace Service::LDN diff --git a/src/core/hle/service/nfc/common/device.cpp b/src/core/hle/service/nfc/common/device.cpp index faa2b1f584..2876c6f739 100644 --- a/src/core/hle/service/nfc/common/device.cpp +++ b/src/core/hle/service/nfc/common/device.cpp @@ -71,13 +71,13 @@ NfcDevice::~NfcDevice() { void NfcDevice::NpadUpdate(Core::HID::ControllerTriggerType type) { if (type == Core::HID::ControllerTriggerType::Connected) { Initialize(); - availability_change_event->Signal(); + availability_change_event->Signal(system.Kernel()); return; } if (type == Core::HID::ControllerTriggerType::Disconnected) { Finalize(); - availability_change_event->Signal(); + availability_change_event->Signal(system.Kernel()); return; } @@ -138,8 +138,8 @@ bool NfcDevice::LoadNfcTag(u8 protocol, u8 tag_type, u8 uuid_length, UniqueSeria }; device_state = DeviceState::TagFound; - deactivate_event->GetReadableEvent().Clear(); - activate_event->Signal(); + deactivate_event->GetReadableEvent().Clear(system.Kernel()); + activate_event->Signal(system.Kernel()); return true; } @@ -192,8 +192,8 @@ void NfcDevice::CloseNfcTag() { device_state = DeviceState::TagRemoved; encrypted_tag_data = {}; tag_data = {}; - activate_event->GetReadableEvent().Clear(); - deactivate_event->Signal(); + activate_event->GetReadableEvent().Clear(system.Kernel()); + deactivate_event->Signal(system.Kernel()); } Kernel::KReadableEvent& NfcDevice::GetActivateEvent() const { diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp index a0302a5841..87e806d86b 100644 --- a/src/core/hle/service/nifm/nifm.cpp +++ b/src/core/hle/service/nifm/nifm.cpp @@ -275,9 +275,19 @@ private: } state.store(State::Processing); - evt_processing->Signal(); + evt_processing->Signal(system.Kernel()); - worker = std::thread(&IScanRequest::WorkerThread, this); + worker = std::thread([this]() { + using namespace std::chrono_literals; + scan_results = Network::ScanWifiNetworks(3s); + { + std::scoped_lock lk{g_scan_mtx}; + g_last_scan_results = scan_results; + } + // choose result code + const bool ok = !scan_results.empty(); + Finish(ok ? ResultSuccess : ResultPendingConnection); + }); IPC::ResponseBuilder{ctx, 2}.Push(ResultSuccess); } @@ -308,25 +318,10 @@ private: enum class State { Idle, Processing, Finished }; - void WorkerThread() { - using namespace std::chrono_literals; - - scan_results = Network::ScanWifiNetworks(3s); - - { - std::scoped_lock lk{g_scan_mtx}; - g_last_scan_results = scan_results; - } - - // choose result code - const bool ok = !scan_results.empty(); - Finish(ok ? ResultSuccess : ResultPendingConnection); - } - void Finish(Result rc) { worker_result.store(rc); state.store(State::Finished); - evt_scan_complete->Signal(); + evt_scan_complete->Signal(system.Kernel()); } KernelHelpers::ServiceContext svc_ctx; @@ -491,7 +486,7 @@ private: void UpdateState(RequestState new_state) { LOG_DEBUG(Service_NIFM, "(STUBBED) called"); state = new_state; - event1->Signal(); + event1->Signal(system.Kernel()); } KernelHelpers::ServiceContext service_context; diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp index 4f465c5da7..5f85302713 100644 --- a/src/core/hle/service/nim/nim.cpp +++ b/src/core/hle/service/nim/nim.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project @@ -429,7 +429,7 @@ private: void StartTask(HLERequestContext& ctx) { // No need to connect to the internet, just finish the task straight away. LOG_DEBUG(Service_NIM, "called"); - finished_event->Signal(); + finished_event->Signal(system.Kernel()); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } @@ -451,7 +451,7 @@ private: void Cancel(HLERequestContext& ctx) { LOG_DEBUG(Service_NIM, "called"); - finished_event->Clear(); + finished_event->Clear(system.Kernel()); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } diff --git a/src/core/hle/service/ns/application_manager_interface.cpp b/src/core/hle/service/ns/application_manager_interface.cpp index 5490c09d03..485eac0afa 100644 --- a/src/core/hle/service/ns/application_manager_interface.cpp +++ b/src/core/hle/service/ns/application_manager_interface.cpp @@ -610,7 +610,7 @@ Result IApplicationManagerInterface::GetApplicationRecordUpdateSystemEvent( OutCopyHandle out_event) { LOG_WARNING(Service_NS, "(STUBBED) called"); - record_update_system_event.Signal(); + record_update_system_event.Signal(system.Kernel()); *out_event = record_update_system_event.GetHandle(); R_SUCCEED(); @@ -820,14 +820,14 @@ Result IApplicationManagerInterface::RequestDownloadApplicationControlDataInBack LOG_INFO(Service_NS, "called, control_source={} app={:016X}", control_source, application_id); - unknown_event.Signal(); + unknown_event.Signal(system.Kernel()); R_SUCCEED(); } Result IApplicationManagerInterface::Unknown4022( OutCopyHandle out_event) { LOG_WARNING(Service_NS, "(STUBBED) called"); - unknown_event.Signal(); + unknown_event.Signal(system.Kernel()); *out_event = unknown_event.GetHandle(); R_SUCCEED(); } diff --git a/src/core/hle/service/ns/read_only_application_control_data_interface.cpp b/src/core/hle/service/ns/read_only_application_control_data_interface.cpp index 03f0a17865..f12d9b03d2 100644 --- a/src/core/hle/service/ns/read_only_application_control_data_interface.cpp +++ b/src/core/hle/service/ns/read_only_application_control_data_interface.cpp @@ -87,7 +87,7 @@ public: RegisterHandlers(functions); completion_event = service_context.CreateEvent("IAsyncValue:Completion"); - completion_event->GetReadableEvent().Signal(); + completion_event->GetReadableEvent().Signal(system.Kernel()); } ~IAsyncValueForListApplicationTitle() override { diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp index f26f5347eb..bdbb16ed04 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp @@ -79,7 +79,9 @@ void nvdisp_disp0::Composite(std::span sorted_layers }); for (size_t i = 0; i < layer.acquire_fence.num_fences; i++) { - output_fences.push_back(layer.acquire_fence.fences[i]); + if (layer.acquire_fence.fences[i].id >= 0) { + output_fences.push_back(layer.acquire_fence.fences[i]); + } } } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp index 3bfef0c29c..1238a21f85 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp @@ -138,8 +138,7 @@ NvResult nvhost_as_gpu::AllocAsEx(IoctlAllocAsEx& params) { static_cast((vm.va_range_end - vm.va_range_split) >> vm.big_page_size_bits)}; vm.big_page_allocator.emplace(start_big_pages, end_big_pages); - gmmu = std::make_shared(system, max_big_page_bits, vm.va_range_split, - vm.big_page_size_bits, VM::PAGE_SIZE_BITS); + gmmu = std::make_unique(system, max_big_page_bits, vm.va_range_split, vm.big_page_size_bits, VM::PAGE_SIZE_BITS); system.GPU().InitAddressSpace(*gmmu); vm.initialised = true; @@ -416,7 +415,7 @@ NvResult nvhost_as_gpu::BindChannel(IoctlBindChannel& params) { LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd); auto gpu_channel_device = module.GetDevice(params.fd); - gpu_channel_device->channel_state->memory_manager = gmmu; + gpu_channel_device->channel_state->memory_manager = gmmu.get(); return NvResult::Success; } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h index 44892ee368..b8ae57e1b4 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h @@ -219,7 +219,7 @@ private: bool initialised{}; } vm; - std::shared_ptr gmmu; + std::unique_ptr gmmu; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index b2580f2bb9..6ef32b890f 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp @@ -203,7 +203,7 @@ NvResult nvhost_ctrl::IocCtrlEventWait(IocCtrlEventWaitParams& params, bool is_a auto& event_ = events[slot]; if (event_.status.exchange(EventState::Signalling, std::memory_order_acq_rel) == EventState::Waiting) { - event_.kevent->Signal(); + event_.kevent->Signal(system.Kernel()); } event_.status.store(EventState::Signalled, std::memory_order_release); }); @@ -292,7 +292,7 @@ NvResult nvhost_ctrl::IocCtrlClearEventWait(IocCtrlEventClearParams& params) { } event.fails++; event.status.store(EventState::Cancelled, std::memory_order_release); - event.kevent->Clear(); + event.kevent->Clear(system.Kernel()); return NvResult::Success; } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp index ace1f7e8f7..b621e5d70d 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp @@ -47,6 +47,8 @@ NvResult nvhost_ctrl_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span return WrapFixed(this, &nvhost_ctrl_gpu::FlushL2, input, output); case 0x14: return WrapFixed(this, &nvhost_ctrl_gpu::GetActiveSlotMask, input, output); + case 0x15: + return WrapFixed(this, &nvhost_ctrl_gpu::PmuGetGpuLoad, input, output); case 0x1c: return WrapFixed(this, &nvhost_ctrl_gpu::GetGpuTime, input, output); default: @@ -234,6 +236,12 @@ NvResult nvhost_ctrl_gpu::GetActiveSlotMask(IoctlActiveSlotMask& params) { return NvResult::Success; } +NvResult nvhost_ctrl_gpu::PmuGetGpuLoad(IoctlPmuGetLoad& params) { + LOG_WARNING(Service_NVDRV, "(stubbed) called"); + params.pmu_gpu_load = 100; + return NvResult::Success; +} + NvResult nvhost_ctrl_gpu::ZCullGetCtxSize(IoctlZcullGetCtxSize& params) { LOG_DEBUG(Service_NVDRV, "called"); params.size = 0x1; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h index e0603f9a71..80b7abd6a1 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project @@ -184,6 +184,11 @@ private: }; static_assert(sizeof(IoctlGetCpuTimeCorrelationInfo) == 264); + struct IoctlPmuGetLoad { + u32 pmu_gpu_load; + }; + static_assert(sizeof(IoctlPmuGetLoad) == 4); + NvResult GetCharacteristics1(IoctlCharacteristics& params); NvResult GetCharacteristics3(IoctlCharacteristics& params, std::span gpu_characteristics); @@ -192,6 +197,7 @@ private: NvResult GetTPCMasks3(IoctlGpuGetTpcMasksArgs& params, std::span tpc_mask); NvResult GetActiveSlotMask(IoctlActiveSlotMask& params); + NvResult PmuGetGpuLoad(IoctlPmuGetLoad& params); NvResult ZCullGetCtxSize(IoctlZcullGetCtxSize& params); NvResult ZCullGetInfo(IoctlNvgpuGpuZcullGetInfoArgs& params); NvResult ZBCSetTable(IoctlZbcSetTable& params); diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp index d64658bddc..7ac3dfaa46 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp @@ -25,18 +25,20 @@ NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, std::span in switch (command.group) { case 0x0: switch (command.cmd) { - case 0x1: + case 0x01: return WrapFixedVariable(this, &nvhost_nvdec::Submit, input, output, fd); - case 0x2: + case 0x02: return WrapFixed(this, &nvhost_nvdec::GetSyncpoint, input, output); - case 0x3: + case 0x03: return WrapFixed(this, &nvhost_nvdec::GetWaitbase, input, output); - case 0x7: + case 0x07: return WrapFixed(this, &nvhost_nvdec::SetSubmitTimeout, input, output); - case 0x9: + case 0x09: return WrapFixedVariable(this, &nvhost_nvdec::MapBuffer, input, output, fd); - case 0xa: + case 0x0a: return WrapFixedVariable(this, &nvhost_nvdec::UnmapBuffer, input, output); + case 0x23: + return WrapFixed(this, &nvhost_nvdec::GetClkRate, input, output); default: break; } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp index c726d6f93e..698712e152 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp @@ -168,6 +168,13 @@ NvResult nvhost_nvdec_common::SetSubmitTimeout(u32 timeout) { return NvResult::Success; } +NvResult nvhost_nvdec_common::GetClkRate(IoctlGetClkRate& params) { + LOG_WARNING(Service_NVDRV, "(STUBBED) called"); + params.clk_rate = 614400000; + params.module_id = 0; + return NvResult::Success; +} + Kernel::KEvent* nvhost_nvdec_common::QueryEvent(u32 event_id) { LOG_CRITICAL(Service_NVDRV, "Unknown HOSTX1 Event {}", event_id); return nullptr; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h index 6b66a0d28d..8b3b7ee285 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h @@ -111,6 +111,12 @@ protected: }; static_assert(sizeof(IoctlMapBuffer) == 0x0C, "IoctlMapBuffer is incorrect size"); + struct IoctlGetClkRate { + u32_le clk_rate{}; + u32_le module_id{}; + }; + static_assert(sizeof(IoctlGetClkRate) == 8); + /// Ioctl command implementations NvResult SetNVMAPfd(IoctlSetNvmapFD&); NvResult Submit(IoctlSubmit& params, std::span input, DeviceFD fd); @@ -119,6 +125,7 @@ protected: NvResult MapBuffer(IoctlMapBuffer& params, std::span entries, DeviceFD fd); NvResult UnmapBuffer(IoctlMapBuffer& params, std::span entries); NvResult SetSubmitTimeout(u32 timeout); + NvResult GetClkRate(IoctlGetClkRate& params); Kernel::KEvent* QueryEvent(u32 event_id) override; diff --git a/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp index 436b0f4b05..c3eb2796e3 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp +++ b/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp @@ -10,6 +10,7 @@ #include "common/assert.h" #include "common/logging.h" #include "common/settings.h" +#include "common/cpu_features.h" #include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_readable_event.h" #include "core/hle/kernel/kernel.h" @@ -26,8 +27,10 @@ namespace Service::android { BufferQueueProducer::BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_, std::shared_ptr buffer_queue_core_, Service::Nvidia::NvCore::NvMap& nvmap_) - : service_context{service_context_}, core{std::move(buffer_queue_core_)}, slots(core->slots), - clock{Common::CreateOptimalClock()}, nvmap(nvmap_) { + : service_context{service_context_}, core{std::move(buffer_queue_core_)} + , slots(core->slots) + , nvmap(nvmap_) +{ buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent"); } @@ -108,7 +111,7 @@ Status BufferQueueProducer::SetBufferCount(s32 buffer_count) { core->override_max_buffer_count = buffer_count; core->SignalDequeueCondition(); - buffer_wait_event->Signal(); + buffer_wait_event->Signal(service_context.kernel); listener = core->consumer_listener; } @@ -485,7 +488,7 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input, slots[slot].buffer_state = BufferState::Queued; slots[slot].frame_number = core->frame_counter; slots[slot].queue_time = timestamp; - slots[slot].presentation_time = clock->GetTimeNS().count(); + slots[slot].presentation_time = Common::g_wall_clock.GetTimeNS().count(); slots[slot].fence = fence; item.slot = slot; @@ -573,7 +576,7 @@ void BufferQueueProducer::CancelBuffer(s32 slot, const Fence& fence) { slots[slot].fence = fence; core->SignalDequeueCondition(); - buffer_wait_event->Signal(); + buffer_wait_event->Signal(service_context.kernel); } Status BufferQueueProducer::Query(NativeWindow what, s32* out_value) { @@ -612,6 +615,9 @@ Status BufferQueueProducer::Query(NativeWindow what, s32* out_value) { case NativeWindow::ConsumerUsageBits: value = core->consumer_usage_bit; break; + case NativeWindow::DefaultDataSpace: + value = core->GetMaxBufferCountLocked(false); + break; default: ASSERT(false); return Status::BadValue; @@ -708,7 +714,7 @@ Status BufferQueueProducer::Disconnect(NativeWindowApi api) { core->connected_producer_listener = nullptr; core->connected_api = NativeWindowApi::NoConnectedApi; core->SignalDequeueCondition(); - buffer_wait_event->Signal(); + buffer_wait_event->Signal(service_context.kernel); listener = core->consumer_listener; } else { LOG_ERROR(Service_Nvnflinger, @@ -758,7 +764,7 @@ Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot, } core->SignalDequeueCondition(); - buffer_wait_event->Signal(); + buffer_wait_event->Signal(service_context.kernel); return Status::NoError; } diff --git a/src/core/hle/service/nvnflinger/buffer_queue_producer.h b/src/core/hle/service/nvnflinger/buffer_queue_producer.h index 06cb49cbad..697de0ac9b 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_producer.h +++ b/src/core/hle/service/nvnflinger/buffer_queue_producer.h @@ -14,7 +14,7 @@ #include #include "common/common_funcs.h" -#include "common/wall_clock.h" +#include "common/cpu_features.h" #include "core/hle/service/nvdrv/nvdata.h" #include "core/hle/service/nvnflinger/binder.h" #include "core/hle/service/nvnflinger/buffer_queue_defs.h" @@ -89,8 +89,6 @@ private: s32 next_callback_ticket{}; s32 current_callback_ticket{}; std::condition_variable_any callback_condition; - std::unique_ptr clock; - Service::Nvidia::NvCore::NvMap& nvmap; }; diff --git a/src/core/hle/service/nvnflinger/hos_binder_driver.h b/src/core/hle/service/nvnflinger/hos_binder_driver.h index b7fb07bd2e..6b551c91d2 100644 --- a/src/core/hle/service/nvnflinger/hos_binder_driver.h +++ b/src/core/hle/service/nvnflinger/hos_binder_driver.h @@ -1,6 +1,11 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#pragma once + #include "core/hle/service/cmif_types.h" #include "core/hle/service/service.h" diff --git a/src/core/hle/service/nvnflinger/ui/fence.h b/src/core/hle/service/nvnflinger/ui/fence.h index 177aed7580..261eb51669 100644 --- a/src/core/hle/service/nvnflinger/ui/fence.h +++ b/src/core/hle/service/nvnflinger/ui/fence.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2012 The Android Open Source Project // SPDX-License-Identifier: GPL-3.0-or-later @@ -15,10 +18,8 @@ namespace Service::android { class Fence { public: - constexpr Fence() = default; - static constexpr Fence NoFence() { - Fence fence; + Fence fence{}; fence.fences[0].id = -1; fence.fences[1].id = -1; fence.fences[2].id = -1; @@ -26,7 +27,6 @@ public: return fence; } -public: u32 num_fences{}; std::array fences{}; }; diff --git a/src/core/hle/service/olsc/daemon_controller.h b/src/core/hle/service/olsc/daemon_controller.h index fc7dd7bbcb..5f978ab153 100644 --- a/src/core/hle/service/olsc/daemon_controller.h +++ b/src/core/hle/service/olsc/daemon_controller.h @@ -4,6 +4,8 @@ // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#pragma once + #include #include "common/uuid.h" diff --git a/src/core/hle/service/olsc/native_handle_holder.cpp b/src/core/hle/service/olsc/native_handle_holder.cpp index d381714bc3..1e50195539 100644 --- a/src/core/hle/service/olsc/native_handle_holder.cpp +++ b/src/core/hle/service/olsc/native_handle_holder.cpp @@ -32,7 +32,7 @@ INativeHandleHolder::~INativeHandleHolder() { Result INativeHandleHolder::GetNativeHandle(OutCopyHandle out_event) { LOG_WARNING(Service_OLSC, "(STUBBED) called"); if (event) { - event->Signal(); + event->Signal(system.Kernel()); *out_event = std::addressof(event->GetReadableEvent()); } else { *out_event = nullptr; diff --git a/src/core/hle/service/olsc/native_handle_holder.h b/src/core/hle/service/olsc/native_handle_holder.h index 985c60d1cb..e0eac50c28 100644 --- a/src/core/hle/service/olsc/native_handle_holder.h +++ b/src/core/hle/service/olsc/native_handle_holder.h @@ -4,6 +4,8 @@ // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#pragma once + #include "core/hle/service/cmif_types.h" #include "core/hle/service/service.h" diff --git a/src/core/hle/service/olsc/olsc_service_for_application.h b/src/core/hle/service/olsc/olsc_service_for_application.h index 3f9ac7536a..8941659478 100644 --- a/src/core/hle/service/olsc/olsc_service_for_application.h +++ b/src/core/hle/service/olsc/olsc_service_for_application.h @@ -1,6 +1,11 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#pragma once + #include "core/hle/service/cmif_types.h" #include "core/hle/service/ns/ns_types.h" #include "core/hle/service/service.h" diff --git a/src/core/hle/service/olsc/olsc_service_for_system_service.h b/src/core/hle/service/olsc/olsc_service_for_system_service.h index 447e37005e..d16c8b979b 100644 --- a/src/core/hle/service/olsc/olsc_service_for_system_service.h +++ b/src/core/hle/service/olsc/olsc_service_for_system_service.h @@ -4,6 +4,8 @@ // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#pragma once + #include #include "common/uuid.h" diff --git a/src/core/hle/service/olsc/remote_storage_controller.h b/src/core/hle/service/olsc/remote_storage_controller.h index bae1e8efb5..21b439013a 100644 --- a/src/core/hle/service/olsc/remote_storage_controller.h +++ b/src/core/hle/service/olsc/remote_storage_controller.h @@ -4,6 +4,8 @@ // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#pragma once + #include "core/hle/service/cmif_types.h" #include "core/hle/service/service.h" diff --git a/src/core/hle/service/olsc/transfer_task_list_controller.h b/src/core/hle/service/olsc/transfer_task_list_controller.h index 72dbf610d8..4ac770b477 100644 --- a/src/core/hle/service/olsc/transfer_task_list_controller.h +++ b/src/core/hle/service/olsc/transfer_task_list_controller.h @@ -4,6 +4,8 @@ // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#pragma once + #include "core/hle/service/cmif_types.h" #include "core/hle/service/service.h" diff --git a/src/core/hle/service/os/event.cpp b/src/core/hle/service/os/event.cpp index ec52c17fd8..8808c5b4f0 100644 --- a/src/core/hle/service/os/event.cpp +++ b/src/core/hle/service/os/event.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -7,25 +10,27 @@ namespace Service { -Event::Event(KernelHelpers::ServiceContext& ctx) { +Event::Event(KernelHelpers::ServiceContext& ctx_) + : ctx{ctx_} +{ m_event = ctx.CreateEvent("Event"); } Event::~Event() { - m_event->GetReadableEvent().Close(); - m_event->Close(); + m_event->GetReadableEvent().Close(ctx.kernel); + m_event->Close(ctx.kernel); } -void Event::Signal() { - m_event->Signal(); +void Event::Signal(Kernel::KernelCore& kernel) noexcept { + m_event->Signal(kernel); } -void Event::Clear() { - m_event->Clear(); +void Event::Clear(Kernel::KernelCore& kernel) noexcept { + m_event->Clear(kernel); } -Kernel::KReadableEvent* Event::GetHandle() { - return &m_event->GetReadableEvent(); +Kernel::KReadableEvent* Event::GetHandle() noexcept { + return std::addressof(m_event->GetReadableEvent()); } } // namespace Service diff --git a/src/core/hle/service/os/event.h b/src/core/hle/service/os/event.h index cdbc4635a6..bc23598b58 100644 --- a/src/core/hle/service/os/event.h +++ b/src/core/hle/service/os/event.h @@ -1,9 +1,13 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once namespace Kernel { +class KernelCore; class KEvent; class KReadableEvent; } // namespace Kernel @@ -19,12 +23,12 @@ public: explicit Event(KernelHelpers::ServiceContext& ctx); ~Event(); - void Signal(); - void Clear(); - - Kernel::KReadableEvent* GetHandle(); + void Signal(Kernel::KernelCore& kernel) noexcept; + void Clear(Kernel::KernelCore& kernel) noexcept; + Kernel::KReadableEvent* GetHandle() noexcept; private: + KernelHelpers::ServiceContext& ctx; Kernel::KEvent* m_event; }; diff --git a/src/core/hle/service/os/mutex.cpp b/src/core/hle/service/os/mutex.cpp index 6009f48668..12b872b57f 100644 --- a/src/core/hle/service/os/mutex.cpp +++ b/src/core/hle/service/os/mutex.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -10,22 +13,22 @@ namespace Service { Mutex::Mutex(Core::System& system) : m_system(system) { m_event = Kernel::KEvent::Create(system.Kernel()); - m_event->Initialize(nullptr); + m_event->Initialize(system.Kernel(), nullptr); // Register the event. Kernel::KEvent::Register(system.Kernel(), m_event); - ASSERT(R_SUCCEEDED(m_event->Signal())); + ASSERT(R_SUCCEEDED(m_event->Signal(system.Kernel()))); } Mutex::~Mutex() { - m_event->GetReadableEvent().Close(); - m_event->Close(); + m_event->GetReadableEvent().Close(m_system.Kernel()); + m_event->Close(m_system.Kernel()); } void Mutex::lock() { // Infinitely retry until we successfully clear the event. - while (R_FAILED(m_event->GetReadableEvent().Reset())) { + while (R_FAILED(m_event->GetReadableEvent().Reset(m_system.Kernel()))) { s32 index; Kernel::KSynchronizationObject* obj = &m_event->GetReadableEvent(); @@ -40,7 +43,7 @@ void Mutex::lock() { void Mutex::unlock() { // Unlock. - ASSERT(R_SUCCEEDED(m_event->Signal())); + ASSERT(R_SUCCEEDED(m_event->Signal(m_system.Kernel()))); } } // namespace Service diff --git a/src/core/hle/service/os/process.cpp b/src/core/hle/service/os/process.cpp index 0dbadc315e..5e46de245d 100644 --- a/src/core/hle/service/os/process.cpp +++ b/src/core/hle/service/os/process.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -10,14 +13,6 @@ namespace Service { -Process::Process(Core::System& system) - : m_system(system), m_process(), m_main_thread_priority(), m_main_thread_stack_size(), - m_process_started() {} - -Process::~Process() { - this->Finalize(); -} - bool Process::Initialize(Loader::AppLoader& loader, Loader::ResultStatus& out_load_result) { // First, ensure we are not holding another process. this->Finalize(); @@ -28,7 +23,7 @@ bool Process::Initialize(Loader::AppLoader& loader, Loader::ResultStatus& out_lo // On exit, ensure we free the additional reference to the process. SCOPE_EXIT { - process->Close(); + process->Close(m_system.Kernel()); }; // Insert process modules into memory. @@ -52,7 +47,7 @@ bool Process::Initialize(Loader::AppLoader& loader, Loader::ResultStatus& out_lo // Take ownership of the process object. m_process = process; - m_process->Open(); + m_process->Open(m_system.Kernel()); // We succeeded. return true; @@ -64,8 +59,7 @@ void Process::Finalize() { // Close the process. if (m_process) { - m_process->Close(); - + m_process->Close(m_system.Kernel()); // TODO: remove this, kernel already tracks this m_system.Kernel().RemoveProcess(m_process); } @@ -85,7 +79,7 @@ bool Process::Run() { // Start. if (m_process) { - m_process->Run(m_main_thread_priority, m_main_thread_stack_size); + m_process->Run(m_system.Kernel(), m_main_thread_priority, m_main_thread_stack_size); } // Mark as started. @@ -97,13 +91,13 @@ bool Process::Run() { void Process::Terminate() { if (m_process) { - m_process->Terminate(); + m_process->Terminate(m_system.Kernel()); } } void Process::ResetSignal() { if (m_process) { - m_process->Reset(); + m_process->Reset(m_system.Kernel()); } } @@ -144,8 +138,7 @@ u64 Process::GetProgramId() const { void Process::Suspend(bool suspended) { if (m_process) { - m_process->SetActivity(suspended ? Kernel::Svc::ProcessActivity::Paused - : Kernel::Svc::ProcessActivity::Runnable); + m_process->SetActivity(m_system.Kernel(), suspended ? Kernel::Svc::ProcessActivity::Paused : Kernel::Svc::ProcessActivity::Runnable); } } diff --git a/src/core/hle/service/os/process.h b/src/core/hle/service/os/process.h index 9109b7d0a5..ca10945f84 100644 --- a/src/core/hle/service/os/process.h +++ b/src/core/hle/service/os/process.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -22,8 +25,8 @@ namespace Service { class Process { public: - explicit Process(Core::System& system); - ~Process(); + inline explicit Process(Core::System& system) noexcept : m_system(system) {} + inline ~Process() { this->Finalize(); } bool Initialize(Loader::AppLoader& loader, Loader::ResultStatus& out_load_result); void Finalize(); @@ -50,8 +53,8 @@ public: private: Core::System& m_system; Kernel::KProcess* m_process{}; - s32 m_main_thread_priority{}; u64 m_main_thread_stack_size{}; + s32 m_main_thread_priority{}; bool m_process_started{}; }; diff --git a/src/core/hle/service/pm/pm.cpp b/src/core/hle/service/pm/pm.cpp index b52468e419..5f94984f4e 100644 --- a/src/core/hle/service/pm/pm.cpp +++ b/src/core/hle/service/pm/pm.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -25,19 +28,15 @@ constexpr u64 NO_PROCESS_FOUND_PID{0}; using ProcessList = std::list>; template -Kernel::KScopedAutoObject SearchProcessList(ProcessList& process_list, - F&& predicate) { - const auto iter = std::find_if(process_list.begin(), process_list.end(), predicate); - - if (iter == process_list.end()) { - return nullptr; - } - - return iter->GetPointerUnsafe(); +Kernel::KScopedAutoObject SearchProcessList(Kernel::KernelCore& kernel, ProcessList& process_list, F&& predicate) { + auto const it = std::find_if(process_list.begin(), process_list.end(), predicate); + if (it == process_list.end()) + return {kernel, nullptr}; + return {kernel, it->GetPointerUnsafe()}; } -void GetApplicationPidGeneric(HLERequestContext& ctx, ProcessList& process_list) { - auto process = SearchProcessList(process_list, [](auto& p) { return p->IsApplication(); }); +void GetApplicationPidGeneric(Kernel::KernelCore& kernel, HLERequestContext& ctx, ProcessList& process_list) { + auto process = SearchProcessList(kernel, process_list, [](auto& p) { return p->IsApplication(); }); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(ResultSuccess); @@ -105,7 +104,7 @@ private: LOG_DEBUG(Service_PM, "called, program_id={:016X}", program_id); auto list = kernel.GetProcessList(); - auto process = SearchProcessList( + auto process = SearchProcessList(system.Kernel(), list, [program_id](auto& p) { return p->GetProgramId() == program_id; }); if (process.IsNull()) { @@ -122,7 +121,7 @@ private: void GetApplicationProcessId(HLERequestContext& ctx) { LOG_DEBUG(Service_PM, "called"); auto list = kernel.GetProcessList(); - GetApplicationPidGeneric(ctx, list); + GetApplicationPidGeneric(system.Kernel(), ctx, list); } void AtmosphereGetProcessInfo(HLERequestContext& ctx) { @@ -134,7 +133,7 @@ private: LOG_WARNING(Service_PM, "(Partial Implementation) called, pid={:016X}", pid); auto list = kernel.GetProcessList(); - auto process = SearchProcessList(list, [pid](auto& p) { return p->GetProcessId() == pid; }); + auto process = SearchProcessList(system.Kernel(), list, [pid](auto& p) { return p->GetProcessId() == pid; }); if (process.IsNull()) { IPC::ResponseBuilder rb{ctx, 2}; @@ -188,7 +187,7 @@ private: LOG_DEBUG(Service_PM, "called, process_id={:016X}", process_id); auto list = kernel.GetProcessList(); - auto process = SearchProcessList( + auto process = SearchProcessList(system.Kernel(), list, [process_id](auto& p) { return p->GetProcessId() == process_id; }); if (process.IsNull()) { @@ -209,7 +208,7 @@ private: LOG_DEBUG(Service_PM, "called, program_id={:016X}", program_id); auto list = system.Kernel().GetProcessList(); - auto process = SearchProcessList( + auto process = SearchProcessList(system.Kernel(), list, [program_id](auto& p) { return p->GetProgramId() == program_id; }); if (process.IsNull()) { @@ -249,7 +248,7 @@ private: void GetApplicationProcessIdForShell(HLERequestContext& ctx) { LOG_DEBUG(Service_PM, "called"); auto list = kernel.GetProcessList(); - GetApplicationPidGeneric(ctx, list); + GetApplicationPidGeneric(system.Kernel(), ctx, list); } }; diff --git a/src/core/hle/service/psc/ovln/receiver.cpp b/src/core/hle/service/psc/ovln/receiver.cpp index f7d3882a05..f43d705126 100644 --- a/src/core/hle/service/psc/ovln/receiver.cpp +++ b/src/core/hle/service/psc/ovln/receiver.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project @@ -100,7 +100,7 @@ Result IReceiver::ReceiveWithTick(Out out_notification, } } if (!has_messages) { - receive_event->Clear(); + receive_event->Clear(system.Kernel()); } R_SUCCEED(); diff --git a/src/core/hle/service/psc/time/alarms.cpp b/src/core/hle/service/psc/time/alarms.cpp index 5e52c19f82..c2de95f653 100644 --- a/src/core/hle/service/psc/time/alarms.cpp +++ b/src/core/hle/service/psc/time/alarms.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -7,8 +10,10 @@ namespace Service::PSC::Time { Alarm::Alarm(Core::System& system, KernelHelpers::ServiceContext& ctx, AlarmType type) - : m_ctx{ctx}, m_event{ctx.CreateEvent("Psc:Alarm:Event")} { - m_event->Clear(); + : m_ctx{ctx} + , m_event{ctx.CreateEvent("Psc:Alarm:Event")} +{ + m_event->Clear(system.Kernel()); switch (type) { case WakeupAlarm: @@ -37,7 +42,7 @@ Alarms::~Alarms() { m_ctx.CloseEvent(m_event); } -Result Alarms::Enable(Alarm& alarm, s64 time) { +Result Alarms::Enable(Kernel::KernelCore& kernel, Alarm& alarm, s64 time) { R_UNLESS(m_steady_clock.IsInitialized(), ResultClockUninitialized); std::scoped_lock l{m_mutex}; @@ -49,21 +54,21 @@ Result Alarms::Enable(Alarm& alarm, s64 time) { time_ns = Common::AlignUp(time_ns, one_second_ns); alarm.SetAlertTime(time_ns); - Insert(alarm); - R_RETURN(UpdateClosestAndSignal()); + Insert(kernel, alarm); + R_RETURN(UpdateClosestAndSignal(kernel)); } -void Alarms::Disable(Alarm& alarm) { +void Alarms::Disable(Kernel::KernelCore& kernel, Alarm& alarm) { std::scoped_lock l{m_mutex}; if (!alarm.IsLinked()) { return; } - Erase(alarm); - UpdateClosestAndSignal(); + Erase(kernel, alarm); + UpdateClosestAndSignal(kernel); } -void Alarms::CheckAndSignal() { +void Alarms::CheckAndSignal(Kernel::KernelCore& kernel) { std::scoped_lock l{m_mutex}; if (m_alarms.empty()) { return; @@ -72,9 +77,9 @@ void Alarms::CheckAndSignal() { bool alarm_signalled{false}; for (auto& alarm : m_alarms) { if (m_steady_clock.GetRawTime() >= alarm.GetAlertTime()) { - alarm.Signal(); + alarm.Signal(kernel); alarm.Lock(); - Erase(alarm); + Erase(kernel, alarm); m_power_state_request_manager.UpdatePendingPowerStateRequestPriority( alarm.GetPriority()); @@ -87,7 +92,7 @@ void Alarms::CheckAndSignal() { } m_power_state_request_manager.SignalPowerStateRequestAvailability(); - UpdateClosestAndSignal(); + UpdateClosestAndSignal(kernel); } bool Alarms::GetClosestAlarm(Alarm** out_alarm) { @@ -97,7 +102,7 @@ bool Alarms::GetClosestAlarm(Alarm** out_alarm) { return alarm != nullptr; } -void Alarms::Insert(Alarm& alarm) { +void Alarms::Insert(Kernel::KernelCore& kernel, Alarm& alarm) { // Alarms are sorted by alert time, then priority auto it{m_alarms.begin()}; while (it != m_alarms.end()) { @@ -113,15 +118,15 @@ void Alarms::Insert(Alarm& alarm) { m_alarms.push_back(alarm); } -void Alarms::Erase(Alarm& alarm) { +void Alarms::Erase(Kernel::KernelCore& kernel, Alarm& alarm) { m_alarms.erase(m_alarms.iterator_to(alarm)); } -Result Alarms::UpdateClosestAndSignal() { +Result Alarms::UpdateClosestAndSignal(Kernel::KernelCore& kernel) { m_closest_alarm = m_alarms.empty() ? nullptr : std::addressof(m_alarms.front()); R_SUCCEED_IF(m_closest_alarm == nullptr); - m_event->Signal(); + m_event->Signal(kernel); R_SUCCEED(); } @@ -183,7 +188,7 @@ void ISteadyClockAlarm::Enable(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; auto time{rp.Pop()}; - auto res = m_alarms.Enable(m_alarm, time); + auto res = m_alarms.Enable(system.Kernel(), m_alarm, time); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(res); @@ -192,7 +197,7 @@ void ISteadyClockAlarm::Enable(HLERequestContext& ctx) { void ISteadyClockAlarm::Disable(HLERequestContext& ctx) { LOG_DEBUG(Service_Time, "called."); - m_alarms.Disable(m_alarm); + m_alarms.Disable(system.Kernel(), m_alarm); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); diff --git a/src/core/hle/service/psc/time/alarms.h b/src/core/hle/service/psc/time/alarms.h index 597770028b..79b66c06ea 100644 --- a/src/core/hle/service/psc/time/alarms.h +++ b/src/core/hle/service/psc/time/alarms.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -48,8 +51,8 @@ struct Alarm : public Common::IntrusiveListBaseNode { return m_priority; } - void Signal() { - m_event->Signal(); + void Signal(Kernel::KernelCore& kernel) { + m_event->Signal(kernel); } Result Lock() { @@ -83,15 +86,15 @@ public: return m_steady_clock.GetRawTime(); } - Result Enable(Alarm& alarm, s64 time); - void Disable(Alarm& alarm); - void CheckAndSignal(); + Result Enable(Kernel::KernelCore& kernel, Alarm& alarm, s64 time); + void Disable(Kernel::KernelCore& kernel, Alarm& alarm); + void CheckAndSignal(Kernel::KernelCore& kernel); bool GetClosestAlarm(Alarm** out_alarm); private: - void Insert(Alarm& alarm); - void Erase(Alarm& alarm); - Result UpdateClosestAndSignal(); + void Insert(Kernel::KernelCore& kernel, Alarm& alarm); + void Erase(Kernel::KernelCore& kernel, Alarm& alarm); + Result UpdateClosestAndSignal(Kernel::KernelCore& kernel); Core::System& m_system; KernelHelpers::ServiceContext m_ctx; diff --git a/src/core/hle/service/psc/time/clocks/context_writers.cpp b/src/core/hle/service/psc/time/clocks/context_writers.cpp index a44486b438..4e54a07a37 100644 --- a/src/core/hle/service/psc/time/clocks/context_writers.cpp +++ b/src/core/hle/service/psc/time/clocks/context_writers.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -6,10 +9,10 @@ namespace Service::PSC::Time { -void ContextWriter::SignalAllNodes() { +void ContextWriter::SignalAllNodes(Kernel::KernelCore& kernel) { std::scoped_lock l{m_mutex}; for (auto& operation : m_operation_events) { - operation.m_event->Signal(); + operation.m_event->Signal(kernel); } } @@ -33,7 +36,7 @@ Result LocalSystemClockContextWriter::Write(const SystemClockContext& context) { m_shared_memory.SetLocalSystemContext(context); - SignalAllNodes(); + SignalAllNodes(m_system.Kernel()); R_SUCCEED(); } @@ -57,7 +60,7 @@ Result NetworkSystemClockContextWriter::Write(const SystemClockContext& context) m_shared_memory.SetNetworkSystemContext(context); - SignalAllNodes(); + SignalAllNodes(m_system.Kernel()); R_SUCCEED(); } @@ -75,7 +78,7 @@ Result EphemeralNetworkSystemClockContextWriter::Write(const SystemClockContext& m_in_use = true; } - SignalAllNodes(); + SignalAllNodes(m_system.Kernel()); R_SUCCEED(); } diff --git a/src/core/hle/service/psc/time/clocks/context_writers.h b/src/core/hle/service/psc/time/clocks/context_writers.h index 6643fc9f2a..2adc7d2546 100644 --- a/src/core/hle/service/psc/time/clocks/context_writers.h +++ b/src/core/hle/service/psc/time/clocks/context_writers.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -11,6 +14,9 @@ #include "core/hle/service/psc/time/common.h" #include "core/hle/service/psc/time/shared_memory.h" +namespace Kernel { +class KernelCore; +} namespace Core { class System; } @@ -25,7 +31,7 @@ public: virtual ~ContextWriter() = default; virtual Result Write(const SystemClockContext& context) = 0; - void SignalAllNodes(); + void SignalAllNodes(Kernel::KernelCore& kernel); void Link(OperationEvent& operation_event); private: diff --git a/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.cpp b/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.cpp index 31ed273966..7a51e51f3c 100644 --- a/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.cpp +++ b/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -57,7 +60,7 @@ Result StandardUserSystemClockCore::GetTimePoint(SteadyClockTimePoint& out_time_ void StandardUserSystemClockCore::SetTimePointAndSignal(SteadyClockTimePoint& time_point) { m_time_point = time_point; - m_event->Signal(); + m_event->Signal(m_system.Kernel()); } } // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/common.h b/src/core/hle/service/psc/time/common.h index 0ad2ed51f8..36a90cd828 100644 --- a/src/core/hle/service/psc/time/common.h +++ b/src/core/hle/service/psc/time/common.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project @@ -13,7 +13,7 @@ #include "common/common_types.h" #include "common/intrusive_list.h" #include "common/uuid.h" -#include "common/wall_clock.h" +#include "common/cpu_features.h" #include "core/hle/kernel/k_event.h" #include "core/hle/service/kernel_helpers.h" #include "core/hle/service/psc/time/errors.h" diff --git a/src/core/hle/service/psc/time/power_state_request_manager.cpp b/src/core/hle/service/psc/time/power_state_request_manager.cpp index b28b513649..7a494f47cb 100644 --- a/src/core/hle/service/psc/time/power_state_request_manager.cpp +++ b/src/core/hle/service/psc/time/power_state_request_manager.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project @@ -35,7 +35,7 @@ void PowerStateRequestManager::SignalPowerStateRequestAvailability() { } m_has_pending_request = false; m_available_request_priority = m_pending_request_priority; - m_event->Signal(); + m_event->Signal(m_system.Kernel()); } } @@ -45,7 +45,7 @@ bool PowerStateRequestManager::GetAndClearPowerStateRequest(u32& out_priority) { if (m_has_available_request) { out_priority = m_available_request_priority; m_has_available_request = false; - m_event->Clear(); + m_event->Clear(m_system.Kernel()); } return had_request; } diff --git a/src/core/hle/service/psc/time/service_manager.cpp b/src/core/hle/service/psc/time/service_manager.cpp index ed9fb32cdf..4646def3db 100644 --- a/src/core/hle/service/psc/time/service_manager.cpp +++ b/src/core/hle/service/psc/time/service_manager.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -238,7 +241,7 @@ Result ServiceManager::GetClosestAlarmUpdatedEvent( Result ServiceManager::CheckAndSignalAlarms() { LOG_DEBUG(Service_Time, "called."); - m_alarms.CheckAndSignal(); + m_alarms.CheckAndSignal(m_system.Kernel()); R_SUCCEED(); } diff --git a/src/core/hle/service/psc/time/static.cpp b/src/core/hle/service/psc/time/static.cpp index 9a0adb2955..488731565d 100644 --- a/src/core/hle/service/psc/time/static.cpp +++ b/src/core/hle/service/psc/time/static.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -171,7 +174,7 @@ Result StaticService::SetStandardUserSystemClockAutomaticCorrectionEnabled( R_TRY(m_time->m_standard_steady_clock.GetCurrentTimePoint(time_point)); m_user_system_clock.SetTimePointAndSignal(time_point); - m_user_system_clock.GetEvent().Signal(); + m_user_system_clock.GetEvent().Signal(system.Kernel()); R_SUCCEED(); } diff --git a/src/core/hle/service/ptm/psm.cpp b/src/core/hle/service/ptm/psm.cpp index f144b8e497..211f380171 100644 --- a/src/core/hle/service/ptm/psm.cpp +++ b/src/core/hle/service/ptm/psm.cpp @@ -6,10 +6,12 @@ #include +#include "common/common_funcs.h" #include "common/device_power_state.h" #include "common/logging.h" #include "core/core.h" #include "core/hle/kernel/k_event.h" +#include "core/hle/result.h" #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/kernel_helpers.h" #include "core/hle/service/ptm/psm.h" @@ -41,19 +43,19 @@ public: void SignalChargerTypeChanged() { if (should_signal && should_signal_charger_type) { - state_change_event->Signal(); + state_change_event->Signal(system.Kernel()); } } void SignalPowerSupplyChanged() { if (should_signal && should_signal_power_supply) { - state_change_event->Signal(); + state_change_event->Signal(system.Kernel()); } } void SignalBatteryVoltageStateChanged() { if (should_signal && should_signal_battery_voltage) { - state_change_event->Signal(); + state_change_event->Signal(system.Kernel()); } } @@ -134,12 +136,12 @@ PSM::PSM(Core::System& system_) : ServiceFramework{system_, "psm"} { {9, nullptr, "DisableEnoughPowerChargeEmulation"}, {10, nullptr, "EnableFastBatteryCharging"}, {11, nullptr, "DisableFastBatteryCharging"}, - {12, nullptr, "GetBatteryVoltageState"}, + {12, &PSM::GetBatteryVoltageState, "GetBatteryVoltageState"}, {13, nullptr, "GetRawBatteryChargePercentage"}, {14, nullptr, "IsEnoughPowerSupplied"}, - {15, nullptr, "GetBatteryAgePercentage"}, + {15, &PSM::GetBatteryAgePercentage, "GetBatteryAgePercentage"}, {16, nullptr, "GetBatteryChargeInfoEvent"}, - {17, nullptr, "GetBatteryChargeInfoFields"}, + {17, &PSM::GetBatteryChargeInfoFields, "GetBatteryChargeInfoFields"}, {18, nullptr, "GetBatteryChargeCalibratedEvent"}, }; // clang-format on @@ -187,4 +189,64 @@ void PSM::OpenSession(HLERequestContext& ctx) { rb.PushIpcInterface(system); } +void PSM::GetBatteryVoltageState(HLERequestContext& ctx) { + LOG_DEBUG(Service_PTM, "(stubbed)"); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushRaw(0); // +} + +void PSM::GetBatteryAgePercentage(HLERequestContext& ctx) { + LOG_DEBUG(Service_PTM, "(stubbed)"); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushRaw(1.f); +} + +struct BatteryChargeInfoFields { + u32 input_current_limit; //mA + u32 boost_mode_current_limit; + u32 fast_charge_current_limit; + u32 charge_voltage_limit; + u32 charger_type; + u8 hi2_mode; + u8 battery_charging; + INSERT_PADDING_BYTES_NOINIT(2); + u32 vdd50_state; + u32 temperature_celcius; + f32 battery_charge_percentage; + u32 battery_charge_milli_voltage; + f32 battery_age_percentage; + u32 usb_power_role; + u32 usb_charger_type; + u32 charger_input_voltage_limit; + u32 charger_input_current_limit; + u8 fast_battery_charging; + u8 controller_power_supply; + u8 otg_request; + INSERT_PADDING_BYTES_NOINIT(1); + INSERT_PADDING_BYTES_NOINIT(0x14); //[+17.0.0] +}; +static_assert(sizeof(struct BatteryChargeInfoFields) == 0x54); +void PSM::GetBatteryChargeInfoFields(HLERequestContext& ctx) { + LOG_DEBUG(Service_PTM, "called"); + Common::PowerStatus power_status = Common::GetPowerStatus(); + + BatteryChargeInfoFields r{}; + r.battery_charge_percentage = f32(power_status.percentage); //100% + r.battery_age_percentage = f32(power_status.percentage); //100% + r.battery_charging = power_status.charging ? 1 : 0; + r.charger_type = u32(power_status.has_battery && power_status.charging + ? ChargerType::RegularCharger : ChargerType::Unplugged); + r.charger_input_voltage_limit = 100; + r.charger_input_voltage_limit = 100; + r.input_current_limit = 100; + r.boost_mode_current_limit = 100; + r.fast_charge_current_limit = 100; + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushRaw(r); +} + } // namespace Service::PTM diff --git a/src/core/hle/service/ptm/psm.h b/src/core/hle/service/ptm/psm.h index 2853a45753..77c38d1f9b 100644 --- a/src/core/hle/service/ptm/psm.h +++ b/src/core/hle/service/ptm/psm.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project @@ -22,10 +22,12 @@ private: LowPowerCharger = 2, Unknown = 3, }; - void GetBatteryChargePercentage(HLERequestContext& ctx); void GetChargerType(HLERequestContext& ctx); void OpenSession(HLERequestContext& ctx); + void GetBatteryVoltageState(HLERequestContext& ctx); + void GetBatteryAgePercentage(HLERequestContext& ctx); + void GetBatteryChargeInfoFields(HLERequestContext& ctx); }; } // namespace Service::PTM diff --git a/src/core/hle/service/ro/ro.cpp b/src/core/hle/service/ro/ro.cpp index cd50e69ce1..879e51c65e 100644 --- a/src/core/hle/service/ro/ro.cpp +++ b/src/core/hle/service/ro/ro.cpp @@ -21,10 +21,13 @@ namespace Service::RO { namespace { -// Convenience definitions. -constexpr size_t MaxSessions = 0x3; -constexpr size_t MaxNrrInfos = 0x40; -constexpr size_t MaxNroInfos = 0x40; +// Atmosphere defines as follows: +// Sessions = 0x03, NrrInfos = 0x40, NroInfos = 0x40 +// This may not be enough for some mods (plugin.nro dependant games) like SSBU +// Suppose someone loads like 64 plugins of these, now what? +constexpr size_t MaxSessions = 0x03; // No change +constexpr size_t MaxNrrInfos = 0x100; // Up to 256 NRRs +constexpr size_t MaxNroInfos = 0x100; // Up to 256 NROs constexpr u64 InvalidProcessId = 0xffffffffffffffffULL; constexpr u64 InvalidContextId = 0xffffffffffffffffULL; @@ -54,7 +57,7 @@ struct NrrInfo { struct ProcessContext { constexpr ProcessContext() = default; - void Initialize(Kernel::KProcess* process, u64 process_id) { + void Initialize(Kernel::KernelCore& kernel, Kernel::KProcess* process, u64 process_id) { ASSERT(!m_in_use); m_nro_in_use = {}; @@ -67,15 +70,15 @@ struct ProcessContext { m_in_use = true; if (m_process) { - m_process->Open(); + m_process->Open(kernel); } } - void Finalize() { + void Finalize(Kernel::KernelCore& kernel) { ASSERT(m_in_use); if (m_process) { - m_process->Close(); + m_process->Close(kernel); } m_nro_in_use = {}; @@ -304,7 +307,7 @@ class RoContext { public: explicit RoContext() = default; - Result RegisterProcess(size_t* out_context_id, Kernel::KProcess* process, u64 process_id) { + Result RegisterProcess(Kernel::KernelCore& kernel, size_t* out_context_id, Kernel::KProcess* process, u64 process_id) { // Validate process id. R_UNLESS(process->GetProcessId() == process_id, RO::ResultInvalidProcess); @@ -312,7 +315,7 @@ public: R_UNLESS(this->GetContextByProcessId(process_id) == nullptr, RO::ResultInvalidSession); // Allocate a context to manage the process handle. - *out_context_id = this->AllocateContext(process, process_id); + *out_context_id = this->AllocateContext(kernel, process, process_id); R_SUCCEED(); } @@ -324,8 +327,8 @@ public: R_SUCCEED(); } - void UnregisterProcess(size_t context_id) { - this->FreeContext(context_id); + void UnregisterProcess(Kernel::KernelCore& kernel, size_t context_id) { + this->FreeContext(kernel, context_id); } Result RegisterModuleInfo(size_t context_id, u64 nrr_address, u64 nrr_size, NrrKind nrr_kind, @@ -478,13 +481,13 @@ private: return nullptr; } - size_t AllocateContext(Kernel::KProcess* process, u64 process_id) { + size_t AllocateContext(Kernel::KernelCore& kernel, Kernel::KProcess* process, u64 process_id) { // Find a free process context. for (size_t i = 0; i < MaxSessions; i++) { ProcessContext* context = std::addressof(process_contexts[i]); if (context->IsFree()) { - context->Initialize(process, process_id); + context->Initialize(kernel, process, process_id); return i; } } @@ -493,9 +496,9 @@ private: UNREACHABLE(); } - void FreeContext(size_t context_id) { + void FreeContext(Kernel::KernelCore& kernel, size_t context_id) { if (ProcessContext* context = GetContextById(context_id); context != nullptr) { - context->Finalize(); + context->Finalize(kernel); } } }; @@ -522,7 +525,7 @@ public: } ~RoInterface() { - m_ro->UnregisterProcess(m_context_id); + m_ro->UnregisterProcess(system.Kernel(), m_context_id); } Result MapManualLoadModuleMemory(Out out_load_address, ClientProcessId client_pid, @@ -548,20 +551,16 @@ public: R_RETURN(m_ro->UnregisterModuleInfo(m_context_id, nrr_address)); } - Result RegisterProcessHandle(ClientProcessId client_pid, - InCopyHandle process) { + Result RegisterProcessHandle(ClientProcessId client_pid, InCopyHandle process) { // Register the process. - R_RETURN(m_ro->RegisterProcess(std::addressof(m_context_id), process.Get(), *client_pid)); + R_RETURN(m_ro->RegisterProcess(system.Kernel(), std::addressof(m_context_id), process.Get(), *client_pid)); } - Result RegisterProcessModuleInfo(ClientProcessId client_pid, u64 nrr_address, u64 nrr_size, - InCopyHandle process) { + Result RegisterProcessModuleInfo(ClientProcessId client_pid, u64 nrr_address, u64 nrr_size, InCopyHandle process) { // Validate the process. R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid)); - // Register the module. - R_RETURN(m_ro->RegisterModuleInfo(m_context_id, nrr_address, nrr_size, m_nrr_kind, - m_nrr_kind == NrrKind::JitPlugin)); + R_RETURN(m_ro->RegisterModuleInfo(m_context_id, nrr_address, nrr_size, m_nrr_kind, m_nrr_kind == NrrKind::JitPlugin)); } private: diff --git a/src/core/hle/service/ro/ro_results.h b/src/core/hle/service/ro/ro_results.h index 00f05c5a59..906378bc7f 100644 --- a/src/core/hle/service/ro/ro_results.h +++ b/src/core/hle/service/ro/ro_results.h @@ -1,6 +1,11 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#pragma once + #include "core/hle/result.h" namespace Service::RO { diff --git a/src/core/hle/service/ro/ro_types.h b/src/core/hle/service/ro/ro_types.h index 50b424395a..8e88f43246 100644 --- a/src/core/hle/service/ro/ro_types.h +++ b/src/core/hle/service/ro/ro_types.h @@ -4,6 +4,8 @@ // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#pragma once + #include "common/assert.h" #include "common/common_funcs.h" #include "common/common_types.h" diff --git a/src/core/hle/service/server_manager.cpp b/src/core/hle/service/server_manager.cpp index 3d898725e8..ee0dc83ee8 100644 --- a/src/core/hle/service/server_manager.cpp +++ b/src/core/hle/service/server_manager.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -28,13 +31,15 @@ enum class UserDataTag { class Port : public MultiWaitHolder, public Common::IntrusiveListBaseNode { public: - explicit Port(Kernel::KServerPort* server_port, SessionRequestHandlerFactory&& handler_factory) - : MultiWaitHolder(server_port), m_handler_factory(std::move(handler_factory)) { + explicit Port(Kernel::KernelCore& kernel, Kernel::KServerPort* server_port, SessionRequestHandlerFactory&& handler_factory) + : MultiWaitHolder(server_port), m_handler_factory(std::move(handler_factory)) + , m_kernel{kernel} + { this->SetUserData(static_cast(UserDataTag::Port)); } ~Port() { - this->GetNativeHandle()->Close(); + this->GetNativeHandle()->Close(m_kernel); } SessionRequestHandlerPtr CreateHandler() { @@ -43,18 +48,20 @@ public: private: const SessionRequestHandlerFactory m_handler_factory; + Kernel::KernelCore& m_kernel; }; class Session : public MultiWaitHolder, public Common::IntrusiveListBaseNode { public: - explicit Session(Kernel::KServerSession* server_session, - std::shared_ptr&& manager) - : MultiWaitHolder(server_session), m_manager(std::move(manager)) { + explicit Session(Kernel::KernelCore& kernel, Kernel::KServerSession* server_session, std::shared_ptr&& manager) + : MultiWaitHolder(server_session), m_manager(std::move(manager)) + , m_kernel{kernel} + { this->SetUserData(static_cast(UserDataTag::Session)); } ~Session() { - this->GetNativeHandle()->Close(); + this->GetNativeHandle()->Close(m_kernel); } std::shared_ptr& GetManager() { @@ -68,12 +75,16 @@ public: private: std::shared_ptr m_manager; std::shared_ptr m_context; + Kernel::KernelCore& m_kernel; }; -ServerManager::ServerManager(Core::System& system) : m_system{system}, m_selection_mutex{system} { +ServerManager::ServerManager(Core::System& system) + : m_system{system} + , m_selection_mutex{system} +{ // Initialize event. m_wakeup_event = Kernel::KEvent::Create(system.Kernel()); - m_wakeup_event->Initialize(nullptr); + m_wakeup_event->Initialize(m_system.Kernel(), nullptr); // Register event. Kernel::KEvent::Register(system.Kernel(), m_wakeup_event); @@ -86,7 +97,7 @@ ServerManager::ServerManager(Core::System& system) : m_system{system}, m_selecti ServerManager::~ServerManager() { // Signal stop. m_stop_source.request_stop(); - m_wakeup_event->Signal(); + m_wakeup_event->Signal(m_system.Kernel()); // Wait for processing to stop. m_stopped.Wait(); @@ -109,11 +120,11 @@ ServerManager::~ServerManager() { } // Close wakeup event. - m_wakeup_event->GetReadableEvent().Close(); - m_wakeup_event->Close(); + m_wakeup_event->GetReadableEvent().Close(m_system.Kernel()); + m_wakeup_event->Close(m_system.Kernel()); if (m_deferral_event) { - m_deferral_event->GetReadableEvent().Close(); + m_deferral_event->GetReadableEvent().Close(m_system.Kernel()); // Write event is owned by ServiceManager } } @@ -125,7 +136,7 @@ void ServerManager::RunServer(std::unique_ptr&& server_manager) { Result ServerManager::RegisterSession(Kernel::KServerSession* server_session, std::shared_ptr manager) { // We are taking ownership of the server session, so don't open it. - auto* session = new Session(server_session, std::move(manager)); + auto* session = new Session(m_system.Kernel(), server_session, std::move(manager)); // Begin tracking the server session. { @@ -148,7 +159,7 @@ Result ServerManager::RegisterNamedService(const std::string& service_name, max_sessions, handler_factory)); // We are taking ownership of the server port, so don't open it. - auto* server = new Port(server_port, std::move(handler_factory)); + auto* server = new Port(m_system.Kernel(), server_port, std::move(handler_factory)); // Begin tracking the server port. { @@ -177,15 +188,15 @@ Result ServerManager::ManageNamedPort(const std::string& service_name, u32 max_sessions) { // Create a new port. auto* port = Kernel::KPort::Create(m_system.Kernel()); - port->Initialize(max_sessions, false, 0); + port->Initialize(m_system.Kernel(), max_sessions, false, 0); // Register the port. Kernel::KPort::Register(m_system.Kernel(), port); // Ensure that our reference to the port is closed if we fail to register it. SCOPE_EXIT { - port->GetClientPort().Close(); - port->GetServerPort().Close(); + port->GetClientPort().Close(m_system.Kernel()); + port->GetServerPort().Close(m_system.Kernel()); }; // Register the object name with the kernel. @@ -193,10 +204,10 @@ Result ServerManager::ManageNamedPort(const std::string& service_name, service_name.c_str())); // Open a new reference to the server port. - port->GetServerPort().Open(); + port->GetServerPort().Open(m_system.Kernel()); // Transfer ownership into a new port object. - auto* server = new Port(std::addressof(port->GetServerPort()), std::move(handler_factory)); + auto* server = new Port(m_system.Kernel(), std::addressof(port->GetServerPort()), std::move(handler_factory)); // Begin tracking the port. { @@ -217,7 +228,7 @@ Result ServerManager::ManageDeferral(Kernel::KEvent** out_event) { ASSERT(m_deferral_event != nullptr); // Initialize the event. - m_deferral_event->Initialize(nullptr); + m_deferral_event->Initialize(m_system.Kernel(), nullptr); // Register the event. Kernel::KEvent::Register(m_system.Kernel(), m_deferral_event); @@ -258,7 +269,7 @@ void ServerManager::LinkToDeferredList(MultiWaitHolder* holder) { } // Signal the wakeup event. - m_wakeup_event->Signal(); + m_wakeup_event->Signal(m_system.Kernel()); } void ServerManager::LinkDeferred() { @@ -281,7 +292,7 @@ MultiWaitHolder* ServerManager::WaitSignaled() { auto* selected = m_multi_wait.WaitAny(m_system.Kernel()); if (selected == std::addressof(*m_wakeup_holder)) { // Clear and restart if we were woken up. - m_wakeup_event->Clear(); + m_wakeup_event->Clear(m_system.Kernel()); } else { // Unlink and handle the event. selected->UnlinkFromMultiWait(); @@ -323,7 +334,7 @@ Result ServerManager::LoopProcessImpl() { Result ServerManager::OnPortEvent(Port* server) { // Accept a new server session. auto* server_port = static_cast(server->GetNativeHandle()); - Kernel::KServerSession* server_session = server_port->AcceptSession(); + Kernel::KServerSession* server_session = server_port->AcceptSession(m_system.Kernel()); ASSERT(server_session != nullptr); // Create the session manager and install the handler. @@ -345,7 +356,7 @@ Result ServerManager::OnSessionEvent(Session* session) { // Try to receive a message. auto* server_session = static_cast(session->GetNativeHandle()); - res = server_session->ReceiveRequestHLE(&session->GetContext(), session->GetManager()); + res = server_session->ReceiveRequestHLE(m_system.Kernel(), &session->GetContext(), session->GetManager()); // If the session has been closed, we're done. if (res == Kernel::ResultSessionClosed) { @@ -382,7 +393,7 @@ Result ServerManager::CompleteSyncRequest(Session* session) { } // Send the reply. - res = server_session->SendReplyHLE(); + res = server_session->SendReplyHLE(m_system.Kernel()); // If the session has been closed, we're done. if (res == Kernel::ResultSessionClosed || service_res == IPC::ResultSessionClosed) { @@ -401,7 +412,7 @@ Result ServerManager::CompleteSyncRequest(Session* session) { Result ServerManager::OnDeferralEvent() { // Clear event before grabbing the list. - m_deferral_event->Clear(); + m_deferral_event->Clear(m_system.Kernel()); // Get and clear list. const auto deferrals = [&] { diff --git a/src/core/hle/service/services.cpp b/src/core/hle/service/services.cpp index 636f54ad49..4dfdf9068f 100644 --- a/src/core/hle/service/services.cpp +++ b/src/core/hle/service/services.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project @@ -25,8 +25,10 @@ #include "core/hle/service/friend/friend.h" #include "core/hle/service/glue/glue.h" #include "core/hle/service/grc/grc.h" +#include "core/hle/service/gpio/gpio.h" #include "core/hle/service/hid/hid.h" #include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/i2c/i2c.h" #include "core/hle/service/jit/jit.h" #include "core/hle/service/lbl/lbl.h" #include "core/hle/service/ldn/ldn.h" @@ -144,7 +146,9 @@ Services::Services(std::shared_ptr& sm, Core::System& system {"ro", &RO::LoopProcess}, {"spl", &SPL::LoopProcess}, {"ssl", &SSL::LoopProcess}, - {"usb", &USB::LoopProcess} + {"usb", &USB::LoopProcess}, + {"i2c", &I2C::LoopProcess}, + {"gpio", &GPIO::LoopProcess}, }) kernel.RunOnGuestCoreProcess(std::string(e.first), [&system, f = e.second] { f(system); }); } diff --git a/src/core/hle/service/set/system_settings_server.cpp b/src/core/hle/service/set/system_settings_server.cpp index 0ed6d28dd4..7fa74e6689 100644 --- a/src/core/hle/service/set/system_settings_server.cpp +++ b/src/core/hle/service/set/system_settings_server.cpp @@ -142,10 +142,10 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_) {22, C<&ISystemSettingsServer::SetEulaVersions>, "SetEulaVersions"}, {23, C<&ISystemSettingsServer::GetColorSetId>, "GetColorSetId"}, {24, C<&ISystemSettingsServer::SetColorSetId>, "SetColorSetId"}, - {25, nullptr, "GetConsoleInformationUploadFlag"}, - {26, nullptr, "SetConsoleInformationUploadFlag"}, - {27, nullptr, "GetAutomaticApplicationDownloadFlag"}, - {28, nullptr, "SetAutomaticApplicationDownloadFlag"}, + {25, C<&ISystemSettingsServer::GetConsoleInformationUploadFlag>, "GetConsoleInformationUploadFlag"}, + {26, C<&ISystemSettingsServer::SetConsoleInformationUploadFlag>, "SetConsoleInformationUploadFlag"}, + {27, C<&ISystemSettingsServer::GetAutomaticApplicationDownloadFlag>, "GetAutomaticApplicationDownloadFlag"}, + {28, C<&ISystemSettingsServer::SetAutomaticApplicationDownloadFlag>, "SetAutomaticApplicationDownloadFlag"}, {29, C<&ISystemSettingsServer::GetNotificationSettings>, "GetNotificationSettings"}, {30, C<&ISystemSettingsServer::SetNotificationSettings>, "SetNotificationSettings"}, {31, C<&ISystemSettingsServer::GetAccountNotificationSettings>, "GetAccountNotificationSettings"}, @@ -160,8 +160,8 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_) {42, nullptr, "SetEdid"}, {43, C<&ISystemSettingsServer::GetAudioOutputMode>, "GetAudioOutputMode"}, {44, C<&ISystemSettingsServer::SetAudioOutputMode>, "SetAudioOutputMode"}, - {45, C<&ISystemSettingsServer::GetSpeakerAutoMuteFlag> , "GetSpeakerAutoMuteFlag"}, - {46, C<&ISystemSettingsServer::SetSpeakerAutoMuteFlag> , "SetSpeakerAutoMuteFlag"}, + {45, C<&ISystemSettingsServer::GetSpeakerAutoMuteFlag>, "GetSpeakerAutoMuteFlag"}, + {46, C<&ISystemSettingsServer::SetSpeakerAutoMuteFlag>, "SetSpeakerAutoMuteFlag"}, {47, C<&ISystemSettingsServer::GetQuestFlag>, "GetQuestFlag"}, {48, C<&ISystemSettingsServer::SetQuestFlag>, "SetQuestFlag"}, {49, nullptr, "GetDataDeletionSettings"}, @@ -180,8 +180,8 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_) {62, C<&ISystemSettingsServer::GetDebugModeFlag>, "GetDebugModeFlag"}, {63, C<&ISystemSettingsServer::GetPrimaryAlbumStorage>, "GetPrimaryAlbumStorage"}, {64, C<&ISystemSettingsServer::SetPrimaryAlbumStorage>, "SetPrimaryAlbumStorage"}, - {65, nullptr, "GetUsb30EnableFlag"}, - {66, nullptr, "SetUsb30EnableFlag"}, + {65, C<&ISystemSettingsServer::GetUsb30EnableFlag>, "GetUsb30EnableFlag"}, + {66, C<&ISystemSettingsServer::SetUsb30EnableFlag>, "SetUsb30EnableFlag"}, {67, C<&ISystemSettingsServer::GetBatteryLot>, "GetBatteryLot"}, {68, C<&ISystemSettingsServer::GetSerialNumber>, "GetSerialNumber"}, {69, C<&ISystemSettingsServer::GetNfcEnableFlag>, "GetNfcEnableFlag"}, @@ -1074,6 +1074,45 @@ Result ISystemSettingsServer::SetNfcEnableFlag(bool nfc_enable_flag) { R_SUCCEED(); } +Result ISystemSettingsServer::GetConsoleInformationUploadFlag(Out out_flag) { + LOG_INFO(Service_SET, "called {}", m_system_settings.console_information_upload_flag); + *out_flag = m_system_settings.console_information_upload_flag; + R_SUCCEED(); +} + +Result ISystemSettingsServer::SetConsoleInformationUploadFlag(bool flag) { + LOG_INFO(Service_SET, "called {}", flag); + m_system_settings.usb_30_enable_flag = flag; + SetSaveNeeded(); + R_SUCCEED(); +} + +Result ISystemSettingsServer::GetAutomaticApplicationDownloadFlag(Out out_flag) { + LOG_INFO(Service_SET, "called {}", m_system_settings.usb_30_enable_flag); + *out_flag = m_system_settings.automatic_application_download_flag; + R_SUCCEED(); +} + +Result ISystemSettingsServer::SetAutomaticApplicationDownloadFlag(bool flag) { + LOG_INFO(Service_SET, "called {}", flag); + m_system_settings.automatic_application_download_flag = flag; + SetSaveNeeded(); + R_SUCCEED(); +} + +Result ISystemSettingsServer::GetUsb30EnableFlag(Out out_usb30_enable_flag) { + LOG_INFO(Service_SET, "called, usb30_enable_flag={}", m_system_settings.usb_30_enable_flag); + *out_usb30_enable_flag = m_system_settings.usb_30_enable_flag; + R_SUCCEED(); +} + +Result ISystemSettingsServer::SetUsb30EnableFlag(bool usb30_enable_flag) { + LOG_INFO(Service_SET, "called, usb30_enable_flag={}", usb30_enable_flag); + m_system_settings.usb_30_enable_flag = usb30_enable_flag; + SetSaveNeeded(); + R_SUCCEED(); +} + Result ISystemSettingsServer::GetSleepSettings(Out out_sleep_settings) { LOG_INFO(Service_SET, "called, flags={}, handheld_sleep_plan={}, console_sleep_plan={}", m_system_settings.sleep_settings.flags.raw, diff --git a/src/core/hle/service/set/system_settings_server.h b/src/core/hle/service/set/system_settings_server.h index 2a160dfc6b..1d56e61f88 100644 --- a/src/core/hle/service/set/system_settings_server.h +++ b/src/core/hle/service/set/system_settings_server.h @@ -109,6 +109,12 @@ public: Result SetPrimaryAlbumStorage(PrimaryAlbumStorage primary_album_storage); Result GetBatteryLot(Out out_battery_lot); Result GetSerialNumber(Out out_console_serial); + Result GetConsoleInformationUploadFlag(Out out_flag); + Result SetConsoleInformationUploadFlag(bool flag); + Result GetAutomaticApplicationDownloadFlag(Out out_flag); + Result SetAutomaticApplicationDownloadFlag(bool flag); + Result GetUsb30EnableFlag(Out out_usb30_enable_flag); + Result SetUsb30EnableFlag(bool usb30_enable_flag); Result GetNfcEnableFlag(Out out_nfc_enable_flag); Result SetNfcEnableFlag(bool nfc_enable_flag); Result GetSleepSettings(Out out_sleep_settings); diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index 1095dcf6c3..241533d53b 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -11,6 +14,7 @@ #include "core/hle/kernel/k_scoped_resource_reservation.h" #include "core/hle/kernel/k_server_port.h" #include "core/hle/result.h" +#include "core/hle/service/cmif_types.h" #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/server_manager.h" #include "core/hle/service/sm/sm.h" @@ -29,11 +33,11 @@ ServiceManager::ServiceManager(Kernel::KernelCore& kernel_) : kernel{kernel_} { ServiceManager::~ServiceManager() { for (auto& [name, port] : service_ports) { - port->Close(); + port->Close(kernel); } if (deferral_event) { - deferral_event->Close(); + deferral_event->Close(kernel); } } @@ -60,7 +64,7 @@ Result ServiceManager::RegisterService(Kernel::KServerPort** out_server_port, st } auto* port = Kernel::KPort::Create(kernel); - port->Initialize(ServerSessionCountMax, false, 0); + port->Initialize(kernel, ServerSessionCountMax, false, 0); // Register the port. Kernel::KPort::Register(kernel, port); @@ -68,7 +72,7 @@ Result ServiceManager::RegisterService(Kernel::KServerPort** out_server_port, st service_ports.emplace(name, std::addressof(port->GetClientPort())); registered_services.emplace(name, handler); if (deferral_event) { - deferral_event->Signal(); + deferral_event->Signal(kernel); } // Set our output. @@ -191,7 +195,7 @@ Result SM::GetServiceImpl(Kernel::KClientSession** out_client_session, HLEReques // Create a new session. Kernel::KClientSession* session{}; - if (const auto result = client_port->CreateSession(&session); result.IsError()) { + if (const auto result = client_port->CreateSession(kernel, &session); result.IsError()) { LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, result.raw); return result; } @@ -250,15 +254,37 @@ void SM::UnregisterService(HLERequestContext& ctx) { rb.Push(service_manager.UnregisterService(name)); } +void SM::AtmosphereHasService(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + std::string name(PopServiceName(rp)); + LOG_WARNING(Service_SM, "(stubbed) called with name={}", name); + IPC::ResponseBuilder rb{ctx, 3}; + Kernel::KClientPort* out_client_port = nullptr; + rb.Push(ResultSuccess); + rb.Push(service_manager.GetServicePort(&out_client_port, name) == ResultSuccess); +} + SM::SM(ServiceManager& service_manager_, Core::System& system_) - : ServiceFramework{system_, "sm:", 4}, - service_manager{service_manager_}, kernel{system_.Kernel()} { + : ServiceFramework{system_, "sm:", 4} + , service_manager{service_manager_} + , kernel{system_.Kernel()} +{ RegisterHandlers({ {0, &SM::Initialize, "Initialize"}, {1, &SM::GetServiceCmif, "GetService"}, {2, &SM::RegisterServiceCmif, "RegisterService"}, {3, &SM::UnregisterService, "UnregisterService"}, {4, nullptr, "DetachClient"}, + // TODO: are these non-TIPC as well? + {65000, nullptr, "AtmosphereInstallMitm"}, + {65001, nullptr, "AtmosphereUninstallMitm"}, + {65002, nullptr, "Deprecated_AtmosphereAssociatePidTidForMitm"}, + {65003, nullptr, "AtmosphereAcknowledgeMitmSession"}, + {65004, nullptr, "AtmosphereHasMitm"}, + {65005, nullptr, "AtmosphereWaitMitm"}, + {65006, nullptr, "AtmosphereDeclareFutureMitm"}, + {65100, &SM::AtmosphereHasService, "AtmosphereHasService"}, + {65101, nullptr, "AtmosphereWaitService"}, }); RegisterHandlersTipc({ {0, &SM::Initialize, "Initialize"}, @@ -266,6 +292,15 @@ SM::SM(ServiceManager& service_manager_, Core::System& system_) {2, &SM::RegisterServiceTipc, "RegisterService"}, {3, &SM::UnregisterService, "UnregisterService"}, {4, nullptr, "DetachClient"}, + {65000, nullptr, "AtmosphereInstallMitm"}, + {65001, nullptr, "AtmosphereUninstallMitm"}, + {65002, nullptr, "Deprecated_AtmosphereAssociatePidTidForMitm"}, + {65003, nullptr, "AtmosphereAcknowledgeMitmSession"}, + {65004, nullptr, "AtmosphereHasMitm"}, + {65005, nullptr, "AtmosphereWaitMitm"}, + {65006, nullptr, "AtmosphereDeclareFutureMitm"}, + {65100, &SM::AtmosphereHasService, "AtmosphereHasService"}, + {65101, nullptr, "AtmosphereWaitService"}, }); } diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h index 65e6876926..b566452ad7 100644 --- a/src/core/hle/service/sm/sm.h +++ b/src/core/hle/service/sm/sm.h @@ -47,6 +47,7 @@ private: void RegisterServiceCmif(HLERequestContext& ctx); void RegisterServiceTipc(HLERequestContext& ctx); void UnregisterService(HLERequestContext& ctx); + void AtmosphereHasService(HLERequestContext& ctx); Result GetServiceImpl(Kernel::KClientSession** out_client_session, HLERequestContext& ctx); void RegisterServiceImpl(HLERequestContext& ctx, std::string name, u32 max_session_count, diff --git a/src/core/hle/service/sm/sm_controller.cpp b/src/core/hle/service/sm/sm_controller.cpp index 00c88f6de1..7d7ef38152 100644 --- a/src/core/hle/service/sm/sm_controller.cpp +++ b/src/core/hle/service/sm/sm_controller.cpp @@ -37,8 +37,7 @@ void Controller::CloneCurrentObject(HLERequestContext& ctx) { // once this is a proper process // Reserve a new session from the process resource limit. - Kernel::KScopedResourceReservation session_reservation( - Kernel::GetCurrentProcessPointer(kernel), Kernel::LimitableResource::SessionCountMax); + Kernel::KScopedResourceReservation session_reservation(system.Kernel(), Kernel::GetCurrentProcessPointer(kernel), Kernel::LimitableResource::SessionCountMax); ASSERT(session_reservation.Succeeded()); // Create the session. @@ -46,7 +45,7 @@ void Controller::CloneCurrentObject(HLERequestContext& ctx) { ASSERT(session != nullptr); // Initialize the session. - session->Initialize(nullptr, 0); + session->Initialize(kernel, nullptr, 0); // Commit the session reservation. session_reservation.Commit(); diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp index fd758d4915..b8078bfd99 100644 --- a/src/core/hle/service/sockets/bsd.cpp +++ b/src/core/hle/service/sockets/bsd.cpp @@ -11,6 +11,7 @@ #include +#include "common/logging.h" #include "common/socket_types.h" #include "core/core.h" #include "core/hle/kernel/k_thread.h" @@ -23,9 +24,6 @@ #include "network/network.h" #include -using Common::Expected; -using Common::Unexpected; - namespace Service::Sockets { namespace { @@ -463,13 +461,22 @@ void BSD::DuplicateSocket(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; auto input = rp.PopRaw(); - Expected res = DuplicateSocketImpl(input.fd); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(ResultSuccess); - rb.PushRaw(OutputParameters{ - .ret = res.value_or(0), - .bsd_errno = res ? Errno::SUCCESS : res.error(), - }); + + auto const res_v = DuplicateSocketImpl(input.fd); + if (auto* res = std::get_if(&res_v)) { + rb.PushRaw(OutputParameters{ + .ret = *res, + .bsd_errno = Errno::SUCCESS, + }); + } else { + auto* err = std::get_if(&res_v); + rb.PushRaw(OutputParameters{ + .ret = 0, + .bsd_errno = *err, + }); + } } void BSD::EventFd(HLERequestContext& ctx) { @@ -629,7 +636,12 @@ Errno BSD::BindImpl(s32 fd, std::span addr) { if (!IsFileDescriptorValid(fd)) { return Errno::BADF; } - ASSERT(addr.size() == sizeof(SockAddrIn)); + ASSERT(addr.size() >= 16); + if (!file_descriptors[fd]->socket) { + LOG_WARNING(Service, "Uninitialized socket"); + return Errno::BADF; + } + auto addr_in = GetValue(addr); return Translate(file_descriptors[fd]->socket->Bind(Translate(addr_in))); @@ -640,7 +652,12 @@ Errno BSD::ConnectImpl(s32 fd, std::span addr) { return Errno::BADF; } - UNIMPLEMENTED_IF(addr.size() != sizeof(SockAddrIn)); + ASSERT(addr.size() >= 16); + if (!file_descriptors[fd]->socket) { + LOG_WARNING(Service, "Uninitialized socket"); + return Errno::BADF; + } + auto addr_in = GetValue(addr); const Errno result = Translate(file_descriptors[fd]->socket->Connect(Translate(addr_in))); @@ -658,6 +675,11 @@ Errno BSD::GetPeerNameImpl(s32 fd, std::vector& write_buffer) { return Errno::BADF; } + if (!file_descriptors[fd]->socket) { + LOG_WARNING(Service, "Uninitialized socket"); + return Errno::BADF; + } + const auto [addr_in, bsd_errno] = file_descriptors[fd]->socket->GetPeerName(); if (bsd_errno != Network::Errno::SUCCESS) { return Translate(bsd_errno); @@ -675,6 +697,11 @@ Errno BSD::GetSockNameImpl(s32 fd, std::vector& write_buffer) { return Errno::BADF; } + if (!file_descriptors[fd]->socket) { + LOG_WARNING(Service, "Uninitialized socket"); + return Errno::BADF; + } + const auto [addr_in, bsd_errno] = file_descriptors[fd]->socket->GetSockName(); if (bsd_errno != Network::Errno::SUCCESS) { return Translate(bsd_errno); @@ -691,6 +718,10 @@ Errno BSD::ListenImpl(s32 fd, s32 backlog) { if (!IsFileDescriptorValid(fd)) { return Errno::BADF; } + if (!file_descriptors[fd]->socket) { + LOG_WARNING(Service, "Uninitialized socket"); + return Errno::BADF; + } return Translate(file_descriptors[fd]->socket->Listen(backlog)); } @@ -698,6 +729,10 @@ std::pair BSD::FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg) { if (!IsFileDescriptorValid(fd)) { return {-1, Errno::BADF}; } + if (!file_descriptors[fd]->socket) { + LOG_WARNING(Service, "Uninitialized socket"); + return {-1, Errno::BADF}; + } FileDescriptor& descriptor = *file_descriptors[fd]; @@ -724,6 +759,10 @@ Errno BSD::GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector& o if (!IsFileDescriptorValid(fd)) { return Errno::BADF; } + if (!file_descriptors[fd]->socket) { + LOG_WARNING(Service, "Uninitialized socket"); + return Errno::BADF; + } if (level != static_cast(SocketLevel::SOCKET)) { UNIMPLEMENTED_MSG("Unknown getsockopt level"); @@ -755,6 +794,10 @@ Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, std::spansocket) { + LOG_WARNING(Service, "Uninitialized socket"); + return Errno::BADF; + } if (level != static_cast(SocketLevel::SOCKET)) { LOG_WARNING(Service, "(STUBBED) setsockopt with level={}, optname={}", level, optname); @@ -805,6 +848,10 @@ Errno BSD::ShutdownImpl(s32 fd, s32 how) { if (!IsFileDescriptorValid(fd)) { return Errno::BADF; } + if (!file_descriptors[fd]->socket) { + LOG_WARNING(Service, "Uninitialized socket"); + return Errno::BADF; + } const Network::ShutdownHow host_how = Translate(static_cast(how)); return Translate(file_descriptors[fd]->socket->Shutdown(host_how)); } @@ -874,7 +921,7 @@ std::pair BSD::RecvFromImpl(s32 fd, u32 flags, std::vector& mess if (ret < 0) { addr.clear(); } else { - ASSERT(addr.size() == sizeof(SockAddrIn)); + ASSERT(addr.size() >= 16); const SockAddrIn result = Translate(addr_in); PutValue(addr, result); } @@ -887,6 +934,10 @@ std::pair BSD::SendImpl(s32 fd, u32 flags, std::span messa if (!IsFileDescriptorValid(fd)) { return {-1, Errno::BADF}; } + if (!file_descriptors[fd]->socket) { + LOG_WARNING(Service, "Uninitialized socket"); + return {-1, Errno::BADF}; + } return Translate(file_descriptors[fd]->socket->Send(message, flags)); } @@ -895,11 +946,15 @@ std::pair BSD::SendToImpl(s32 fd, u32 flags, std::span mes if (!IsFileDescriptorValid(fd)) { return {-1, Errno::BADF}; } + if (!file_descriptors[fd]->socket) { + LOG_WARNING(Service, "Uninitialized socket"); + return {-1, Errno::BADF}; + } Network::SockAddrIn addr_in; Network::SockAddrIn* p_addr_in = nullptr; if (!addr.empty()) { - ASSERT(addr.size() == sizeof(SockAddrIn)); + ASSERT(addr.size() >= 16); auto guest_addr_in = GetValue(addr); addr_in = Translate(guest_addr_in); p_addr_in = &addr_in; @@ -912,6 +967,10 @@ Errno BSD::CloseImpl(s32 fd) { if (!IsFileDescriptorValid(fd)) { return Errno::BADF; } + if (!file_descriptors[fd]->socket) { + LOG_WARNING(Service, "Uninitialized socket"); + return Errno::BADF; + } const Errno bsd_errno = Translate(file_descriptors[fd]->socket->Close()); if (bsd_errno != Errno::SUCCESS) { @@ -924,15 +983,15 @@ Errno BSD::CloseImpl(s32 fd) { return bsd_errno; } -Expected BSD::DuplicateSocketImpl(s32 fd) { +std::variant BSD::DuplicateSocketImpl(s32 fd) { if (!IsFileDescriptorValid(fd)) { - return Unexpected(Errno::BADF); + return Errno::BADF; } const s32 new_fd = FindFreeFileDescriptorHandle(); if (new_fd < 0) { LOG_ERROR(Service, "No more file descriptors available"); - return Unexpected(Errno::MFILE); + return Errno::MFILE; } file_descriptors[new_fd] = FileDescriptor{ @@ -947,6 +1006,10 @@ std::optional> BSD::GetSocket(s32 fd) { if (!IsFileDescriptorValid(fd)) { return std::nullopt; } + if (!file_descriptors[fd]->socket) { + LOG_WARNING(Service, "Uninitialized socket"); + return std::nullopt; + } return file_descriptors[fd]->socket; } diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h index 32b5bdc3cf..15e4a33592 100644 --- a/src/core/hle/service/sockets/bsd.h +++ b/src/core/hle/service/sockets/bsd.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -8,9 +8,9 @@ #include #include #include +#include #include "common/common_types.h" -#include "common/expected.h" #include "common/socket_types.h" #include "core/hle/service/service.h" #include "core/hle/service/sockets/sockets.h" @@ -35,7 +35,7 @@ public: // These methods are called from SSL; the first two are also called from // this class for the corresponding IPC methods. // On the real device, the SSL service makes IPC calls to this service. - Common::Expected DuplicateSocketImpl(s32 fd); + std::variant DuplicateSocketImpl(s32 fd); Errno CloseImpl(s32 fd); std::optional> GetSocket(s32 fd); diff --git a/src/core/hle/service/sockets/sfdnsres.cpp b/src/core/hle/service/sockets/sfdnsres.cpp index f9f0ee56eb..9e71be3547 100644 --- a/src/core/hle/service/sockets/sfdnsres.cpp +++ b/src/core/hle/service/sockets/sfdnsres.cpp @@ -55,12 +55,45 @@ enum class NetDbError : s32 { static const constexpr std::array blockedDomains = { "srv.nintendo.net", //obvious + "nintendo.es", + "nintendowifi.net", + "nintendo-europe.com", + "nintendo.com.hk", + "nintendo.com.au", + "nintendo.co.kr", + "nintendo.co.uk", + "nintendo.co.jp", + "nintendo.co.nz", + "nintendo.co.za", + "nintendo.com", + "nintendo.jp", + "nintendo.tw", + "nintendo.at", + "nintendo.be", + "nintendo.dk", + "nintendo.de", + "nintendo.fi", + "nintendo.fr", + "nintendo.gr", + "nintendo.hu", + "nintendo.it", + "nintendo.nl", + "nintendo.no", + "nintendo.pt", + "nintendo.ru", + "nintendo.ch", + "nintendo.se", + "nintendoswitch.com.cn", + "nintendoswitch.com", + "sun.hac.lp1.d4c.nintendo.net", "phoenix-api.wbagora.com", //hogwarts legacy "battle.net", - "microsoft.com", //minecraft dungeons + other games + "microsoft.com", // Minecraft dungeons + other games "mojang.com", "xboxlive.com", - "minecraftservices.com" + "api.epicgames.dev", // marvel cosmic invasion +? + "minecraftservices.com", + "508223012e5a5ff19f30a391b2bdadc0.my.2k.com", // Civilization 5 }; static bool IsBlockedHost(const std::string& host) { @@ -175,16 +208,15 @@ static std::pair GetHostByNameRequestImpl(HLERequestConte return {0, GetAddrInfoError::AGAIN}; } - auto res = Network::GetAddressInfo(host, /*service*/ std::nullopt); - if (!res.has_value()) { - return {0, Translate(res.error())}; + auto res_v = Network::GetAddressInfo(host, /*service*/ std::nullopt); + if (auto* res = std::get_if>(&res_v)) { + const std::vector data = SerializeAddrInfoAsHostEnt(*res, host); + const u32 data_size = u32(data.size()); + ctx.WriteBuffer(data, 0); + return {data_size, GetAddrInfoError::SUCCESS}; } - - const std::vector data = SerializeAddrInfoAsHostEnt(res.value(), host); - const u32 data_size = static_cast(data.size()); - ctx.WriteBuffer(data, 0); - - return {data_size, GetAddrInfoError::SUCCESS}; + auto* err = std::get_if(&res_v); + return {0, Translate(*err)}; } void SFDNSRES::GetHostByNameRequest(HLERequestContext& ctx) { @@ -238,7 +270,7 @@ static std::vector SerializeAddrInfo(const std::vector& v Append(data, static_cast(Translate(addrinfo.family))); // ai_family Append(data, static_cast(Translate(addrinfo.socket_type))); // ai_socktype Append(data, static_cast(Translate(addrinfo.protocol))); // ai_protocol - Append(data, sizeof(SockAddrIn)); // ai_addrlen + Append(data, 16); // ai_addrlen // ^ *not* sizeof(SerializedSockAddrIn), not that it matters since they're the same size // ai_addr: @@ -300,16 +332,15 @@ static std::pair GetAddrInfoRequestImpl(HLERequestContext // Serialized hints are also passed in a buffer, but are ignored for now. - auto res = Network::GetAddressInfo(host, service); - if (!res.has_value()) { - return {0, Translate(res.error())}; + auto res_v = Network::GetAddressInfo(host, service); + if (auto* res = std::get_if>(&res_v)) { + const std::vector data = SerializeAddrInfo(*res, host); + const u32 data_size = u32(data.size()); + ctx.WriteBuffer(data, 0); + return {data_size, GetAddrInfoError::SUCCESS}; } - - const std::vector data = SerializeAddrInfo(res.value(), host); - const u32 data_size = static_cast(data.size()); - ctx.WriteBuffer(data, 0); - - return {data_size, GetAddrInfoError::SUCCESS}; + auto* err = std::get_if(&res_v); + return {0, Translate(*err)}; } void SFDNSRES::GetAddrInfoRequest(HLERequestContext& ctx) { diff --git a/src/core/hle/service/sockets/sockets.h b/src/core/hle/service/sockets/sockets.h index b72bf53c16..2837ce71ea 100644 --- a/src/core/hle/service/sockets/sockets.h +++ b/src/core/hle/service/sockets/sockets.h @@ -65,10 +65,121 @@ enum class Type : u32 { }; enum class Protocol : u32 { - Unspecified = 0, + IP = 0, ICMP = 1, TCP = 6, UDP = 17, + // + IPV6 = 41, + RAW = 255, + // + HOPOPTS = 0, + IGMP = 2, + GGP = 3, + IPV4 = 4, + ST = 7, + EGP = 8, + PIGP = 9, + RCCMON = 10, + NVPII = 11, + PUP = 12, + ARGUS = 13, + EMCON = 14, + XNET = 15, + CHAOS = 16, + MUX = 18, + MEAS = 19, + HMP = 20, + PRM = 21, + IDP = 22, + TRUNK1 = 23, + TRUNK2 = 24, + LEAF1 = 25, + LEAF2 = 26, + RDP = 27, + IRTP = 28, + TP = 29, + BLT = 30, + NSP = 31, + INP = 32, + DCCP = 33, + //3PC = 34, + IDPR = 35, + XTP = 36, + DDP = 37, + CMTP = 38, + TPXX = 39, + IL = 40, + SDRP = 42, + ROUTING = 43, + FRAGMENT = 44, + IDRP = 45, + RSVP = 46, + GRE = 47, + MHRP = 48, + BHA = 49, + ESP = 50, + AH = 51, + INLSP = 52, + SWIPE = 53, + NHRP = 54, + MOBILE = 55, + TLSP = 56, + SKIP = 57, + ICMPV6 = 58, + NONE = 59, + DSTOPTS = 60, + AHIP = 61, + CFTP = 62, + HELLO = 63, + SATEXPAK = 64, + KRYPTOLAN = 65, + RVD = 66, + IPPC = 67, + ADFS = 68, + SATMON = 69, + VISA = 70, + IPCV = 71, + CPNX = 72, + CPHB = 73, + WSN = 74, + PVP = 75, + BRSATMON = 76, + ND = 77, + WBMON = 78, + WBEXPAK = 79, + EON = 80, + VMTP = 81, + SVMTP = 82, + VINES = 83, + TTP = 84, + IGP = 85, + DGP = 86, + TCF = 87, + IGRP = 88, + OSPFIGP = 89, + SRPC = 90, + LARP = 91, + MTP = 92, + AX25 = 93, + IPEIP = 94, + MICP = 95, + SCCSP = 96, + ETHERIP = 97, + ENCAP = 98, + APES = 99, + GMTP = 100, + IPCOMP = 108, + SCTP = 132, + MH = 135, + UDPLITE = 136, + HIP = 139, + SHIM6 = 140, + PIM = 103, + CARP = 112, + PGM = 113, + MPLS = 137, + PFSYNC = 240, }; enum class SocketLevel : u32 { @@ -110,8 +221,9 @@ struct SockAddrIn { u8 family; u16 portno; std::array ip; - std::array zeroes; + std::array zeroes; }; +static_assert(sizeof(SockAddrIn) == 0x100); enum class PollEvents : u16 { // Using Pascal case because IN is a macro on Windows. diff --git a/src/core/hle/service/sockets/sockets_translate.cpp b/src/core/hle/service/sockets/sockets_translate.cpp index ea12977f62..46ef0a89cb 100644 --- a/src/core/hle/service/sockets/sockets_translate.cpp +++ b/src/core/hle/service/sockets/sockets_translate.cpp @@ -175,49 +175,151 @@ Network::Type Translate(Type type) { Type Translate(Network::Type type) { switch (type) { - case Network::Type::Unspecified: - return Type::Unspecified; - case Network::Type::STREAM: - return Type::STREAM; - case Network::Type::DGRAM: - return Type::DGRAM; - case Network::Type::RAW: - return Type::RAW; - case Network::Type::SEQPACKET: - return Type::SEQPACKET; + case Network::Type::Unspecified: return Type::Unspecified; + case Network::Type::STREAM: return Type::STREAM; + case Network::Type::DGRAM: return Type::DGRAM; + case Network::Type::RAW: return Type::RAW; + case Network::Type::SEQPACKET: return Type::SEQPACKET; default: UNIMPLEMENTED_MSG("Unimplemented type={}", type); return Type{}; } } -Network::Protocol Translate(Protocol protocol) { +#define NETWORK_PROTOCOL_TRANSLATE_LIST \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(IP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(ICMP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(TCP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(UDP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(IPV6) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(RAW) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(IGMP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(GGP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(IPV4) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(ST) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(EGP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(PIGP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(RCCMON) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(NVPII) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(PUP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(ARGUS) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(EMCON) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(XNET) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(CHAOS) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(MUX) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(MEAS) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(HMP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(PRM) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(IDP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(TRUNK1) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(TRUNK2) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(LEAF1) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(LEAF2) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(RDP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(IRTP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(TP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(BLT) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(NSP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(INP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(DCCP) \ + /*NETWORK_PROTOCOL_TRANSLATE_ELEM(3PC)*/ \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(IDPR) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(XTP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(DDP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(CMTP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(TPXX) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(IL) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(SDRP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(ROUTING) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(FRAGMENT) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(IDRP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(RSVP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(GRE) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(MHRP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(BHA) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(ESP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(AH) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(INLSP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(SWIPE) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(NHRP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(MOBILE) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(TLSP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(SKIP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(ICMPV6) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(NONE) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(DSTOPTS) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(AHIP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(CFTP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(HELLO) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(SATEXPAK) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(KRYPTOLAN) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(RVD) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(IPPC) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(ADFS) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(SATMON) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(VISA) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(IPCV) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(CPNX) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(CPHB) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(WSN) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(PVP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(BRSATMON) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(ND) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(WBMON) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(WBEXPAK) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(EON) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(VMTP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(SVMTP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(VINES) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(TTP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(IGP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(DGP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(TCF) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(IGRP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(OSPFIGP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(SRPC) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(LARP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(MTP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(AX25) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(IPEIP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(MICP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(SCCSP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(ETHERIP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(ENCAP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(APES) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(GMTP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(IPCOMP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(SCTP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(MH) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(UDPLITE) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(HIP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(SHIM6) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(PIM) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(CARP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(PGM) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(MPLS) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(PFSYNC) +[[nodiscard]] Network::Protocol Translate(Protocol protocol) { switch (protocol) { - case Protocol::Unspecified: - return Network::Protocol::Unspecified; - case Protocol::TCP: - return Network::Protocol::TCP; - case Protocol::UDP: - return Network::Protocol::UDP; +#define NETWORK_PROTOCOL_TRANSLATE_ELEM(name) case Protocol::name: return Network::Protocol::name; + NETWORK_PROTOCOL_TRANSLATE_LIST +#undef NETWORK_PROTOCOL_TRANSLATE_ELEM default: UNIMPLEMENTED_MSG("Unimplemented protocol={}", protocol); - return Network::Protocol::Unspecified; + return {}; } } - -Protocol Translate(Network::Protocol protocol) { +[[nodiscard]] Protocol Translate(Network::Protocol protocol) { switch (protocol) { - case Network::Protocol::Unspecified: - return Protocol::Unspecified; - case Network::Protocol::TCP: - return Protocol::TCP; - case Network::Protocol::UDP: - return Protocol::UDP; +#define NETWORK_PROTOCOL_TRANSLATE_ELEM(name) case Network::Protocol::name: return Protocol::name; + NETWORK_PROTOCOL_TRANSLATE_LIST +#undef NETWORK_PROTOCOL_TRANSLATE_ELEM default: UNIMPLEMENTED_MSG("Unimplemented protocol={}", protocol); - return Protocol::Unspecified; + return {}; } } +#undef NETWORK_PROTOCOL_TRANSLATE_LIST Network::PollEvents Translate(PollEvents flags) { Network::PollEvents result{}; @@ -265,13 +367,9 @@ PollEvents Translate(Network::PollEvents flags) { } Network::SockAddrIn Translate(SockAddrIn value) { - if (value.len != 0 && value.len != sizeof(value) && value.len != 6) { - LOG_WARNING(Service, "Unexpected SockAddrIn len={}, expected 0, {}, or 6", - value.len, sizeof(value)); - } - + // All lengths are valid, from [0 upto 256] return { - .family = Translate(static_cast(value.family)), + .family = Translate(Domain(value.family)), .ip = value.ip, .portno = static_cast(value.portno >> 8 | value.portno << 8), }; @@ -279,7 +377,7 @@ Network::SockAddrIn Translate(SockAddrIn value) { SockAddrIn Translate(Network::SockAddrIn value) { return { - .len = sizeof(SockAddrIn), + .len = 16, .family = static_cast(Translate(value.family)), .portno = static_cast(value.portno >> 8 | value.portno << 8), .ip = value.ip, diff --git a/src/core/hle/service/ssl/ssl.cpp b/src/core/hle/service/ssl/ssl.cpp index b66c686b2a..766673c653 100644 --- a/src/core/hle/service/ssl/ssl.cpp +++ b/src/core/hle/service/ssl/ssl.cpp @@ -160,29 +160,26 @@ private: auto bsd = system.ServiceManager().GetService("bsd:u"); ASSERT_OR_EXECUTE(bsd, { return ResultInternalError; }); - auto res = bsd->DuplicateSocketImpl(fd); - if (!res.has_value()) { - LOG_ERROR(Service_SSL, "Failed to duplicate socket with fd {}", fd); - return ResultInvalidSocket; + auto const res_v = bsd->DuplicateSocketImpl(fd); + if (auto *res = std::get_if(&res_v)) { + const s32 duplicated_fd = *res; + if (do_not_close_socket) { + *out_fd = duplicated_fd; + } else { + *out_fd = -1; + fd_to_close = duplicated_fd; + } + std::optional> sock = bsd->GetSocket(duplicated_fd); + if (!sock.has_value()) { + LOG_ERROR(Service_SSL, "invalid socket fd {} after duplication", duplicated_fd); + return ResultInvalidSocket; + } + socket = std::move(*sock); + backend->SetSocket(socket); + return ResultSuccess; } - - const s32 duplicated_fd = *res; - - if (do_not_close_socket) { - *out_fd = duplicated_fd; - } else { - *out_fd = -1; - fd_to_close = duplicated_fd; - } - - std::optional> sock = bsd->GetSocket(duplicated_fd); - if (!sock.has_value()) { - LOG_ERROR(Service_SSL, "invalid socket fd {} after duplication", duplicated_fd); - return ResultInvalidSocket; - } - socket = std::move(*sock); - backend->SetSocket(socket); - return ResultSuccess; + LOG_ERROR(Service_SSL, "Failed to duplicate socket with fd {}", fd); + return ResultInvalidSocket; } Result SetHostNameImpl(const std::string& hostname) { diff --git a/src/core/hle/service/vi/application_display_service.h b/src/core/hle/service/vi/application_display_service.h index 1bdeb8f845..a2eeb4c70c 100644 --- a/src/core/hle/service/vi/application_display_service.h +++ b/src/core/hle/service/vi/application_display_service.h @@ -1,6 +1,11 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#pragma once + #include #include diff --git a/src/core/hle/service/vi/conductor.cpp b/src/core/hle/service/vi/conductor.cpp index 801a135dd7..002c653b8c 100644 --- a/src/core/hle/service/vi/conductor.cpp +++ b/src/core/hle/service/vi/conductor.cpp @@ -70,7 +70,7 @@ void Conductor::UnlinkVsyncEvent(u64 display_id, Event* event) { void Conductor::ProcessVsync() { for (auto& [display_id, manager] : m_vsync_managers) { m_container.ComposeOnDisplay(&m_swap_interval, &m_compose_speed_scale, display_id); - manager.SignalVsync(); + manager.SignalVsync(m_system.Kernel()); } } diff --git a/src/core/hle/service/vi/system_display_service.h b/src/core/hle/service/vi/system_display_service.h index d6cc8c75f1..a3b88c519d 100644 --- a/src/core/hle/service/vi/system_display_service.h +++ b/src/core/hle/service/vi/system_display_service.h @@ -1,9 +1,11 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#pragma once + #include "common/math_util.h" #include "core/hle/service/cmif_types.h" #include "core/hle/service/nvnflinger/ui/fence.h" diff --git a/src/core/hle/service/vi/vsync_manager.cpp b/src/core/hle/service/vi/vsync_manager.cpp index bdc4dfa966..59d0b82e0a 100644 --- a/src/core/hle/service/vi/vsync_manager.cpp +++ b/src/core/hle/service/vi/vsync_manager.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -9,18 +12,10 @@ namespace Service::VI { VsyncManager::VsyncManager() = default; VsyncManager::~VsyncManager() = default; -void VsyncManager::SignalVsync() { +void VsyncManager::SignalVsync(Kernel::KernelCore& kernel) { for (auto* event : m_vsync_events) { - event->Signal(); + event->Signal(kernel); } } -void VsyncManager::LinkVsyncEvent(Event* event) { - m_vsync_events.insert(event); -} - -void VsyncManager::UnlinkVsyncEvent(Event* event) { - m_vsync_events.erase(event); -} - } // namespace Service::VI diff --git a/src/core/hle/service/vi/vsync_manager.h b/src/core/hle/service/vi/vsync_manager.h index 5d45bb5eec..9c04516749 100644 --- a/src/core/hle/service/vi/vsync_manager.h +++ b/src/core/hle/service/vi/vsync_manager.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -5,6 +8,10 @@ #include +namespace Kernel { +class KernelCore; +} + namespace Service { class Event; } @@ -18,9 +25,13 @@ public: explicit VsyncManager(); ~VsyncManager(); - void SignalVsync(); - void LinkVsyncEvent(Event* event); - void UnlinkVsyncEvent(Event* event); + void SignalVsync(Kernel::KernelCore& kernel); + void LinkVsyncEvent(Event* event) { + m_vsync_events.insert(event); + } + void UnlinkVsyncEvent(Event* event) { + m_vsync_events.erase(event); + } private: std::set m_vsync_events; diff --git a/src/core/internal_network/network.cpp b/src/core/internal_network/network.cpp index 1b27934f69..077a2e042e 100644 --- a/src/core/internal_network/network.cpp +++ b/src/core/internal_network/network.cpp @@ -28,7 +28,6 @@ #include "common/assert.h" #include "common/common_types.h" -#include "common/expected.h" #include "common/logging.h" #include "common/settings.h" #include "core/internal_network/network.h" @@ -326,52 +325,37 @@ Errno GetAndLogLastError(CallType call_type = CallType::Other) { GetAddrInfoError TranslateGetAddrInfoErrorFromNative(int gai_err) { switch (gai_err) { - case 0: - return GetAddrInfoError::SUCCESS; + case 0: return GetAddrInfoError::SUCCESS; + case EAI_AGAIN: return GetAddrInfoError::AGAIN; + case EAI_BADFLAGS: return GetAddrInfoError::BADFLAGS; + case EAI_FAIL: return GetAddrInfoError::FAIL; + case EAI_FAMILY: return GetAddrInfoError::FAMILY; + case EAI_MEMORY: return GetAddrInfoError::MEMORY; + case EAI_NONAME: return GetAddrInfoError::NONAME; + case EAI_SERVICE: return GetAddrInfoError::SERVICE; + case EAI_SOCKTYPE: return GetAddrInfoError::SOCKTYPE; + // These codes may not be defined on all systems: #ifdef EAI_ADDRFAMILY - case EAI_ADDRFAMILY: - return GetAddrInfoError::ADDRFAMILY; + case EAI_ADDRFAMILY: return GetAddrInfoError::ADDRFAMILY; #endif - case EAI_AGAIN: - return GetAddrInfoError::AGAIN; - case EAI_BADFLAGS: - return GetAddrInfoError::BADFLAGS; - case EAI_FAIL: - return GetAddrInfoError::FAIL; - case EAI_FAMILY: - return GetAddrInfoError::FAMILY; - case EAI_MEMORY: - return GetAddrInfoError::MEMORY; - case EAI_NONAME: - return GetAddrInfoError::NONAME; - case EAI_SERVICE: - return GetAddrInfoError::SERVICE; - case EAI_SOCKTYPE: - return GetAddrInfoError::SOCKTYPE; - // These codes may not be defined on all systems: #ifdef EAI_SYSTEM - case EAI_SYSTEM: - return GetAddrInfoError::SYSTEM; + case EAI_SYSTEM: return GetAddrInfoError::SYSTEM; #endif #ifdef EAI_BADHINTS - case EAI_BADHINTS: - return GetAddrInfoError::BADHINTS; + case EAI_BADHINTS: return GetAddrInfoError::BADHINTS; #endif #ifdef EAI_PROTOCOL - case EAI_PROTOCOL: - return GetAddrInfoError::PROTOCOL; + case EAI_PROTOCOL: return GetAddrInfoError::PROTOCOL; #endif #ifdef EAI_OVERFLOW - case EAI_OVERFLOW: - return GetAddrInfoError::OVERFLOW_; + case EAI_OVERFLOW: return GetAddrInfoError::OVERFLOW_; #endif default: #ifdef EAI_NODATA // This can't be a case statement because it would create a duplicate // case on Windows where EAI_NODATA is an alias for EAI_NONAME. - if (gai_err == EAI_NODATA) { + if (gai_err == EAI_NODATA) return GetAddrInfoError::NODATA; - } #endif return GetAddrInfoError::OTHER; } @@ -405,14 +389,10 @@ Type TranslateTypeFromNative(int type) { switch (type) { case 0: return Type::Unspecified; - case SOCK_STREAM: - return Type::STREAM; - case SOCK_DGRAM: - return Type::DGRAM; - case SOCK_RAW: - return Type::RAW; - case SOCK_SEQPACKET: - return Type::SEQPACKET; + case SOCK_STREAM: return Type::STREAM; + case SOCK_DGRAM: return Type::DGRAM; + case SOCK_RAW: return Type::RAW; + case SOCK_SEQPACKET: return Type::SEQPACKET; default: UNIMPLEMENTED_MSG("Unimplemented type={}", type); return Type::STREAM; @@ -423,55 +403,227 @@ int TranslateTypeToNative(Type type) { switch (type) { case Type::Unspecified: return 0; - case Type::STREAM: - return SOCK_STREAM; - case Type::DGRAM: - return SOCK_DGRAM; - case Type::RAW: - return SOCK_RAW; + case Type::STREAM: return SOCK_STREAM; + case Type::DGRAM: return SOCK_DGRAM; + case Type::RAW: return SOCK_RAW; + case Type::SEQPACKET: return SOCK_SEQPACKET; default: UNIMPLEMENTED_MSG("Unimplemented type={}", type); return 0; } } -Protocol TranslateProtocolFromNative(int protocol) { +// Some of those protocols may not be supported on some platforms +// It doesn't really matter, except that some homebrew may not work correctly +// Official software uses TCP & UDP mainly, SCTP is used by some homebrew as well +#ifdef __FreeBSD__ +#define NETWORK_PROTOCOL_TRANSLATE_LIST \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(IP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(ICMP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(TCP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(UDP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(IPV6) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(RAW) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(IGMP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(GGP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(IPV4) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(ST) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(EGP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(PIGP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(RCCMON) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(NVPII) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(PUP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(ARGUS) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(EMCON) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(XNET) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(CHAOS) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(MUX) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(MEAS) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(HMP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(PRM) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(IDP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(TRUNK1) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(TRUNK2) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(LEAF1) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(LEAF2) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(RDP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(IRTP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(TP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(BLT) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(NSP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(INP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(DCCP) \ + /*NETWORK_PROTOCOL_TRANSLATE_ELEM(3PC)*/ \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(IDPR) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(XTP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(DDP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(CMTP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(TPXX) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(IL) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(SDRP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(ROUTING) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(FRAGMENT) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(IDRP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(RSVP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(GRE) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(MHRP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(BHA) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(ESP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(AH) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(INLSP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(SWIPE) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(NHRP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(MOBILE) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(TLSP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(SKIP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(ICMPV6) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(NONE) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(DSTOPTS) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(AHIP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(CFTP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(HELLO) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(SATEXPAK) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(KRYPTOLAN) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(RVD) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(IPPC) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(ADFS) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(SATMON) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(VISA) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(IPCV) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(CPNX) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(CPHB) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(WSN) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(PVP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(BRSATMON) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(ND) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(WBMON) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(WBEXPAK) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(EON) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(VMTP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(SVMTP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(VINES) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(TTP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(IGP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(DGP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(TCF) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(IGRP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(OSPFIGP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(SRPC) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(LARP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(MTP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(AX25) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(IPEIP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(MICP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(SCCSP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(ETHERIP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(ENCAP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(APES) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(GMTP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(IPCOMP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(SCTP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(MH) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(UDPLITE) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(HIP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(SHIM6) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(PIM) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(CARP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(PGM) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(MPLS) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(PFSYNC) +#elif defined(__linux__) +// Other platforms get fucked +#define NETWORK_PROTOCOL_TRANSLATE_LIST \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(IP) \ + /*NETWORK_PROTOCOL_TRANSLATE_ELEM(HOPOPTS)*/ \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(ICMP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(IGMP) \ + /*NETWORK_PROTOCOL_TRANSLATE_ELEM(IPIP)*/ \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(TCP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(EGP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(PUP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(UDP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(IDP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(TP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(DCCP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(IPV6) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(ROUTING) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(FRAGMENT) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(RSVP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(GRE) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(ESP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(AH) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(ICMPV6) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(NONE) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(DSTOPTS) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(MTP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(ENCAP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(PIM) \ + /*NETWORK_PROTOCOL_TRANSLATE_ELEM(COMP)*/ \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(SCTP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(UDPLITE) +#elif defined(_WIN32) +#define NETWORK_PROTOCOL_TRANSLATE_LIST \ + /*NETWORK_PROTOCOL_TRANSLATE_ELEM(HOPOPTS)*/ \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(ICMP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(IGMP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(GGP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(IPV4) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(ST) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(TCP) \ + /*NETWORK_PROTOCOL_TRANSLATE_ELEM(CBT)*/ \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(EGP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(IGP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(PUP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(UDP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(IDP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(RDP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(IPV6) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(ROUTING) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(FRAGMENT) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(ESP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(AH) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(ICMPV6) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(NONE) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(DSTOPTS) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(ND) \ + /*NETWORK_PROTOCOL_TRANSLATE_ELEM(ICLFXBM)*/ \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(PIM) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(PGM) \ + /*NETWORK_PROTOCOL_TRANSLATE_ELEM(L2TP)*/ \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(SCTP) +#else +#define NETWORK_PROTOCOL_TRANSLATE_LIST \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(TCP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(UDP) \ + NETWORK_PROTOCOL_TRANSLATE_ELEM(SCTP) +#endif +[[nodiscard]] Protocol TranslateProtocolFromNative(u32 protocol) { switch (protocol) { - case 0: - return Protocol::Unspecified; - case IPPROTO_TCP: - return Protocol::TCP; - case IPPROTO_UDP: - return Protocol::UDP; +#define NETWORK_PROTOCOL_TRANSLATE_ELEM(x) case IPPROTO_##x: return Protocol::x; + NETWORK_PROTOCOL_TRANSLATE_LIST +#undef NETWORK_PROTOCOL_TRANSLATE_ELEM default: UNIMPLEMENTED_MSG("Unimplemented protocol={}", protocol); - return Protocol::Unspecified; + return Protocol::IP; } } - -int TranslateProtocolToNative(Protocol protocol) { +[[nodiscard]] u32 TranslateProtocolToNative(Protocol protocol) { switch (protocol) { - case Protocol::Unspecified: - return 0; - case Protocol::TCP: - return IPPROTO_TCP; - case Protocol::UDP: - return IPPROTO_UDP; +#define NETWORK_PROTOCOL_TRANSLATE_ELEM(x) case Protocol::x: return IPPROTO_##x; + NETWORK_PROTOCOL_TRANSLATE_LIST +#undef NETWORK_PROTOCOL_TRANSLATE_ELEM default: UNIMPLEMENTED_MSG("Unimplemented protocol={}", protocol); return 0; } } +#undef NETWORK_PROTOCOL_TRANSLATE_LIST SockAddrIn TranslateToSockAddrIn(sockaddr_in input, size_t input_len) { - SockAddrIn result; - + SockAddrIn result{}; result.family = TranslateDomainFromNative(input.sin_family); - result.portno = ntohs(input.sin_port); - result.ip = TranslateIPv4(input.sin_addr); - return result; } @@ -580,15 +732,14 @@ u32 IPv4AddressToInteger(IPv4Address ip_addr) { static_cast(ip_addr[2]) << 8 | static_cast(ip_addr[3]); } -Common::Expected, GetAddrInfoError> GetAddressInfo( +std::variant, GetAddrInfoError> GetAddressInfo( const std::string& host, const std::optional& service) { addrinfo hints{}; hints.ai_family = AF_INET; // Switch only supports IPv4. addrinfo* addrinfo; - s32 gai_err = getaddrinfo(host.c_str(), service.has_value() ? service->c_str() : nullptr, - &hints, &addrinfo); + s32 gai_err = getaddrinfo(host.c_str(), service.has_value() ? service->c_str() : nullptr, &hints, &addrinfo); if (gai_err != 0) { - return Common::Unexpected(TranslateGetAddrInfoErrorFromNative(gai_err)); + return TranslateGetAddrInfoErrorFromNative(gai_err); } std::vector ret; for (auto* current = addrinfo; current; current = current->ai_next) { @@ -685,8 +836,7 @@ Errno Socket::SetSockOpt(SOCKET fd_so, int option, T value) { } Errno Socket::Initialize(Domain domain, Type type, Protocol protocol) { - fd = socket(TranslateDomainToNative(domain), TranslateTypeToNative(type), - TranslateProtocolToNative(protocol)); + fd = socket(TranslateDomainToNative(domain), TranslateTypeToNative(type), TranslateProtocolToNative(protocol)); if (fd != INVALID_SOCKET) { return Errno::SUCCESS; } diff --git a/src/core/internal_network/network.h b/src/core/internal_network/network.h index ce259c0e66..2c9ae523c5 100644 --- a/src/core/internal_network/network.h +++ b/src/core/internal_network/network.h @@ -9,6 +9,7 @@ #include #include #include +#include #include "common/common_funcs.h" #include "common/common_types.h" @@ -124,7 +125,6 @@ std::string IPv4AddressToString(IPv4Address ip_addr); u32 IPv4AddressToInteger(IPv4Address ip_addr); // named to avoid name collision with Windows macro -Common::Expected, GetAddrInfoError> GetAddressInfo( - const std::string& host, const std::optional& service); +std::variant, GetAddrInfoError> GetAddressInfo(const std::string& host, const std::optional& service); } // namespace Network diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index afa5cdbc36..3286606c5e 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp @@ -71,9 +71,10 @@ struct PatchCollection { std::array module_patcher_indices{}; }; -AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file_, - bool override_update_) - : AppLoader(std::move(file_)), override_update(override_update_), is_hbl(false) { +AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file_, bool override_update_) + : AppLoader(std::move(file_)) + , override_update(override_update_) +{ const auto file_dir = file->GetContainingDirectory(); // Title ID @@ -124,9 +125,11 @@ AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys } AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory( - FileSys::VirtualDir directory, bool override_update_, bool is_hbl_) - : AppLoader(directory->GetFile("main")), dir(std::move(directory)), - override_update(override_update_), is_hbl(is_hbl_) {} + FileSys::VirtualDir directory, bool override_update_) + : AppLoader(directory->GetFile("main")) + , dir(std::move(directory)) + , override_update(override_update_) +{} FileType AppLoader_DeconstructedRomDirectory::IdentifyType(const FileSys::VirtualFile& dir_file) { if (FileSys::IsDirectoryExeFS(dir_file->GetContainingDirectory())) { @@ -232,7 +235,7 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect ? ::Settings::values.rng_seed.GetValue() : Common::Random::Random64(0)) << 12) & 0xfff000; // Setup the process code layout - if (process.LoadFromMetadata(metadata, code_size, fastmem_base, aslr_offset, is_hbl).IsError()) { + if (process.LoadFromMetadata(system.Kernel(), metadata, code_size, fastmem_base, aslr_offset).IsError()) { return {ResultStatus::ErrorUnableToParseKernelMetadata, {}}; } diff --git a/src/core/loader/deconstructed_rom_directory.h b/src/core/loader/deconstructed_rom_directory.h index 1e9f765c9d..7283db0662 100644 --- a/src/core/loader/deconstructed_rom_directory.h +++ b/src/core/loader/deconstructed_rom_directory.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -22,13 +25,9 @@ namespace Loader { */ class AppLoader_DeconstructedRomDirectory final : public AppLoader { public: - explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile main_file, - bool override_update_ = false); - + explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile main_file, bool override_update_ = false); // Overload to accept exefs directory. Must contain 'main' and 'main.npdm' - explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualDir directory, - bool override_update_ = false, - bool is_hbl_ = false); + explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualDir directory, bool override_update_ = false); /** * Identifies whether or not the given file is a deconstructed ROM directory. @@ -63,7 +62,6 @@ private: std::string name; u64 title_id{}; bool override_update; - bool is_hbl; Modules modules; }; diff --git a/src/core/loader/kip.cpp b/src/core/loader/kip.cpp index 978ffed2b9..62bcbc9579 100644 --- a/src/core/loader/kip.cpp +++ b/src/core/loader/kip.cpp @@ -93,11 +93,11 @@ AppLoader::LoadResult AppLoader_KIP::Load(Kernel::KProcess& process, ? ::Settings::values.rng_seed.GetValue() : Common::Random::Random64(0)) << 12) & 0xfff000; // Setup the process code layout - if (process.LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), codeset.memory.size(), 0, aslr_offset, false).IsError()) { + if (process.LoadFromMetadata(system.Kernel(), FileSys::ProgramMetadata::GetDefault(), codeset.memory.size(), 0, aslr_offset).IsError()) { return {ResultStatus::ErrorNotInitialized, {}}; } const VAddr base_address = GetInteger(process.GetEntryPoint()); - process.LoadModule(std::move(codeset), base_address); + process.LoadModule(system.Kernel(), std::move(codeset), base_address); LOG_DEBUG(Loader, "loaded module {} @ {:#X}", kip->GetName(), base_address); diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index 738d805149..4ba4bde0bb 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp @@ -4,9 +4,15 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include +#include +#include +#include +#include #include #include +#include "common/alignment.h" #include "common/common_funcs.h" #include "common/common_types.h" #include "common/logging.h" @@ -23,7 +29,6 @@ #include "core/hle/kernel/k_thread.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/loader/nro.h" -#include "core/loader/nso.h" #include "core/memory.h" #ifdef HAS_NCE @@ -174,19 +179,6 @@ static bool LoadNroImpl(Core::System& system, Kernel::KProcess& process, codeset.segments[i].size = PageAlignSize(nro_header.segments[i].size); } - if (!Settings::values.program_args.GetValue().empty()) { - const auto arg_data = Settings::values.program_args.GetValue(); - codeset.DataSegment().size += NSO_ARGUMENT_DATA_ALLOCATION_SIZE; - NSOArgumentHeader args_header{ - NSO_ARGUMENT_DATA_ALLOCATION_SIZE, static_cast(arg_data.size()), {}}; - const auto end_offset = program_image.size(); - program_image.resize(static_cast(program_image.size()) + - NSO_ARGUMENT_DATA_ALLOCATION_SIZE); - std::memcpy(program_image.data() + end_offset, &args_header, sizeof(NSOArgumentHeader)); - std::memcpy(program_image.data() + end_offset + sizeof(NSOArgumentHeader), arg_data.data(), - arg_data.size()); - } - // Default .bss to NRO header bss size if MOD0 section doesn't exist u32 bss_size{PageAlignSize(nro_header.bss_size)}; @@ -203,6 +195,47 @@ static bool LoadNroImpl(Core::System& system, Kernel::KProcess& process, codeset.DataSegment().size += bss_size; program_image.resize(static_cast(program_image.size()) + bss_size); + struct ConfigEntry { + u32_le key; + u32_le flags; + u64_le value[2]; + }; + static_assert(sizeof(ConfigEntry) == 0x18); + // AArch64 encoding for svc #0x7 (ExitProcess). + constexpr u32 kSvcExitProcessInstruction = 0xD40000E1; + constexpr size_t kNumEntries = 4; // MainThreadHandle, AppletType, Argv, EndOfList + constexpr size_t kConfigTableSize = kNumEntries * sizeof(ConfigEntry); + std::string argv_string; + size_t args_offset_in_image = 0; + std::optional exit_process_offset_in_image; + const auto& program_args = Settings::values.program_args.GetValue(); + if (!program_args.empty()) { + argv_string = "homebrew "; + argv_string += program_args; + argv_string.push_back('\0'); + + const auto& code = codeset.CodeSegment(); + const size_t code_end = (std::min)(program_image.size(), code.offset + code.size); + for (size_t offset = code.offset; offset + sizeof(u32) <= code_end; offset += sizeof(u32)) { + u32 instruction{}; + std::memcpy(&instruction, program_image.data() + offset, sizeof(instruction)); + if (instruction == kSvcExitProcessInstruction) { + exit_process_offset_in_image = offset; + break; + } + } + if (!exit_process_offset_in_image) { + LOG_WARNING(Loader, + "Unable to find svcExitProcess in NRO; returning from main may fault"); + } + + const size_t entries_and_argv = + Common::AlignUp(kConfigTableSize + argv_string.size(), Core::Memory::YUZU_PAGESIZE); + + args_offset_in_image = program_image.size(); + codeset.DataSegment().size += static_cast(entries_and_argv); + program_image.resize(args_offset_in_image + entries_and_argv); + } size_t image_size = program_image.size(); #ifdef HAS_NCE @@ -247,7 +280,7 @@ static bool LoadNroImpl(Core::System& system, Kernel::KProcess& process, // Setup the process code layout if (process - .LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), image_size, fastmem_base, aslr_offset, false) + .LoadFromMetadata(system.Kernel(), FileSys::ProgramMetadata::GetDefault(), image_size, fastmem_base, aslr_offset) .IsError()) { return false; } @@ -263,7 +296,33 @@ static bool LoadNroImpl(Core::System& system, Kernel::KProcess& process, // Load codeset for current process codeset.memory = std::move(program_image); - process.LoadModule(std::move(codeset), process.GetEntryPoint()); + process.LoadModule(system.Kernel(), std::move(codeset), process.GetEntryPoint()); + if (!argv_string.empty()) { + constexpr u32 kEntryEndOfList = 0; + constexpr u32 kEntryMainThreadHandle = 1; + constexpr u32 kEntryArgv = 5; + constexpr u32 kEntryAppletType = 7; + constexpr u32 kAppletTypeApplication = 0; + + const u64 base = GetInteger(process.GetEntryPoint()); + const u64 config_addr = base + args_offset_in_image; + const u64 argv_addr = config_addr + kConfigTableSize; + + const ConfigEntry entries[kNumEntries] = { + {kEntryMainThreadHandle, 0, {0, 0}}, // Value[0] patched in Run() + {kEntryAppletType, 0, {kAppletTypeApplication, 0}}, + {kEntryArgv, 0, {0, argv_addr}}, + {kEntryEndOfList, 0, {0, 0}}, + }; + process.GetMemory().WriteBlock(Common::ProcessAddress{config_addr}, entries, sizeof(entries)); + process.GetMemory().WriteBlock(Common::ProcessAddress{argv_addr}, argv_string.data(), argv_string.size()); + constexpr size_t kMainThreadHandleValueOffset = offsetof(ConfigEntry, value); + process.SetArgPointer(Kernel::KProcessAddress{config_addr}); + if (exit_process_offset_in_image) { + process.SetArgReturnAddress(Kernel::KProcessAddress{base + *exit_process_offset_in_image}); + } + process.SetMainThreadHandleAddr(Kernel::KProcessAddress{config_addr + kMainThreadHandleValueOffset}); + } return true; } diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index 482c853542..a831f44a40 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp @@ -217,7 +217,7 @@ std::optional AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core:: } // Load codeset for current process - process.LoadModule(std::move(codeset), load_base); + process.LoadModule(system.Kernel(), std::move(codeset), load_base); return load_base + image_size; } diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp index 4333acb70c..726bb428f6 100644 --- a/src/core/loader/nsp.cpp +++ b/src/core/loader/nsp.cpp @@ -34,8 +34,7 @@ AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file_, } if (nsp->IsExtractedType()) { - secondary_loader = std::make_unique( - nsp->GetExeFS(), false, file->GetName() == "hbl.nsp"); + secondary_loader = std::make_unique(nsp->GetExeFS(), false); } else { const auto control_nca = nsp->GetNCA(nsp->GetProgramTitleID(), FileSys::ContentRecordType::Control); diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 3a9ea308a8..38e77e863f 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -58,9 +58,9 @@ struct Memory::Impl { #ifdef __ANDROID__ heap_tracker.emplace(system.DeviceMemory().buffer); - buffer = std::addressof(*heap_tracker); + host_buffer = std::addressof(*heap_tracker); #else - buffer = std::addressof(system.DeviceMemory().buffer); + host_buffer = std::addressof(system.DeviceMemory().buffer); #endif } @@ -75,8 +75,7 @@ struct Memory::Impl { Common::PageType::Memory); if (current_page_table->fastmem_arena) { - buffer->Map(GetInteger(base), GetInteger(target) - DramMemoryMap::Base, size, perms, - separate_heap); + host_buffer->Map(GetInteger(base), GetInteger(target) - DramMemoryMap::Base, size, perms, separate_heap); } } @@ -88,7 +87,7 @@ struct Memory::Impl { Common::PageType::Unmapped); if (current_page_table->fastmem_arena) { - buffer->Unmap(GetInteger(base), size, separate_heap); + host_buffer->Unmap(GetInteger(base), size, separate_heap); } } @@ -107,7 +106,7 @@ struct Memory::Impl { switch (page_type) { case Common::PageType::RasterizerCachedMemory: if (protect_bytes > 0) { - buffer->Protect(protect_begin, protect_bytes, perms); + host_buffer->Protect(protect_begin, protect_bytes, perms); protect_bytes = 0; } break; @@ -119,7 +118,7 @@ struct Memory::Impl { } if (protect_bytes > 0) { - buffer->Protect(protect_begin, protect_bytes, perms); + host_buffer->Protect(protect_begin, protect_bytes, perms); } } @@ -233,225 +232,138 @@ struct Memory::Impl { return string; } - bool WalkBlock(const Common::ProcessAddress addr, const std::size_t size, auto on_unmapped, - auto on_memory, auto on_rasterizer, auto increment) { - const auto& page_table = *current_page_table; + template + inline bool WalkBlock(const Common::ProcessAddress addr, const std::size_t size, F1&& on_unmapped, F2&& on_memory, F3&& on_rasterizer) { + std::size_t offset = 0; + if (!AddressSpaceContains(*current_page_table, addr, size)) { + on_unmapped(offset, size, addr); + return false; + } std::size_t remaining_size = size; std::size_t page_index = addr >> YUZU_PAGEBITS; std::size_t page_offset = addr & YUZU_PAGEMASK; bool user_accessible = true; - - if (!AddressSpaceContains(page_table, addr, size)) [[unlikely]] { - on_unmapped(size, addr); - return false; - } - - while (remaining_size) { - const std::size_t copy_amount = - (std::min)(static_cast(YUZU_PAGESIZE) - page_offset, remaining_size); - const auto current_vaddr = - static_cast((page_index << YUZU_PAGEBITS) + page_offset); - - const auto [pointer, type] = page_table.entries[page_index].ptr.PointerType(); + while (remaining_size != 0) { + const std::size_t copy_amount = (std::min)(std::size_t(YUZU_PAGESIZE) - page_offset, remaining_size); + const auto current_vaddr = u64((page_index << YUZU_PAGEBITS) + page_offset); + const auto [pointer, type] = current_page_table->entries[page_index].ptr.PointerType(); switch (type) { case Common::PageType::Unmapped: { user_accessible = false; - on_unmapped(copy_amount, current_vaddr); + on_unmapped(offset, copy_amount, current_vaddr); break; } case Common::PageType::Memory: { - u8* mem_ptr = - reinterpret_cast(pointer + page_offset + (page_index << YUZU_PAGEBITS)); - on_memory(copy_amount, mem_ptr); + u8* mem_ptr = reinterpret_cast(pointer + page_offset + (page_index << YUZU_PAGEBITS)); + on_memory(offset, copy_amount, mem_ptr); break; } case Common::PageType::DebugMemory: { - u8* const mem_ptr{GetPointerFromDebugMemory(current_vaddr)}; - on_memory(copy_amount, mem_ptr); + u8* const mem_ptr = GetPointerFromDebugMemory(current_vaddr); + on_memory(offset, copy_amount, mem_ptr); break; } case Common::PageType::RasterizerCachedMemory: { - u8* const host_ptr{GetPointerFromRasterizerCachedMemory(current_vaddr)}; - on_rasterizer(current_vaddr, copy_amount, host_ptr); + u8* const host_ptr = GetPointerFromRasterizerCachedMemory(current_vaddr); + on_rasterizer(current_vaddr, offset, copy_amount, host_ptr); break; } default: UNREACHABLE(); } - page_index++; page_offset = 0; - increment(copy_amount); + offset += copy_amount; remaining_size -= copy_amount; } - return user_accessible; } - template - bool ReadBlockImpl(const Common::ProcessAddress src_addr, void* dest_buffer, - const std::size_t size) { - return WalkBlock( - src_addr, size, - [src_addr, size, &dest_buffer](const std::size_t copy_amount, - const Common::ProcessAddress current_vaddr) { - LOG_ERROR(HW_Memory, - "Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", - GetInteger(current_vaddr), GetInteger(src_addr), size); - std::memset(dest_buffer, 0, copy_amount); - }, - [&](const std::size_t copy_amount, const u8* const src_ptr) { - std::memcpy(dest_buffer, src_ptr, copy_amount); - }, - [&](const Common::ProcessAddress current_vaddr, const std::size_t copy_amount, - const u8* const host_ptr) { - if constexpr (!UNSAFE) { - HandleRasterizerDownload(GetInteger(current_vaddr), copy_amount); - } - std::memcpy(dest_buffer, host_ptr, copy_amount); - }, - [&](const std::size_t copy_amount) { - dest_buffer = static_cast(dest_buffer) + copy_amount; - }); - } - - bool ReadBlock(const Common::ProcessAddress src_addr, void* dest_buffer, - const std::size_t size) { - // TODO: If you want a proper multithreaded implementation (w/o cache coherency fights) - // use TBB or something that splits the job properly - return ReadBlockImpl(src_addr, dest_buffer, size); - } - - bool ReadBlockUnsafe(const Common::ProcessAddress src_addr, void* dest_buffer, - const std::size_t size) { - return ReadBlockImpl(src_addr, dest_buffer, size); - } - - const u8* GetSpan(const VAddr src_addr, const std::size_t size) const { - if (current_page_table->entries[src_addr >> YUZU_PAGEBITS].block == - current_page_table->entries[(src_addr + size) >> YUZU_PAGEBITS].block) { - return GetPointerSilent(src_addr); - } - return nullptr; - } - - u8* GetSpan(const VAddr src_addr, const std::size_t size) { - if (current_page_table->entries[src_addr >> YUZU_PAGEBITS].block == - current_page_table->entries[(src_addr + size) >> YUZU_PAGEBITS].block) { - return GetPointerSilent(src_addr); - } - return nullptr; - } - - template - bool WriteBlockImpl(const Common::ProcessAddress dest_addr, const void* src_buffer, - const std::size_t size) { - return WalkBlock( - dest_addr, size, - [dest_addr, size](const std::size_t copy_amount, - const Common::ProcessAddress current_vaddr) { - LOG_ERROR(HW_Memory, - "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", - GetInteger(current_vaddr), GetInteger(dest_addr), size); - }, - [&](const std::size_t copy_amount, u8* const dest_ptr) { - std::memcpy(dest_ptr, src_buffer, copy_amount); - }, - [&](const Common::ProcessAddress current_vaddr, const std::size_t copy_amount, - u8* const host_ptr) { - if constexpr (!UNSAFE) { - HandleRasterizerWrite(GetInteger(current_vaddr), copy_amount); - } - std::memcpy(host_ptr, src_buffer, copy_amount); - }, - [&](const std::size_t copy_amount) { - src_buffer = static_cast(src_buffer) + copy_amount; - }); - } - - bool WriteBlock(const Common::ProcessAddress dest_addr, const void* src_buffer, - const std::size_t size) { - // TODO: If you want a proper multithreaded implementation (w/o cache coherency fights) - // use TBB or something that splits the job properly - return WriteBlockImpl(dest_addr, src_buffer, size); - } - - bool WriteBlockUnsafe(const Common::ProcessAddress dest_addr, const void* src_buffer, - const std::size_t size) { - return WriteBlockImpl(dest_addr, src_buffer, size); - } - - bool ZeroBlock(const Common::ProcessAddress dest_addr, const std::size_t size) { - return WalkBlock( - dest_addr, size, - [dest_addr, size](const std::size_t copy_amount, - const Common::ProcessAddress current_vaddr) { - LOG_ERROR(HW_Memory, - "Unmapped ZeroBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", - GetInteger(current_vaddr), GetInteger(dest_addr), size); - }, - [](const std::size_t copy_amount, u8* const dest_ptr) { - std::memset(dest_ptr, 0, copy_amount); - }, - [&](const Common::ProcessAddress current_vaddr, const std::size_t copy_amount, - u8* const host_ptr) { - HandleRasterizerWrite(GetInteger(current_vaddr), copy_amount); - std::memset(host_ptr, 0, copy_amount); - }, - [](const std::size_t copy_amount) {}); - } - - bool CopyBlock(Common::ProcessAddress dest_addr, Common::ProcessAddress src_addr, - const std::size_t size) { - return WalkBlock( - dest_addr, size, - [&](const std::size_t copy_amount, const Common::ProcessAddress current_vaddr) { - LOG_ERROR(HW_Memory, - "Unmapped CopyBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", - GetInteger(current_vaddr), GetInteger(src_addr), size); - ZeroBlock(dest_addr, copy_amount); - }, - [&](const std::size_t copy_amount, const u8* const src_ptr) { - WriteBlockImpl(dest_addr, src_ptr, copy_amount); - }, - [&](const Common::ProcessAddress current_vaddr, const std::size_t copy_amount, - u8* const host_ptr) { + bool ReadBlockImpl(const Common::ProcessAddress addr, void* buffer, const std::size_t size, bool unsafe) { + return WalkBlock(addr, size, + [addr, size, &buffer](const std::size_t offset, const std::size_t copy_amount, const Common::ProcessAddress current_vaddr) { + LOG_ERROR(HW_Memory, "Unmapped @ {:#016X} (start address = {:#016X}, size = {})", GetInteger(current_vaddr), GetInteger(addr), size); + std::memset(reinterpret_cast(buffer) + offset, 0, copy_amount); + }, + [&](const std::size_t offset, const std::size_t copy_amount, const u8* const ptr) { + std::memcpy(reinterpret_cast(buffer) + offset, ptr, copy_amount); + }, + [&](const Common::ProcessAddress current_vaddr, const std::size_t offset, const std::size_t copy_amount, const u8* const ptr) { + if (!unsafe) { HandleRasterizerDownload(GetInteger(current_vaddr), copy_amount); - WriteBlockImpl(dest_addr, host_ptr, copy_amount); - }, - [&](const std::size_t copy_amount) { - dest_addr += copy_amount; - src_addr += copy_amount; - }); + } + std::memcpy(reinterpret_cast(buffer) + offset, ptr, copy_amount); + }); + } + + [[nodiscard]] inline const u8* GetSpan(const VAddr addr, const std::size_t size) const noexcept { + return (current_page_table->entries[addr >> YUZU_PAGEBITS].block == current_page_table->entries[(addr + size) >> YUZU_PAGEBITS].block) ? GetPointerSilent(addr) : nullptr; + } + [[nodiscard]] inline u8* GetSpan(const VAddr addr, const std::size_t size) noexcept { + return (current_page_table->entries[addr >> YUZU_PAGEBITS].block == current_page_table->entries[(addr + size) >> YUZU_PAGEBITS].block) ? GetPointerSilent(addr) : nullptr; + } + + bool WriteBlockImpl(const Common::ProcessAddress addr, const void* buffer, const std::size_t size, bool unsafe) { + return WalkBlock(addr, size, + [addr, size](const std::size_t offset, const std::size_t copy_amount, const Common::ProcessAddress current_vaddr) { + LOG_ERROR(HW_Memory, "Unmapped @ 0x{:016X} (start address = 0x{:016X}, size = {})", GetInteger(current_vaddr), GetInteger(addr), size); + }, + [&](const std::size_t offset, const std::size_t copy_amount, u8* const ptr) { + std::memcpy(ptr, reinterpret_cast(buffer) + offset, copy_amount); + }, + [&](const Common::ProcessAddress current_vaddr, const std::size_t offset, const std::size_t copy_amount, u8* const ptr) { + if (!unsafe) { + HandleRasterizerWrite(GetInteger(current_vaddr), copy_amount); + } + std::memcpy(ptr, reinterpret_cast(buffer) + offset, copy_amount); + }); + } + + bool ZeroBlock(const Common::ProcessAddress addr, const std::size_t size) { + return WalkBlock(addr, size, + [addr, size](const std::size_t offset, const std::size_t copy_amount, const Common::ProcessAddress current_vaddr) { + LOG_ERROR(HW_Memory, "Unmapped @ {:#016X} (start address = {:#016X}, size = {})", GetInteger(current_vaddr), GetInteger(addr), size); + }, + [=](const std::size_t offset, const std::size_t copy_amount, u8* const ptr) { + std::memset(ptr, 0, copy_amount); + }, + [&](const Common::ProcessAddress current_vaddr, const std::size_t offset, const std::size_t copy_amount, u8* const ptr) { + HandleRasterizerWrite(GetInteger(current_vaddr), copy_amount); + std::memset(ptr, 0, copy_amount); + }); + } + + bool CopyBlock(Common::ProcessAddress dest_addr, Common::ProcessAddress src_addr, const std::size_t size) { + return WalkBlock(dest_addr, size, + [&](const std::size_t offset, const std::size_t copy_amount, const Common::ProcessAddress current_vaddr) { + LOG_ERROR(HW_Memory, "Unmapped @ {:#016X} (start address = {:#016X}, size = {})", GetInteger(current_vaddr), GetInteger(src_addr), size); + ZeroBlock(dest_addr + offset, copy_amount); + }, + [&](const std::size_t offset, const std::size_t copy_amount, const u8* const ptr) { + WriteBlockImpl(dest_addr + offset, ptr, copy_amount, false); + }, + [&](const Common::ProcessAddress current_vaddr, const std::size_t offset, const std::size_t copy_amount, u8* const ptr) { + HandleRasterizerDownload(GetInteger(current_vaddr), copy_amount); + WriteBlockImpl(dest_addr + offset, ptr, copy_amount, false); + }); } template - Result PerformCacheOperation(Common::ProcessAddress dest_addr, std::size_t size, - Callback&& cb) { - class InvalidMemoryException : public std::exception {}; - - try { - WalkBlock( - dest_addr, size, - [&](const std::size_t block_size, const Common::ProcessAddress current_vaddr) { - LOG_ERROR(HW_Memory, "Unmapped cache maintenance @ {:#018X}", - GetInteger(current_vaddr)); - throw InvalidMemoryException(); - }, - [&](const std::size_t block_size, u8* const host_ptr) {}, - [&](const Common::ProcessAddress current_vaddr, const std::size_t block_size, - u8* const host_ptr) { cb(current_vaddr, block_size); }, - [](const std::size_t block_size) {}); - } catch (InvalidMemoryException&) { - return Kernel::ResultInvalidCurrentMemory; - } - - return ResultSuccess; + Result PerformCacheOperation(Common::ProcessAddress dest_addr, std::size_t size, Callback&& cb) { + bool had_unmapped = false; + WalkBlock(dest_addr, size, + [&](const std::size_t offset, const std::size_t copy_amount, const Common::ProcessAddress current_vaddr) { + LOG_ERROR(HW_Memory, "Unmapped @ {:#018X}", GetInteger(current_vaddr)); + had_unmapped = true; + }, + [](const std::size_t offset, const std::size_t copy_amount, u8* const host_ptr) {}, + [&](const Common::ProcessAddress current_vaddr, const std::size_t offset, const std::size_t copy_amount, u8* const host_ptr) { cb(current_vaddr, offset, copy_amount); } + ); + return had_unmapped ? Kernel::ResultInvalidCurrentMemory : ResultSuccess; } Result InvalidateDataCache(Common::ProcessAddress dest_addr, std::size_t size) { - auto on_rasterizer = [&](const Common::ProcessAddress current_vaddr, - const std::size_t block_size) { + auto on_rasterizer = [&](const Common::ProcessAddress current_vaddr, const std::size_t offset, const std::size_t block_size) { // dc ivac: Invalidate to point of coherency // GPU flush -> CPU invalidate HandleRasterizerDownload(GetInteger(current_vaddr), block_size); @@ -460,8 +372,7 @@ struct Memory::Impl { } Result StoreDataCache(Common::ProcessAddress dest_addr, std::size_t size) { - auto on_rasterizer = [&](const Common::ProcessAddress current_vaddr, - const std::size_t block_size) { + auto on_rasterizer = [&](const Common::ProcessAddress current_vaddr, const std::size_t offset, const std::size_t block_size) { // dc cvac: Store to point of coherency // CPU flush -> GPU invalidate HandleRasterizerWrite(GetInteger(current_vaddr), block_size); @@ -470,8 +381,7 @@ struct Memory::Impl { } Result FlushDataCache(Common::ProcessAddress dest_addr, std::size_t size) { - auto on_rasterizer = [&](const Common::ProcessAddress current_vaddr, - const std::size_t block_size) { + auto on_rasterizer = [&](const Common::ProcessAddress current_vaddr, const std::size_t offset, const std::size_t block_size) { // dc civac: Store to point of coherency, and invalidate from cache // CPU flush -> GPU invalidate HandleRasterizerWrite(GetInteger(current_vaddr), block_size); @@ -487,7 +397,7 @@ struct Memory::Impl { if (current_page_table->fastmem_arena) { const auto perm{debug ? Common::MemoryPermission{} : Common::MemoryPermission::ReadWrite}; - buffer->Protect(vaddr, size, perm); + host_buffer->Protect(vaddr, size, perm); } // Iterate over a contiguous CPU address space, marking/unmarking the region. @@ -547,7 +457,7 @@ struct Memory::Impl { if (!cached) { perm |= Common::MemoryPermission::Write; } - buffer->Protect(vaddr, size, perm); + host_buffer->Protect(vaddr, size, perm); } // Iterate over a contiguous CPU address space, which corresponds to the specified GPU @@ -859,9 +769,9 @@ struct Memory::Impl { std::mutex sys_core_guard; #ifdef __ANDROID__ std::optional heap_tracker; - Common::HeapTracker* buffer{}; + Common::HeapTracker* host_buffer{}; #else - Common::HostMemory* buffer{}; + Common::HostMemory* host_buffer{}; #endif }; @@ -989,12 +899,12 @@ std::string Memory::ReadCString(Common::ProcessAddress vaddr, std::size_t max_le bool Memory::ReadBlock(const Common::ProcessAddress src_addr, void* dest_buffer, const std::size_t size) { - return impl->ReadBlock(src_addr, dest_buffer, size); + return impl->ReadBlockImpl(src_addr, dest_buffer, size, false); } bool Memory::ReadBlockUnsafe(const Common::ProcessAddress src_addr, void* dest_buffer, const std::size_t size) { - return impl->ReadBlockUnsafe(src_addr, dest_buffer, size); + return impl->ReadBlockImpl(src_addr, dest_buffer, size, true); } const u8* Memory::GetSpan(const VAddr src_addr, const std::size_t size) const { @@ -1007,12 +917,12 @@ u8* Memory::GetSpan(const VAddr src_addr, const std::size_t size) { bool Memory::WriteBlock(const Common::ProcessAddress dest_addr, const void* src_buffer, const std::size_t size) { - return impl->WriteBlock(dest_addr, src_buffer, size); + return impl->WriteBlockImpl(dest_addr, src_buffer, size, false); } bool Memory::WriteBlockUnsafe(const Common::ProcessAddress dest_addr, const void* src_buffer, const std::size_t size) { - return impl->WriteBlockUnsafe(dest_addr, src_buffer, size); + return impl->WriteBlockImpl(dest_addr, src_buffer, size, true); } bool Memory::CopyBlock(Common::ProcessAddress dest_addr, Common::ProcessAddress src_addr, @@ -1066,7 +976,7 @@ bool Memory::InvalidateNCE(Common::ProcessAddress vaddr, size_t size) { #ifdef __ANDROID__ if (!rasterizer && mapped) { - impl->buffer->DeferredMapSeparateHeap(GetInteger(vaddr)); + impl->host_buffer->DeferredMapSeparateHeap(GetInteger(vaddr)); } #endif @@ -1075,7 +985,7 @@ bool Memory::InvalidateNCE(Common::ProcessAddress vaddr, size_t size) { bool Memory::InvalidateSeparateHeap(void* fault_address) { #ifdef __ANDROID__ - return impl->buffer->DeferredMapSeparateHeap(static_cast(fault_address)); + return impl->host_buffer->DeferredMapSeparateHeap(static_cast(fault_address)); #else return false; #endif diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp index dcfd23644f..8e23a54a0d 100644 --- a/src/core/memory/cheat_engine.cpp +++ b/src/core/memory/cheat_engine.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project @@ -91,17 +91,15 @@ u64 StandardVmCallbacks::HidKeysDown() { } void StandardVmCallbacks::PauseProcess() { - if (system.ApplicationProcess()->IsSuspended()) { - return; + if (!system.ApplicationProcess()->IsSuspended()) { + system.ApplicationProcess()->SetActivity(system.Kernel(), Kernel::Svc::ProcessActivity::Paused); } - system.ApplicationProcess()->SetActivity(Kernel::Svc::ProcessActivity::Paused); } void StandardVmCallbacks::ResumeProcess() { - if (!system.ApplicationProcess()->IsSuspended()) { - return; + if (system.ApplicationProcess()->IsSuspended()) { + system.ApplicationProcess()->SetActivity(system.Kernel(), Kernel::Svc::ProcessActivity::Runnable); } - system.ApplicationProcess()->SetActivity(Kernel::Svc::ProcessActivity::Runnable); } void StandardVmCallbacks::DebugLog(u8 id, u64 value) { diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp index 1723636811..2a5212fed8 100644 --- a/src/core/reporter.cpp +++ b/src/core/reporter.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project @@ -20,6 +20,7 @@ #include "common/settings.h" #include "core/arm/arm_interface.h" #include "core/core.h" +#include "core/hle/ipc.h" #include "core/hle/kernel/k_page_table.h" #include "core/hle/kernel/k_process.h" #include "core/hle/result.h" @@ -124,8 +125,7 @@ json GetFullDataAuto(const std::string& timestamp, u64 title_id, Core::System& s } template -json GetHLEBufferDescriptorData(const std::vector& buffer, - Core::Memory::Memory& memory) { +json GetHLEBufferDescriptorData(const boost::container::static_vector& buffer, Core::Memory::Memory& memory) { auto buffer_out = json::array(); for (const auto& desc : buffer) { auto entry = json{ diff --git a/src/core/tools/renderdoc.cpp b/src/core/tools/renderdoc.cpp index 4d91956902..6a6df23432 100644 --- a/src/core/tools/renderdoc.cpp +++ b/src/core/tools/renderdoc.cpp @@ -31,7 +31,7 @@ RenderdocAPI::RenderdocAPI() { #elif defined(__HAIKU__) // no rtld on haiku #else -#ifdef ANDROID +#ifdef __ANDROID__ static constexpr const char RENDERDOC_LIB[] = "libVkLayer_GLES_RenderDoc.so"; #else static constexpr const char RENDERDOC_LIB[] = "librenderdoc.so"; diff --git a/src/dedicated_room/CMakeLists.txt b/src/dedicated_room/CMakeLists.txt index 36c8af5e34..8ca138473f 100644 --- a/src/dedicated_room/CMakeLists.txt +++ b/src/dedicated_room/CMakeLists.txt @@ -7,8 +7,7 @@ add_library(yuzu-room STATIC EXCLUDE_FROM_ALL yuzu_room.cpp yuzu_room.h - yuzu_room.rc -) + yuzu_room.rc) target_link_libraries(yuzu-room PRIVATE common network) if (ENABLE_WEB_SERVICE) diff --git a/src/dynarmic/CMakeLists.txt b/src/dynarmic/CMakeLists.txt index 4c4bf86d3e..46b0b618e8 100644 --- a/src/dynarmic/CMakeLists.txt +++ b/src/dynarmic/CMakeLists.txt @@ -130,6 +130,10 @@ if ("riscv64" IN_LIST ARCHITECTURE) find_package(biscuit 0.9.1 REQUIRED) endif() +if ("loongarch64" IN_LIST ARCHITECTURE) + find_package(lagoon REQUIRED) +endif() + if ("x86_64" IN_LIST ARCHITECTURE) find_package(xbyak 7 CONFIG) endif() diff --git a/src/dynarmic/src/dynarmic/CMakeLists.txt b/src/dynarmic/src/dynarmic/CMakeLists.txt index c51a660c03..98fcea6333 100644 --- a/src/dynarmic/src/dynarmic/CMakeLists.txt +++ b/src/dynarmic/src/dynarmic/CMakeLists.txt @@ -295,6 +295,34 @@ if ("riscv64" IN_LIST ARCHITECTURE) message(WARNING "TODO: Incomplete frontend for this host architecture") endif() +if ("loongarch64" IN_LIST ARCHITECTURE) + target_link_libraries(dynarmic PRIVATE lagoon::lagoon) + + target_sources(dynarmic PRIVATE + backend/loongarch64/abi.h + backend/loongarch64/a32_jitstate.cpp + backend/loongarch64/a32_jitstate.h + backend/loongarch64/code_block.h + backend/loongarch64/emit_context.h + backend/loongarch64/emit_loongarch64.cpp + backend/loongarch64/emit_loongarch64.h + backend/loongarch64/emit_loongarch64_a32.cpp + backend/loongarch64/emit_loongarch64_data_processing.cpp + backend/loongarch64/a32_address_space.cpp + backend/loongarch64/a32_address_space.h + backend/loongarch64/a32_interface.cpp + backend/loongarch64/a64_interface.cpp + backend/loongarch64/exclusive_monitor.cpp + backend/loongarch64/reg_alloc.cpp + backend/loongarch64/reg_alloc.h + backend/loongarch64/stack_layout.h + backend/loongarch64/lagoon_cpp.h + + common/spin_lock_loongarch64.cpp + ) + message(WARNING "TODO: Incomplete frontend for this host architecture") +endif() + if (WIN32) target_sources(dynarmic PRIVATE backend/exception_handler_windows.cpp) elseif (APPLE) diff --git a/src/dynarmic/src/dynarmic/backend/arm64/emit_arm64_memory.h b/src/dynarmic/src/dynarmic/backend/arm64/emit_arm64_memory.h index 25608dbfe3..3084f3df4d 100644 --- a/src/dynarmic/src/dynarmic/backend/arm64/emit_arm64_memory.h +++ b/src/dynarmic/src/dynarmic/backend/arm64/emit_arm64_memory.h @@ -6,6 +6,7 @@ * SPDX-License-Identifier: 0BSD */ +#pragma once #include namespace oaknut { diff --git a/src/dynarmic/src/dynarmic/backend/arm64/fastmem.h b/src/dynarmic/src/dynarmic/backend/arm64/fastmem.h index 2eae740f20..83ce16d753 100644 --- a/src/dynarmic/src/dynarmic/backend/arm64/fastmem.h +++ b/src/dynarmic/src/dynarmic/backend/arm64/fastmem.h @@ -24,7 +24,7 @@ using DoNotFastmemMarker = std::tuple; constexpr std::size_t xmrx(std::size_t x) noexcept { x ^= x >> 32; x *= 0xff51afd7ed558ccd; - x ^= mcl::bit::rotate_right(x, 47) ^ mcl::bit::rotate_right(x, 23); + x ^= std::rotr(x, 47) ^ std::rotr(x, 23); return x; } diff --git a/src/dynarmic/src/dynarmic/backend/exception_handler.h b/src/dynarmic/src/dynarmic/backend/exception_handler.h index b62bd7ab71..ce6abef5ce 100644 --- a/src/dynarmic/src/dynarmic/backend/exception_handler.h +++ b/src/dynarmic/src/dynarmic/backend/exception_handler.h @@ -26,6 +26,10 @@ class CodeBlock; namespace Dynarmic::Backend::RV64 { class CodeBlock; } // namespace Dynarmic::Backend::RV64 +#elif defined(ARCHITECTURE_loongarch64) +namespace Dynarmic::Backend::LoongArch64 { +class CodeBlock; +} // namespace Dynarmic::Backend::LoongArch64 #else # error "Invalid architecture" #endif @@ -45,6 +49,10 @@ struct FakeCall { struct FakeCall { u64 call_sepc; }; +#elif defined(ARCHITECTURE_loongarch64) +struct FakeCall { + u64 call_pc; +}; #else # error "Invalid architecture" #endif @@ -60,6 +68,8 @@ public: void Register(oaknut::CodeBlock& mem, std::size_t mem_size); #elif defined(ARCHITECTURE_riscv64) void Register(RV64::CodeBlock& mem, std::size_t mem_size); +#elif defined(ARCHITECTURE_loongarch64) + void Register(LoongArch64::CodeBlock& mem, std::size_t mem_size); #else # error "Invalid architecture" #endif diff --git a/src/dynarmic/src/dynarmic/backend/exception_handler_generic.cpp b/src/dynarmic/src/dynarmic/backend/exception_handler_generic.cpp index 23dc294511..f19fe98a6d 100644 --- a/src/dynarmic/src/dynarmic/backend/exception_handler_generic.cpp +++ b/src/dynarmic/src/dynarmic/backend/exception_handler_generic.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later /* This file is part of the dynarmic project. @@ -28,6 +28,10 @@ void ExceptionHandler::Register(oaknut::CodeBlock&, std::size_t) { void ExceptionHandler::Register(RV64::CodeBlock&, std::size_t) { // Do nothing } +#elif defined(ARCHITECTURE_loongarch64) +void ExceptionHandler::Register(LoongArch64::CodeBlock&, std::size_t) { + // Do nothing +} #else # error "Invalid architecture" #endif diff --git a/src/dynarmic/src/dynarmic/backend/exception_handler_posix.cpp b/src/dynarmic/src/dynarmic/backend/exception_handler_posix.cpp index 50d552aa0e..c586cf2fb1 100644 --- a/src/dynarmic/src/dynarmic/backend/exception_handler_posix.cpp +++ b/src/dynarmic/src/dynarmic/backend/exception_handler_posix.cpp @@ -6,20 +6,25 @@ * SPDX-License-Identifier: 0BSD */ +#include + +#include +#include #include #include -#include #include #include -#include #include -#include -#include +#include + #include -#include "dynarmic/backend/exception_handler.h" +#include +#include + #include "common/assert.h" -#include "dynarmic/common/context.h" #include "common/common_types.h" +#include "dynarmic/backend/exception_handler.h" +#include "dynarmic/common/context.h" #if defined(ARCHITECTURE_x86_64) # include "dynarmic/backend/x64/block_of_code.h" #elif defined(ARCHITECTURE_arm64) @@ -27,6 +32,8 @@ # include "dynarmic/backend/arm64/abi.h" #elif defined(ARCHITECTURE_riscv64) # include "dynarmic/backend/riscv64/code_block.h" +#elif defined(ARCHITECTURE_loongarch64) +# include "dynarmic/backend/loongarch64/code_block.h" #else # error "Invalid architecture" #endif @@ -121,15 +128,15 @@ void SigHandler::SigAction(int sig, siginfo_t* info, void* raw_context) { #if defined(ARCHITECTURE_x86_64) { std::shared_lock guard(sig_handler->code_block_infos_mutex); - if (auto const iter = sig_handler->FindCodeBlockInfo(CTX_RIP); iter != sig_handler->code_block_infos.end()) { - FakeCall fc = iter->second.cb(CTX_RIP); - CTX_RSP -= sizeof(u64); - *std::bit_cast(CTX_RSP) = fc.ret_rip; - CTX_RIP = fc.call_rip; + if (auto const iter = sig_handler->FindCodeBlockInfo(CTX_PC); iter != sig_handler->code_block_infos.end()) { + FakeCall fc = iter->second.cb(CTX_PC); + CTX_SP -= sizeof(u64); + *std::bit_cast(CTX_SP) = fc.ret_rip; + CTX_PC = fc.call_rip; return; } } - fmt::print(stderr, "Unhandled {} at rip {:#018x}\n", sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", CTX_RIP); + fmt::print(stderr, "Unhandled {} at rip {:#018x}\n", sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", CTX_PC); #elif defined(ARCHITECTURE_arm64) { std::shared_lock guard(sig_handler->code_block_infos_mutex); @@ -150,6 +157,16 @@ void SigHandler::SigAction(int sig, siginfo_t* info, void* raw_context) { } } fmt::print(stderr, "Unhandled {} at pc {:#018x}\n", sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", CTX_SEPC); +#elif defined(ARCHITECTURE_loongarch64) + { + std::shared_lock guard(sig_handler->code_block_infos_mutex); + if (const auto iter = sig_handler->FindCodeBlockInfo(CTX_PC); iter != sig_handler->code_block_infos.end()) { + FakeCall fc = iter->second.cb(CTX_PC); + CTX_PC = fc.call_pc; + return; + } + } + fmt::print(stderr, "Unhandled {} at pc {:#018x}\n", sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", CTX_PC); #else # error "Invalid architecture" #endif @@ -209,6 +226,10 @@ void ExceptionHandler::Register(oaknut::CodeBlock& mem, std::size_t size) { void ExceptionHandler::Register(RV64::CodeBlock& mem, std::size_t size) { impl = std::make_unique(std::bit_cast(mem.ptr()), size); } +#elif defined(ARCHITECTURE_loongarch64) +void ExceptionHandler::Register(LoongArch64::CodeBlock& mem, std::size_t size) { + impl = std::make_unique(std::bit_cast(mem.ptr()), size); +} #else # error "Invalid architecture" #endif diff --git a/src/dynarmic/src/dynarmic/backend/loongarch64/a32_address_space.cpp b/src/dynarmic/src/dynarmic/backend/loongarch64/a32_address_space.cpp new file mode 100644 index 0000000000..f1a7d55d2c --- /dev/null +++ b/src/dynarmic/src/dynarmic/backend/loongarch64/a32_address_space.cpp @@ -0,0 +1,123 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "dynarmic/backend/loongarch64/a32_address_space.h" + +#include "common/assert.h" + +#include "dynarmic/backend/loongarch64/a32_jitstate.h" +#include "dynarmic/backend/loongarch64/abi.h" +#include "dynarmic/backend/loongarch64/emit_loongarch64.h" +#include "dynarmic/frontend/A32/a32_location_descriptor.h" +#include "dynarmic/frontend/A32/translate/a32_translate.h" +#include "dynarmic/ir/opt_passes.h" + +namespace Dynarmic::Backend::LoongArch64 { + +A32AddressSpace::A32AddressSpace(const A32::UserConfig& conf) + : conf(conf) + , cb(conf.code_cache_size) { + EmitPrelude(); +} + +void A32AddressSpace::GenerateIR(IR::Block& ir_block, IR::LocationDescriptor descriptor) const { + A32::Translate(ir_block, A32::LocationDescriptor{descriptor}, conf.callbacks, {conf.arch_version, conf.define_unpredictable_behaviour, conf.hook_hint_instructions}); + Optimization::Optimize(ir_block, conf, {.sha256 = true}); +} + +CodePtr A32AddressSpace::Get(IR::LocationDescriptor descriptor) { + if (const auto iter = block_entries.find(descriptor.Value()); iter != block_entries.end()) { + return iter->second; + } + return nullptr; +} + +CodePtr A32AddressSpace::GetOrEmit(IR::LocationDescriptor descriptor) { + if (CodePtr block_entry = Get(descriptor)) { + return block_entry; + } + + IR::Block ir_block{descriptor}; + GenerateIR(ir_block, descriptor); + const EmittedBlockInfo block_info = Emit(std::move(ir_block)); + + block_infos.insert_or_assign(descriptor.Value(), block_info); + block_entries.insert_or_assign(descriptor.Value(), block_info.entry_point); + return block_info.entry_point; +} + +void A32AddressSpace::ClearCache() { + block_entries.clear(); + block_infos.clear(); + SetCursorPtr(prelude_info.end_of_prelude); +} + +void A32AddressSpace::EmitPrelude() { + prelude_info.run_code = GetCursorPtr(); + + // Save all GPRs except sp (r3) and tp (r2) + la_addi_d(&cb.as, LA_SP, LA_SP, -64 * 8); + for (u32 i = 1; i < 32; i++) { + if (i == LA_SP || i == LA_TP) + continue; + la_st_d(&cb.as, static_cast(i), LA_SP, static_cast(i * 8)); + } + + // Set up reserved registers and jump to block entry + la_move(&cb.as, Xstate, LA_A1); // Xstate = state ptr + la_move(&cb.as, Xhalt, LA_A2); // Xhalt = halt reason ptr + la_jr(&cb.as, LA_A0); // jump to block_entry + + prelude_info.return_from_run_code = GetCursorPtr(); + + // Restore all GPRs except sp and tp + for (u32 i = 1; i < 32; i++) { + if (i == LA_SP || i == LA_TP) + continue; + la_ld_d(&cb.as, static_cast(i), LA_SP, static_cast(i * 8)); + } + la_addi_d(&cb.as, LA_SP, LA_SP, 64 * 8); + la_ret(&cb.as); + + prelude_info.end_of_prelude = GetCursorPtr(); +} + +void A32AddressSpace::SetCursorPtr(CodePtr ptr) { + cb.as.cursor = reinterpret_cast(ptr); +} + +size_t A32AddressSpace::GetRemainingSize() { + return la_get_remaining_buffer_size(&cb.as); +} + +EmittedBlockInfo A32AddressSpace::Emit(IR::Block block) { + if (GetRemainingSize() < 1024 * 1024) { + ClearCache(); + } + + EmitConfig emit_conf{}; + EmittedBlockInfo block_info = EmitLoongArch64(cb.as, std::move(block), emit_conf); + Link(block_info); + + return block_info; +} + +void A32AddressSpace::Link(EmittedBlockInfo& block_info) { + for (const auto& reloc : block_info.relocations) { + uint8_t* patch_location = reinterpret_cast(block_info.entry_point) + reloc.code_offset; + + switch (reloc.target) { + case LinkTarget::ReturnFromRunCode: { + std::ptrdiff_t off = reinterpret_cast(prelude_info.return_from_run_code) - patch_location; + lagoon_assembler_t patch_as; + la_init_assembler(&patch_as, patch_location, 4); + la_b(&patch_as, static_cast(off)); + break; + } + default: + ASSERT(false && "Invalid relocation target"); + } + } +} + +} // namespace Dynarmic::Backend::LoongArch64 diff --git a/src/dynarmic/src/dynarmic/backend/loongarch64/a32_address_space.h b/src/dynarmic/src/dynarmic/backend/loongarch64/a32_address_space.h new file mode 100644 index 0000000000..55c4d8c5f3 --- /dev/null +++ b/src/dynarmic/src/dynarmic/backend/loongarch64/a32_address_space.h @@ -0,0 +1,68 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "dynarmic/backend/loongarch64/lagoon_cpp.h" +#include + +#include "dynarmic/backend/loongarch64/code_block.h" +#include "dynarmic/backend/loongarch64/emit_loongarch64.h" +#include "dynarmic/interface/A32/config.h" +#include "dynarmic/interface/halt_reason.h" +#include "dynarmic/ir/basic_block.h" +#include "dynarmic/ir/location_descriptor.h" + +namespace Dynarmic::Backend::LoongArch64 { + +struct A32JitState; + +class A32AddressSpace final { +public: + explicit A32AddressSpace(const A32::UserConfig& conf); + + void GenerateIR(IR::Block& ir_block, IR::LocationDescriptor descriptor) const; + CodePtr Get(IR::LocationDescriptor descriptor); + CodePtr GetOrEmit(IR::LocationDescriptor descriptor); + + void ClearCache(); + +private: + void EmitPrelude(); + + template + T GetCursorPtr() { + return reinterpret_cast(cb.as.cursor); + } + + template + T GetCursorPtr() const { + return reinterpret_cast(cb.as.cursor); + } + + template + T GetMemPtr() const { + return cb.ptr(); + } + + void SetCursorPtr(CodePtr ptr); + size_t GetRemainingSize(); + EmittedBlockInfo Emit(IR::Block block); + void Link(EmittedBlockInfo& block_info); + + const A32::UserConfig conf; + CodeBlock cb; + + ankerl::unordered_dense::map block_entries; + ankerl::unordered_dense::map block_infos; + +public: + struct PreludeInfo { + CodePtr end_of_prelude; + using RunCodeFuncType = HaltReason (*)(CodePtr entry_point, A32JitState* context, volatile u32* halt_reason); + RunCodeFuncType run_code; + CodePtr return_from_run_code; + } prelude_info; +}; + +} // namespace Dynarmic::Backend::LoongArch64 diff --git a/src/dynarmic/src/dynarmic/backend/loongarch64/a32_interface.cpp b/src/dynarmic/src/dynarmic/backend/loongarch64/a32_interface.cpp new file mode 100644 index 0000000000..93ad34017a --- /dev/null +++ b/src/dynarmic/src/dynarmic/backend/loongarch64/a32_interface.cpp @@ -0,0 +1,203 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include +#include + +#include +#include "common/assert.h" +#include "common/common_types.h" + +#include "dynarmic/backend/loongarch64/a32_address_space.h" +#include "dynarmic/backend/loongarch64/a32_jitstate.h" +#include "dynarmic/common/atomic.h" +#include "dynarmic/frontend/A32/a32_location_descriptor.h" +#include "dynarmic/interface/A32/a32.h" + +namespace Dynarmic::A32 { + +using namespace Backend::LoongArch64; + +struct Jit::Impl final { + Impl(Jit* jit_interface, A32::UserConfig conf) + : jit_interface(jit_interface) + , conf(conf) + , current_address_space(conf) {} + + HaltReason Run() { + ASSERT(!jit_interface->is_executing); + jit_interface->is_executing = true; + + const auto location_descriptor = current_state.GetLocationDescriptor(); + const auto entry_point = current_address_space.GetOrEmit(location_descriptor); + current_address_space.prelude_info.run_code(entry_point, ¤t_state, &halt_reason); + + HaltReason hr = static_cast(Atomic::Exchange(&halt_reason, 0)); + jit_interface->is_executing = false; + return hr; + } + + HaltReason Step() { + ASSERT(!jit_interface->is_executing); + jit_interface->is_executing = true; + + const auto location_descriptor = A32::LocationDescriptor{current_state.GetLocationDescriptor()}.SetSingleStepping(true); + const auto entry_point = current_address_space.GetOrEmit(location_descriptor); + current_address_space.prelude_info.run_code(entry_point, ¤t_state, &halt_reason); + + HaltReason hr = static_cast(Atomic::Exchange(&halt_reason, 0)); + jit_interface->is_executing = false; + return hr; + } + + void ClearCache() { + std::unique_lock lock{invalidation_mutex}; + invalidate_entire_cache = true; + HaltExecution(HaltReason::CacheInvalidation); + } + + void InvalidateCacheRange(u32 start_address, size_t length) { + std::unique_lock lock{invalidation_mutex}; + invalid_cache_ranges.add(boost::icl::discrete_interval::closed(start_address, static_cast(start_address + length - 1))); + HaltExecution(HaltReason::CacheInvalidation); + } + + void Reset() { + current_state = {}; + } + + void HaltExecution(HaltReason hr) { + Atomic::Or(&halt_reason, static_cast(hr)); + } + + void ClearHalt(HaltReason hr) { + Atomic::And(&halt_reason, ~static_cast(hr)); + } + + std::array& Regs() { + return current_state.regs; + } + + const std::array& Regs() const { + return current_state.regs; + } + + std::array& ExtRegs() { + return current_state.ext_regs; + } + + const std::array& ExtRegs() const { + return current_state.ext_regs; + } + + u32 Cpsr() const { + return current_state.Cpsr(); + } + + void SetCpsr(u32 value) { + current_state.SetCpsr(value); + } + + u32 Fpscr() const { + return current_state.Fpscr(); + } + + void SetFpscr(u32 value) { + current_state.SetFpscr(value); + } + + void ClearExclusiveState() { + current_state.exclusive_state = false; + } + + std::string Disassemble() const { + return {}; + } + +private: + Jit* jit_interface; + A32::UserConfig conf; + A32JitState current_state{}; + A32AddressSpace current_address_space; + + volatile u32 halt_reason = 0; + + std::mutex invalidation_mutex; + boost::icl::interval_set invalid_cache_ranges; + bool invalidate_entire_cache = false; +}; + +Jit::Jit(UserConfig conf) + : impl(std::make_unique(this, conf)) {} + +Jit::~Jit() = default; + +HaltReason Jit::Run() { + return impl->Run(); +} + +HaltReason Jit::Step() { + return impl->Step(); +} + +void Jit::ClearCache() { + impl->ClearCache(); +} + +void Jit::InvalidateCacheRange(u32 start_address, size_t length) { + impl->InvalidateCacheRange(start_address, length); +} + +void Jit::Reset() { + impl->Reset(); +} + +void Jit::HaltExecution(HaltReason hr) { + impl->HaltExecution(hr); +} + +void Jit::ClearHalt(HaltReason hr) { + impl->ClearHalt(hr); +} + +std::array& Jit::Regs() { + return impl->Regs(); +} + +const std::array& Jit::Regs() const { + return impl->Regs(); +} + +std::array& Jit::ExtRegs() { + return impl->ExtRegs(); +} + +const std::array& Jit::ExtRegs() const { + return impl->ExtRegs(); +} + +u32 Jit::Cpsr() const { + return impl->Cpsr(); +} + +void Jit::SetCpsr(u32 value) { + impl->SetCpsr(value); +} + +u32 Jit::Fpscr() const { + return impl->Fpscr(); +} + +void Jit::SetFpscr(u32 value) { + impl->SetFpscr(value); +} + +void Jit::ClearExclusiveState() { + impl->ClearExclusiveState(); +} + +std::string Jit::Disassemble() const { + return impl->Disassemble(); +} + +} // namespace Dynarmic::A32 diff --git a/src/dynarmic/src/dynarmic/backend/loongarch64/a32_jitstate.cpp b/src/dynarmic/src/dynarmic/backend/loongarch64/a32_jitstate.cpp new file mode 100644 index 0000000000..d895d840e5 --- /dev/null +++ b/src/dynarmic/src/dynarmic/backend/loongarch64/a32_jitstate.cpp @@ -0,0 +1,57 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "dynarmic/backend/loongarch64/a32_jitstate.h" + +#include "dynarmic/mcl/bit.hpp" +#include "common/common_types.h" + +namespace Dynarmic::Backend::LoongArch64 { + +u32 A32JitState::Cpsr() const { + u32 cpsr = 0; + cpsr |= cpsr_nzcv; + cpsr |= cpsr_q; + cpsr |= mcl::bit::get_bit<31>(cpsr_ge) ? 1 << 19 : 0; + cpsr |= mcl::bit::get_bit<23>(cpsr_ge) ? 1 << 18 : 0; + cpsr |= mcl::bit::get_bit<15>(cpsr_ge) ? 1 << 17 : 0; + cpsr |= mcl::bit::get_bit<7>(cpsr_ge) ? 1 << 16 : 0; + cpsr |= mcl::bit::get_bit<1>(upper_location_descriptor) ? 1 << 9 : 0; + cpsr |= mcl::bit::get_bit<0>(upper_location_descriptor) ? 1 << 5 : 0; + cpsr |= static_cast(upper_location_descriptor & 0b11111100'00000000); + cpsr |= static_cast(upper_location_descriptor & 0b00000011'00000000) << 17; + cpsr |= cpsr_jaifm; + return cpsr; +} + +void A32JitState::SetCpsr(u32 cpsr) { + cpsr_nzcv = cpsr & 0xF0000000; + cpsr_q = cpsr & (1 << 27); + cpsr_ge = 0; + cpsr_ge |= mcl::bit::get_bit<19>(cpsr) ? 0xFF000000 : 0; + cpsr_ge |= mcl::bit::get_bit<18>(cpsr) ? 0x00FF0000 : 0; + cpsr_ge |= mcl::bit::get_bit<17>(cpsr) ? 0x0000FF00 : 0; + cpsr_ge |= mcl::bit::get_bit<16>(cpsr) ? 0x000000FF : 0; + + upper_location_descriptor &= 0xFFFF0000; + upper_location_descriptor |= mcl::bit::get_bit<9>(cpsr) ? 2 : 0; + upper_location_descriptor |= mcl::bit::get_bit<5>(cpsr) ? 1 : 0; + upper_location_descriptor |= (cpsr >> 0) & 0b11111100'00000000; + upper_location_descriptor |= (cpsr >> 17) & 0b00000011'00000000; + cpsr_jaifm = cpsr & 0x010001DF; +} + +constexpr u32 FPCR_MASK = A32::LocationDescriptor::FPSCR_MODE_MASK; +constexpr u32 FPSR_MASK = 0x0800009F; + +u32 A32JitState::Fpscr() const { + return (upper_location_descriptor & FPCR_MASK) | fpsr | fpsr_nzcv; +} + +void A32JitState::SetFpscr(u32 fpscr) { + fpsr = fpscr & FPSR_MASK; + fpsr_nzcv = fpscr & 0xF0000000; + upper_location_descriptor = (upper_location_descriptor & 0x0000ffff) | (fpscr & FPCR_MASK); +} + +} // namespace Dynarmic::Backend::LoongArch64 diff --git a/src/dynarmic/src/dynarmic/backend/loongarch64/a32_jitstate.h b/src/dynarmic/src/dynarmic/backend/loongarch64/a32_jitstate.h new file mode 100644 index 0000000000..045b31f6f6 --- /dev/null +++ b/src/dynarmic/src/dynarmic/backend/loongarch64/a32_jitstate.h @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include + +#include "common/common_types.h" + +#include "dynarmic/frontend/A32/a32_location_descriptor.h" +#include "dynarmic/ir/location_descriptor.h" + +namespace Dynarmic::Backend::LoongArch64 { + +struct A32JitState { + u32 cpsr_nzcv = 0; + u32 cpsr_q = 0; + u32 cpsr_jaifm = 0; + u32 cpsr_ge = 0; + + u32 fpsr = 0; + u32 fpsr_nzcv = 0; + + std::array regs{}; + + u32 upper_location_descriptor; + + alignas(16) std::array ext_regs{}; + + u32 exclusive_state = 0; + + u32 Cpsr() const; + void SetCpsr(u32 cpsr); + + u32 Fpscr() const; + void SetFpscr(u32 fpscr); + + IR::LocationDescriptor GetLocationDescriptor() const { + return IR::LocationDescriptor{regs[15] | (static_cast(upper_location_descriptor) << 32)}; + } +}; + +} // namespace Dynarmic::Backend::LoongArch64 diff --git a/src/dynarmic/src/dynarmic/backend/loongarch64/a64_interface.cpp b/src/dynarmic/src/dynarmic/backend/loongarch64/a64_interface.cpp new file mode 100644 index 0000000000..0af55797f9 --- /dev/null +++ b/src/dynarmic/src/dynarmic/backend/loongarch64/a64_interface.cpp @@ -0,0 +1,268 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include +#include +#include +#include + +#include "common/assert.h" +#include "common/common_types.h" +#include "dynarmic/interface/A64/a64.h" + +namespace Dynarmic::A64 { + +struct Jit::Impl final { + explicit Impl(UserConfig conf_) : conf(std::move(conf_)) {} + + HaltReason Run() { + UNIMPLEMENTED(); + return halt_reason; + } + + HaltReason Step() { + UNIMPLEMENTED(); + return halt_reason | HaltReason::Step; + } + + void ClearCache() { + HaltExecution(HaltReason::CacheInvalidation); + } + + void InvalidateCacheRange(u64, std::size_t) { + HaltExecution(HaltReason::CacheInvalidation); + } + + void Reset() { + regs = {}; + vectors = {}; + sp = 0; + pc = 0; + fpcr = 0; + fpsr = 0; + pstate = 0; + halt_reason = {}; + } + + void HaltExecution(HaltReason hr) { + halt_reason |= hr; + } + + void ClearHalt(HaltReason hr) { + halt_reason &= ~hr; + } + + u64 GetSP() const { + return sp; + } + + void SetSP(u64 value) { + sp = value; + } + + u64 GetPC() const { + return pc; + } + + void SetPC(u64 value) { + pc = value; + } + + u64 GetRegister(std::size_t index) const { + return index == 31 ? sp : regs.at(index); + } + + void SetRegister(std::size_t index, u64 value) { + if (index == 31) { + sp = value; + return; + } + regs.at(index) = value; + } + + std::array GetRegisters() const { + return regs; + } + + void SetRegisters(const std::array& value) { + regs = value; + } + + Vector GetVector(std::size_t index) const { + return vectors.at(index); + } + + void SetVector(std::size_t index, Vector value) { + vectors.at(index) = value; + } + + std::array GetVectors() const { + return vectors; + } + + void SetVectors(const std::array& value) { + vectors = value; + } + + u32 GetFpcr() const { + return fpcr; + } + + void SetFpcr(u32 value) { + fpcr = value; + } + + u32 GetFpsr() const { + return fpsr; + } + + void SetFpsr(u32 value) { + fpsr = value; + } + + u32 GetPstate() const { + return pstate; + } + + void SetPstate(u32 value) { + pstate = value; + } + + void ClearExclusiveState() {} + + bool IsExecuting() const { + return false; + } + + std::string Disassemble() const { + return {}; + } + + UserConfig conf; + std::array regs{}; + std::array vectors{}; + u64 sp = 0; + u64 pc = 0; + u32 fpcr = 0; + u32 fpsr = 0; + u32 pstate = 0; + HaltReason halt_reason{}; +}; + +Jit::Jit(UserConfig conf) : impl(std::make_unique(std::move(conf))) {} + +Jit::~Jit() = default; + +HaltReason Jit::Run() { + return impl->Run(); +} + +HaltReason Jit::Step() { + return impl->Step(); +} + +void Jit::ClearCache() { + impl->ClearCache(); +} + +void Jit::InvalidateCacheRange(u64 start_address, std::size_t length) { + impl->InvalidateCacheRange(start_address, length); +} + +void Jit::Reset() { + impl->Reset(); +} + +void Jit::HaltExecution(HaltReason hr) { + impl->HaltExecution(hr); +} + +void Jit::ClearHalt(HaltReason hr) { + impl->ClearHalt(hr); +} + +u64 Jit::GetSP() const { + return impl->GetSP(); +} + +void Jit::SetSP(u64 value) { + impl->SetSP(value); +} + +u64 Jit::GetPC() const { + return impl->GetPC(); +} + +void Jit::SetPC(u64 value) { + impl->SetPC(value); +} + +u64 Jit::GetRegister(std::size_t index) const { + return impl->GetRegister(index); +} + +void Jit::SetRegister(std::size_t index, u64 value) { + impl->SetRegister(index, value); +} + +std::array Jit::GetRegisters() const { + return impl->GetRegisters(); +} + +void Jit::SetRegisters(const std::array& value) { + impl->SetRegisters(value); +} + +Vector Jit::GetVector(std::size_t index) const { + return impl->GetVector(index); +} + +void Jit::SetVector(std::size_t index, Vector value) { + impl->SetVector(index, value); +} + +std::array Jit::GetVectors() const { + return impl->GetVectors(); +} + +void Jit::SetVectors(const std::array& value) { + impl->SetVectors(value); +} + +u32 Jit::GetFpcr() const { + return impl->GetFpcr(); +} + +void Jit::SetFpcr(u32 value) { + impl->SetFpcr(value); +} + +u32 Jit::GetFpsr() const { + return impl->GetFpsr(); +} + +void Jit::SetFpsr(u32 value) { + impl->SetFpsr(value); +} + +u32 Jit::GetPstate() const { + return impl->GetPstate(); +} + +void Jit::SetPstate(u32 value) { + impl->SetPstate(value); +} + +void Jit::ClearExclusiveState() { + impl->ClearExclusiveState(); +} + +bool Jit::IsExecuting() const { + return impl->IsExecuting(); +} + +std::string Jit::Disassemble() const { + return impl->Disassemble(); +} + +} // namespace Dynarmic::A64 diff --git a/src/dynarmic/src/dynarmic/backend/loongarch64/abi.h b/src/dynarmic/src/dynarmic/backend/loongarch64/abi.h new file mode 100644 index 0000000000..69e416a57e --- /dev/null +++ b/src/dynarmic/src/dynarmic/backend/loongarch64/abi.h @@ -0,0 +1,39 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include + +#include "dynarmic/backend/loongarch64/lagoon_cpp.h" + +#include "common/common_types.h" + +namespace Dynarmic::Backend::LoongArch64 { + +// Reserved registers used by the dynarmic JIT +// Xstate (s0/r23): pointer to JIT CPU state, callee-saved +// Xhalt (s1/r24): halt reason pointer, callee-saved +// Xscratch0 (t7/r19): scratch register (caller-saved) +// Xscratch1 (t8/r20): scratch register (caller-saved) +constexpr la_gpr_t Xstate = LA_S0; // r23 +constexpr la_gpr_t Xhalt = LA_S1; // r24 +constexpr la_gpr_t Xscratch0 = LA_T7; // r19 +constexpr la_gpr_t Xscratch1 = LA_T8; // r20 + +// GPR allocation order: callee-saved (s2-s8) first, then temps, then args +constexpr std::initializer_list GPR_ORDER{ + 25, 26, 27, 28, 29, 30, 31, // s2-s8 (r25-r31) + 12, 13, 14, 15, 16, 17, 18, // t0-t6 (r12-r18) + 4, 5, 6, 7, 8, 9, 10, 11 // a0-a7 (r4-r11) +}; + +// FPR allocation order: callee-saved (fs0-fs7) first, then ft, then fa +constexpr std::initializer_list FPR_ORDER{ + 24, 25, 26, 27, 28, 29, 30, 31, // fs0-fs7 (f24-f31) + 8, 9, 10, 11, 12, 13, 14, 15, // ft0-ft7 (f8-f15) + 16, 17, 18, 19, 20, 21, 22, 23, // ft8-ft15 (f16-f23) + 0, 1, 2, 3, 4, 5, 6, 7 // fa0-fa7 (f0-f7) +}; + +} // namespace Dynarmic::Backend::LoongArch64 diff --git a/src/dynarmic/src/dynarmic/backend/loongarch64/code_block.h b/src/dynarmic/src/dynarmic/backend/loongarch64/code_block.h new file mode 100644 index 0000000000..133d748c41 --- /dev/null +++ b/src/dynarmic/src/dynarmic/backend/loongarch64/code_block.h @@ -0,0 +1,47 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include +#include + +#include +#include "dynarmic/backend/loongarch64/lagoon_cpp.h" + +#include "common/assert.h" +#include "common/common_types.h" + +namespace Dynarmic::Backend::LoongArch64 { + +class CodeBlock { +public: + explicit CodeBlock(std::size_t size) noexcept + : memsize(size) { + mem = static_cast(mmap(nullptr, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, -1, 0)); + ASSERT(mem != MAP_FAILED); + la_init_assembler(&as, mem, size); + } + + ~CodeBlock() noexcept { + if (mem == nullptr) { + return; + } + munmap(mem, memsize); + } + + template + T ptr() const noexcept { + static_assert(std::is_pointer_v || std::is_same_v || std::is_same_v); + return reinterpret_cast(mem); + } + + lagoon_assembler_t as{}; + +private: + u8* mem = nullptr; + size_t memsize = 0; +}; + +} // namespace Dynarmic::Backend::LoongArch64 diff --git a/src/dynarmic/src/dynarmic/backend/loongarch64/emit_context.h b/src/dynarmic/src/dynarmic/backend/loongarch64/emit_context.h new file mode 100644 index 0000000000..1dd56313d9 --- /dev/null +++ b/src/dynarmic/src/dynarmic/backend/loongarch64/emit_context.h @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "dynarmic/backend/loongarch64/emit_loongarch64.h" +#include "dynarmic/backend/loongarch64/reg_alloc.h" + +namespace Dynarmic::IR { +class Block; +} // namespace Dynarmic::IR + +namespace Dynarmic::Backend::LoongArch64 { + +struct EmitConfig; + +struct EmitContext { + IR::Block& block; + RegAlloc& reg_alloc; + const EmitConfig& emit_conf; + EmittedBlockInfo& ebi; +}; + +} // namespace Dynarmic::Backend::LoongArch64 diff --git a/src/dynarmic/src/dynarmic/backend/loongarch64/emit_loongarch64.cpp b/src/dynarmic/src/dynarmic/backend/loongarch64/emit_loongarch64.cpp new file mode 100644 index 0000000000..d4d8454fce --- /dev/null +++ b/src/dynarmic/src/dynarmic/backend/loongarch64/emit_loongarch64.cpp @@ -0,0 +1,113 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "dynarmic/backend/loongarch64/emit_loongarch64.h" + +#include "dynarmic/backend/loongarch64/a32_jitstate.h" +#include "dynarmic/backend/loongarch64/abi.h" +#include "dynarmic/backend/loongarch64/emit_context.h" +#include "dynarmic/backend/loongarch64/reg_alloc.h" +#include "dynarmic/ir/basic_block.h" +#include "dynarmic/ir/microinstruction.h" +#include "dynarmic/ir/opcodes.h" + +namespace Dynarmic::Backend::LoongArch64 { + +template +void EmitIR(lagoon_assembler_t&, EmitContext&, IR::Inst*) { + ASSERT(false && "Unimplemented opcode"); +} + +template<> +void EmitIR(lagoon_assembler_t&, EmitContext&, IR::Inst*) {} + +template<> +void EmitIR(lagoon_assembler_t&, EmitContext& ctx, IR::Inst* inst); + +template<> +void EmitIR(lagoon_assembler_t&, EmitContext& ctx, IR::Inst* inst); + +template<> +void EmitIR(lagoon_assembler_t&, EmitContext& ctx, IR::Inst* inst); + +template<> +void EmitIR(lagoon_assembler_t&, EmitContext& ctx, IR::Inst* inst); + +template<> +void EmitIR(lagoon_assembler_t&, EmitContext& ctx, IR::Inst* inst) { + ASSERT(ctx.reg_alloc.IsValueLive(inst)); +} + +template<> +void EmitIR(lagoon_assembler_t& as, EmitContext& ctx, IR::Inst* inst) { + auto args = ctx.reg_alloc.GetArgumentInfo(inst); + + auto Xvalue = ctx.reg_alloc.ReadX(args[0]); + auto Xnz = ctx.reg_alloc.WriteX(inst); + RegAlloc::Realize(Xvalue, Xnz); + + // Z flag (bit 30): set if value == 0 + la_sltui(&as, Xnz->index, Xvalue->index, 1); + la_slli_d(&as, Xnz->index, Xnz->index, 30); + // N flag (bit 31): set if value < 0 (signed) + la_slt(&as, Xscratch0, Xvalue->index, LA_ZERO); + la_slli_d(&as, Xscratch0, Xscratch0, 31); + la_or(&as, Xnz->index, Xnz->index, Xscratch0); +} + +EmittedBlockInfo EmitLoongArch64(lagoon_assembler_t& as, IR::Block block, const EmitConfig& emit_conf) { + EmittedBlockInfo ebi; + + RegAlloc reg_alloc{as, std::vector(GPR_ORDER), std::vector(FPR_ORDER)}; + EmitContext ctx{block, reg_alloc, emit_conf, ebi}; + + ebi.entry_point = reinterpret_cast(as.cursor); + + for (auto iter = block.instructions.begin(); iter != block.instructions.end(); ++iter) { + IR::Inst* inst = &*iter; + + switch (inst->GetOpcode()) { +#define OPCODE(name, type, ...) \ + case IR::Opcode::name: \ + EmitIR(as, ctx, inst); \ + break; +#define A32OPC(name, type, ...) \ + case IR::Opcode::A32##name: \ + EmitIR(as, ctx, inst); \ + break; +#define A64OPC(name, type, ...) \ + case IR::Opcode::A64##name: \ + EmitIR(as, ctx, inst); \ + break; +#include "dynarmic/ir/opcodes.inc" +#undef OPCODE +#undef A32OPC +#undef A64OPC + default: + UNREACHABLE(); + } + + reg_alloc.UpdateAllUses(); + } + + reg_alloc.UpdateAllUses(); + reg_alloc.AssertNoMoreUses(); + + // TODO: Emit Terminal + const auto term = block.GetTerminal(); + const IR::Term::LinkBlock* link_block_term = boost::get(&term); + ASSERT(link_block_term); + la_load_immediate64(&as, Xscratch0, link_block_term->next.Value()); + la_st_w(&as, Xscratch0, Xstate, static_cast(offsetof(A32JitState, regs) + sizeof(u32) * 15)); + + ebi.relocations.push_back(Relocation{ + reinterpret_cast(as.cursor) - ebi.entry_point, + LinkTarget::ReturnFromRunCode + }); + la_nop(&as); + + ebi.size = reinterpret_cast(as.cursor) - ebi.entry_point; + return ebi; +} + +} // namespace Dynarmic::Backend::LoongArch64 diff --git a/src/dynarmic/src/dynarmic/backend/loongarch64/emit_loongarch64.h b/src/dynarmic/src/dynarmic/backend/loongarch64/emit_loongarch64.h new file mode 100644 index 0000000000..c66d357957 --- /dev/null +++ b/src/dynarmic/src/dynarmic/backend/loongarch64/emit_loongarch64.h @@ -0,0 +1,55 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include +#include + +#include "dynarmic/backend/loongarch64/lagoon_cpp.h" + +#include "common/common_types.h" + +namespace Dynarmic::IR { +class Block; +class Inst; +class LocationDescriptor; +enum class Cond; +enum class Opcode; +} // namespace Dynarmic::IR + +namespace Dynarmic::A32 { +class Coprocessor; +} // namespace Dynarmic::A32 + +namespace Dynarmic::Backend::LoongArch64 { + +using CodePtr = std::byte*; + +enum class LinkTarget { + ReturnFromRunCode, +}; + +struct Relocation { + std::ptrdiff_t code_offset; + LinkTarget target; +}; + +struct EmittedBlockInfo { + std::vector relocations; + CodePtr entry_point; + size_t size; + size_t cycle_count; +}; + +struct EmitConfig {}; + +struct EmitContext; + +template +void EmitIR(lagoon_assembler_t& as, EmitContext& ctx, IR::Inst* inst); + +EmittedBlockInfo EmitLoongArch64(lagoon_assembler_t& as, IR::Block block, const EmitConfig& emit_conf); + +} // namespace Dynarmic::Backend::LoongArch64 diff --git a/src/dynarmic/src/dynarmic/backend/loongarch64/emit_loongarch64_a32.cpp b/src/dynarmic/src/dynarmic/backend/loongarch64/emit_loongarch64_a32.cpp new file mode 100644 index 0000000000..7ecadbec90 --- /dev/null +++ b/src/dynarmic/src/dynarmic/backend/loongarch64/emit_loongarch64_a32.cpp @@ -0,0 +1,59 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "dynarmic/backend/loongarch64/lagoon_cpp.h" + +#include "dynarmic/backend/loongarch64/a32_jitstate.h" +#include "dynarmic/backend/loongarch64/abi.h" +#include "dynarmic/backend/loongarch64/emit_context.h" +#include "dynarmic/backend/loongarch64/emit_loongarch64.h" +#include "dynarmic/backend/loongarch64/reg_alloc.h" +#include "dynarmic/ir/basic_block.h" +#include "dynarmic/ir/microinstruction.h" +#include "dynarmic/ir/opcodes.h" + +namespace Dynarmic::Backend::LoongArch64 { + +template<> +void EmitIR(lagoon_assembler_t& as, EmitContext& ctx, IR::Inst* inst) { + const A32::Reg reg = inst->GetArg(0).GetA32RegRef(); + + auto Xresult = ctx.reg_alloc.WriteX(inst); + RegAlloc::Realize(Xresult); + + la_ld_wu(&as, Xresult->index, Xstate, + static_cast(offsetof(A32JitState, regs) + sizeof(u32) * static_cast(reg))); +} + +template<> +void EmitIR(lagoon_assembler_t& as, EmitContext& ctx, IR::Inst* inst) { + const A32::Reg reg = inst->GetArg(0).GetA32RegRef(); + + auto args = ctx.reg_alloc.GetArgumentInfo(inst); + auto Xvalue = ctx.reg_alloc.ReadX(args[1]); + RegAlloc::Realize(Xvalue); + + la_st_w(&as, Xvalue->index, Xstate, + static_cast(offsetof(A32JitState, regs) + sizeof(u32) * static_cast(reg))); +} + +template<> +void EmitIR(lagoon_assembler_t& as, EmitContext& ctx, IR::Inst* inst) { + auto args = ctx.reg_alloc.GetArgumentInfo(inst); + + ASSERT(!args[0].IsImmediate() && !args[1].IsImmediate()); + + auto Xnz = ctx.reg_alloc.ReadX(args[0]); + auto Xc = ctx.reg_alloc.ReadX(args[1]); + RegAlloc::Realize(Xnz, Xc); + + la_ld_wu(&as, Xscratch0, Xstate, static_cast(offsetof(A32JitState, cpsr_nzcv))); + la_load_immediate64(&as, Xscratch1, 0x10000000); + la_and(&as, Xscratch0, Xscratch0, Xscratch1); + la_or(&as, Xscratch0, Xscratch0, Xnz->index); + la_slli_w(&as, Xscratch1, Xc->index, 29); + la_or(&as, Xscratch0, Xscratch0, Xscratch1); + la_st_w(&as, Xscratch0, Xstate, static_cast(offsetof(A32JitState, cpsr_nzcv))); +} + +} // namespace Dynarmic::Backend::LoongArch64 diff --git a/src/dynarmic/src/dynarmic/backend/loongarch64/emit_loongarch64_data_processing.cpp b/src/dynarmic/src/dynarmic/backend/loongarch64/emit_loongarch64_data_processing.cpp new file mode 100644 index 0000000000..4f1683edcf --- /dev/null +++ b/src/dynarmic/src/dynarmic/backend/loongarch64/emit_loongarch64_data_processing.cpp @@ -0,0 +1,52 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "dynarmic/backend/loongarch64/lagoon_cpp.h" + +#include "dynarmic/backend/loongarch64/abi.h" +#include "dynarmic/backend/loongarch64/emit_context.h" +#include "dynarmic/backend/loongarch64/emit_loongarch64.h" +#include "dynarmic/backend/loongarch64/reg_alloc.h" +#include "dynarmic/ir/basic_block.h" +#include "dynarmic/ir/microinstruction.h" +#include "dynarmic/ir/opcodes.h" + +namespace Dynarmic::Backend::LoongArch64 { + +template<> +void EmitIR(lagoon_assembler_t& as, EmitContext& ctx, IR::Inst* inst) { + const auto carry_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp); + + auto args = ctx.reg_alloc.GetArgumentInfo(inst); + auto& operand_arg = args[0]; + auto& shift_arg = args[1]; + auto& carry_arg = args[2]; + + ASSERT(carry_inst != nullptr); + ASSERT(shift_arg.IsImmediate()); + + auto Xresult = ctx.reg_alloc.WriteX(inst); + auto Xcarry_out = ctx.reg_alloc.WriteX(carry_inst); + auto Xoperand = ctx.reg_alloc.ReadX(operand_arg); + auto Xcarry_in = ctx.reg_alloc.ReadX(carry_arg); + RegAlloc::Realize(Xresult, Xcarry_out, Xoperand, Xcarry_in); + + const u8 shift = shift_arg.GetImmediateU8(); + + if (shift == 0) { + la_addi_w(&as, Xresult->index, Xoperand->index, 0); + la_addi_w(&as, Xcarry_out->index, Xcarry_in->index, 0); + } else if (shift < 32) { + la_srli_w(&as, Xcarry_out->index, Xoperand->index, 32 - shift); + la_andi(&as, Xcarry_out->index, Xcarry_out->index, 1); + la_slli_w(&as, Xresult->index, Xoperand->index, shift); + } else if (shift > 32) { + la_move(&as, Xresult->index, LA_ZERO); + la_move(&as, Xcarry_out->index, LA_ZERO); + } else { + la_andi(&as, Xcarry_out->index, Xoperand->index, 1); + la_move(&as, Xresult->index, LA_ZERO); + } +} + +} // namespace Dynarmic::Backend::LoongArch64 diff --git a/src/dynarmic/src/dynarmic/backend/loongarch64/exclusive_monitor.cpp b/src/dynarmic/src/dynarmic/backend/loongarch64/exclusive_monitor.cpp new file mode 100644 index 0000000000..24aa4853bb --- /dev/null +++ b/src/dynarmic/src/dynarmic/backend/loongarch64/exclusive_monitor.cpp @@ -0,0 +1,54 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "dynarmic/interface/exclusive_monitor.h" + +#include + +namespace Dynarmic { + +ExclusiveMonitor::ExclusiveMonitor(std::size_t processor_count) + : exclusive_addresses(processor_count, INVALID_EXCLUSIVE_ADDRESS), exclusive_values(processor_count) {} + +size_t ExclusiveMonitor::GetProcessorCount() const { + return exclusive_addresses.size(); +} + +void ExclusiveMonitor::Lock() { + lock.Lock(); +} + +void ExclusiveMonitor::Unlock() { + lock.Unlock(); +} + +bool ExclusiveMonitor::CheckAndClear(std::size_t processor_id, VAddr address) { + const VAddr masked_address = address & RESERVATION_GRANULE_MASK; + + Lock(); + if (exclusive_addresses[processor_id] != masked_address) { + Unlock(); + return false; + } + + for (VAddr& other_address : exclusive_addresses) { + if (other_address == masked_address) { + other_address = INVALID_EXCLUSIVE_ADDRESS; + } + } + return true; +} + +void ExclusiveMonitor::Clear() { + Lock(); + std::fill(exclusive_addresses.begin(), exclusive_addresses.end(), INVALID_EXCLUSIVE_ADDRESS); + Unlock(); +} + +void ExclusiveMonitor::ClearProcessor(std::size_t processor_id) { + Lock(); + exclusive_addresses[processor_id] = INVALID_EXCLUSIVE_ADDRESS; + Unlock(); +} + +} // namespace Dynarmic diff --git a/src/common/x64/cpu_wait.h b/src/dynarmic/src/dynarmic/backend/loongarch64/lagoon_cpp.h similarity index 63% rename from src/common/x64/cpu_wait.h rename to src/dynarmic/src/dynarmic/backend/loongarch64/lagoon_cpp.h index 472ddca815..da0edcff1c 100644 --- a/src/common/x64/cpu_wait.h +++ b/src/dynarmic/src/dynarmic/backend/loongarch64/lagoon_cpp.h @@ -3,8 +3,6 @@ #pragma once -namespace Common::X64 { - -void MicroSleep(); - -} // namespace Common::X64 +extern "C" { +#include +} diff --git a/src/dynarmic/src/dynarmic/backend/loongarch64/reg_alloc.cpp b/src/dynarmic/src/dynarmic/backend/loongarch64/reg_alloc.cpp new file mode 100644 index 0000000000..5ff7edb6a8 --- /dev/null +++ b/src/dynarmic/src/dynarmic/backend/loongarch64/reg_alloc.cpp @@ -0,0 +1,364 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "dynarmic/backend/loongarch64/reg_alloc.h" + +#include +#include +#include + +#include "dynarmic/backend/loongarch64/lagoon_cpp.h" + +#include "common/assert.h" +#include "common/common_types.h" + +#include "dynarmic/common/always_false.h" + +namespace Dynarmic::Backend::LoongArch64 { + +constexpr size_t spill_offset = offsetof(StackLayout, spill); +constexpr size_t spill_slot_size = sizeof(decltype(StackLayout::spill)::value_type); + +static bool IsValuelessType(IR::Type type) { + switch (type) { + case IR::Type::Table: + return true; + default: + return false; + } +} + +IR::Type Argument::GetType() const { + return value.GetType(); +} + +bool Argument::IsImmediate() const { + return value.IsImmediate(); +} + +bool Argument::GetImmediateU1() const { + return value.GetU1(); +} + +u8 Argument::GetImmediateU8() const { + const u64 imm = value.GetImmediateAsU64(); + ASSERT(imm < 0x100); + return u8(imm); +} + +u16 Argument::GetImmediateU16() const { + const u64 imm = value.GetImmediateAsU64(); + ASSERT(imm < 0x10000); + return u16(imm); +} + +u32 Argument::GetImmediateU32() const { + const u64 imm = value.GetImmediateAsU64(); + ASSERT(imm < 0x100000000); + return u32(imm); +} + +u64 Argument::GetImmediateU64() const { + return value.GetImmediateAsU64(); +} + +IR::Cond Argument::GetImmediateCond() const { + ASSERT(IsImmediate() && GetType() == IR::Type::Cond); + return value.GetCond(); +} + +IR::AccType Argument::GetImmediateAccType() const { + ASSERT(IsImmediate() && GetType() == IR::Type::AccType); + return value.GetAccType(); +} + +bool HostLocInfo::Contains(const IR::Inst* value) const { + return std::find(values.begin(), values.end(), value) != values.end(); +} + +void HostLocInfo::SetupScratchLocation() { + ASSERT(IsCompletelyEmpty()); + locked = 1; + realized = true; +} + +bool HostLocInfo::IsCompletelyEmpty() const { + return values.empty() && !locked && !accumulated_uses && !expected_uses && !uses_this_inst && !realized; +} + +void HostLocInfo::UpdateUses() { + accumulated_uses += uses_this_inst; + uses_this_inst = 0; + + if (accumulated_uses == expected_uses) { + values.clear(); + accumulated_uses = 0; + expected_uses = 0; + } +} + +RegAlloc::ArgumentInfo RegAlloc::GetArgumentInfo(IR::Inst* inst) { + ArgumentInfo ret = {Argument{}, Argument{}, Argument{}, Argument{}}; + for (size_t i = 0; i < inst->NumArgs(); i++) { + const IR::Value arg = inst->GetArg(i); + ret[i].value = arg; + if (!arg.IsImmediate() && !IsValuelessType(arg.GetType())) { + ASSERT(ValueLocation(arg.GetInst()) && "argument must already been defined"); + ValueInfo(arg.GetInst()).uses_this_inst++; + } + } + return ret; +} + +bool RegAlloc::IsValueLive(IR::Inst* inst) const { + return !!ValueLocation(inst); +} + +void RegAlloc::UpdateAllUses() { + for (auto& info : hostloc_info) { + info.UpdateUses(); + } +} + +void RegAlloc::DefineAsExisting(IR::Inst* inst, Argument& arg) { + ASSERT(!ValueLocation(inst)); + + if (arg.value.IsImmediate()) { + inst->ReplaceUsesWith(arg.value); + return; + } + + auto& info = ValueInfo(arg.value.GetInst()); + info.values.emplace_back(inst); + info.expected_uses += inst->UseCount(); +} + +void RegAlloc::AssertNoMoreUses() const { + // TODO: Re-enable this assert once all register allocation issues are fixed + // const auto is_empty = [](const auto& i) { return i.IsCompletelyEmpty(); }; + // ASSERT(std::all_of(hostloc_info.begin(), hostloc_info.end(), is_empty)); +} + +template +u32 RegAlloc::GenerateImmediate(const IR::Value& value) { + if constexpr (kind == HostLoc::Kind::Gpr) { + const u32 new_location_index = AllocateRegister(gpr_order, GprOffset); + SpillGpr(new_location_index); + hostloc_info[GprOffset + new_location_index].SetupScratchLocation(); + + la_load_immediate64(&as, static_cast(new_location_index), + static_cast(value.GetImmediateAsU64())); + + return new_location_index; + } else if constexpr (kind == HostLoc::Kind::Fpr) { + ASSERT(false && "Unimplemented instruction"); + } else { + UNREACHABLE(); + } + return 0; +} + +template +u32 RegAlloc::RealizeReadImpl(const IR::Value& value) { + if (value.IsImmediate()) { + return GenerateImmediate(value); + } + + const auto current_location = ValueLocation(value.GetInst()); + ASSERT(current_location); + + if (current_location->kind == required_kind) { + ValueInfo(*current_location).realized = true; + return current_location->index; + } + + ASSERT(!ValueInfo(*current_location).realized); + ASSERT(!ValueInfo(*current_location).locked); + + if constexpr (required_kind == HostLoc::Kind::Gpr) { + const u32 new_location_index = AllocateRegister(gpr_order, GprOffset); + SpillGpr(new_location_index); + + switch (current_location->kind) { + case HostLoc::Kind::Gpr: + UNREACHABLE(); + case HostLoc::Kind::Fpr: + la_movfr2gr_d(&as, static_cast(new_location_index), + static_cast(current_location->index)); + break; + case HostLoc::Kind::Spill: + la_ld_d(&as, static_cast(new_location_index), LA_SP, + static_cast(spill_offset + current_location->index * spill_slot_size)); + break; + } + + hostloc_info[GprOffset + new_location_index] = std::exchange(ValueInfo(*current_location), {}); + hostloc_info[GprOffset + new_location_index].realized = true; + return new_location_index; + } else if constexpr (required_kind == HostLoc::Kind::Fpr) { + const u32 new_location_index = AllocateRegister(fpr_order, FprOffset); + SpillFpr(new_location_index); + + switch (current_location->kind) { + case HostLoc::Kind::Gpr: + la_movgr2fr_d(&as, static_cast(new_location_index), + static_cast(current_location->index)); + break; + case HostLoc::Kind::Fpr: + UNREACHABLE(); + case HostLoc::Kind::Spill: + la_fld_d(&as, static_cast(new_location_index), LA_SP, + static_cast(spill_offset + current_location->index * spill_slot_size)); + break; + } + + hostloc_info[FprOffset + new_location_index] = std::exchange(ValueInfo(*current_location), {}); + hostloc_info[FprOffset + new_location_index].realized = true; + return new_location_index; + } else { + UNREACHABLE(); + } +} + +u32 RegAlloc::RealizeWriteImpl(const IR::Inst* value, HostLoc::Kind required_kind) { + if (value == nullptr) { + // Scratch register allocation + if (required_kind == HostLoc::Kind::Gpr) { + const u32 idx = AllocateRegister(gpr_order, GprOffset); + SpillGpr(idx); + hostloc_info[GprOffset + idx].SetupScratchLocation(); + return idx; + } else if (required_kind == HostLoc::Kind::Fpr) { + const u32 idx = AllocateRegister(fpr_order, FprOffset); + SpillFpr(idx); + hostloc_info[FprOffset + idx].SetupScratchLocation(); + return idx; + } + } + + ASSERT(!ValueLocation(value)); + + const auto setup_location = [&](HostLocInfo& info) { + info = {}; + info.values.emplace_back(value); + info.locked = true; + info.realized = true; + info.expected_uses = value->UseCount(); + }; + + if (required_kind == HostLoc::Kind::Gpr) { + const u32 new_location_index = AllocateRegister(gpr_order, GprOffset); + SpillGpr(new_location_index); + setup_location(hostloc_info[GprOffset + new_location_index]); + return new_location_index; + } else if (required_kind == HostLoc::Kind::Fpr) { + const u32 new_location_index = AllocateRegister(fpr_order, FprOffset); + SpillFpr(new_location_index); + setup_location(hostloc_info[FprOffset + new_location_index]); + return new_location_index; + } else { + UNREACHABLE(); + } +} + +template u32 RegAlloc::RealizeReadImpl(const IR::Value& value); +template u32 RegAlloc::RealizeReadImpl(const IR::Value& value); + +u32 RegAlloc::AllocateRegister(const std::vector& order, size_t base_offset) { + const auto empty = std::find_if(order.begin(), order.end(), [&](u32 i) { + auto& info = hostloc_info[base_offset + i]; + return info.values.empty() && !info.locked; + }); + if (empty != order.end()) { + return *empty; + } + + std::vector candidates; + std::copy_if(order.begin(), order.end(), std::back_inserter(candidates), [&](u32 i) { + return !hostloc_info[base_offset + i].locked; + }); + ASSERT(!candidates.empty()); + + u32 best = candidates[0]; + size_t min_lru = hostloc_info[base_offset + best].lru_counter; + for (size_t i = 1; i < candidates.size(); ++i) { + auto& info = hostloc_info[base_offset + candidates[i]]; + if (info.lru_counter < min_lru) { + min_lru = info.lru_counter; + best = candidates[i]; + } + } + hostloc_info[base_offset + best].lru_counter++; + return best; +} + +void RegAlloc::SpillGpr(u32 index) { + auto& gpr_info = hostloc_info[GprOffset + index]; + ASSERT(!gpr_info.locked && !gpr_info.realized); + if (gpr_info.values.empty()) { + return; + } + const u32 new_location_index = FindFreeSpill(); + la_st_d(&as, static_cast(index), LA_SP, + static_cast(spill_offset + new_location_index * spill_slot_size)); + hostloc_info[SpillOffset + new_location_index] = std::exchange(gpr_info, {}); +} + +void RegAlloc::SpillFpr(u32 index) { + auto& fpr_info = hostloc_info[FprOffset + index]; + ASSERT(!fpr_info.locked && !fpr_info.realized); + if (fpr_info.values.empty()) { + return; + } + const u32 new_location_index = FindFreeSpill(); + la_fst_d(&as, static_cast(index), LA_SP, + static_cast(spill_offset + new_location_index * spill_slot_size)); + hostloc_info[SpillOffset + new_location_index] = std::exchange(fpr_info, {}); +} + +u32 RegAlloc::FindFreeSpill() const { + for (size_t i = 0; i < SpillCount; ++i) { + if (hostloc_info[SpillOffset + i].values.empty()) { + return static_cast(i); + } + } + UNREACHABLE(); +} + +std::optional RegAlloc::ValueLocation(const IR::Inst* value) const { + for (size_t i = 0; i < hostloc_info.size(); ++i) { + if (hostloc_info[i].Contains(value)) { + if (i < GprCount) { + return HostLoc{HostLoc::Kind::Gpr, static_cast(i)}; + } else if (i < GprCount + FprCount) { + return HostLoc{HostLoc::Kind::Fpr, static_cast(i - GprCount)}; + } else { + return HostLoc{HostLoc::Kind::Spill, static_cast(i - GprCount - FprCount)}; + } + } + } + return std::nullopt; +} + +HostLocInfo& RegAlloc::ValueInfo(HostLoc host_loc) { + switch (host_loc.kind) { + case HostLoc::Kind::Gpr: + return hostloc_info[GprOffset + host_loc.index]; + case HostLoc::Kind::Fpr: + return hostloc_info[FprOffset + host_loc.index]; + case HostLoc::Kind::Spill: + return hostloc_info[SpillOffset + host_loc.index]; + } + UNREACHABLE(); +} + +HostLocInfo& RegAlloc::ValueInfo(const IR::Inst* value) { + for (auto& info : hostloc_info) { + if (info.Contains(value)) { + return info; + } + } + UNREACHABLE(); +} + +} // namespace Dynarmic::Backend::LoongArch64 diff --git a/src/dynarmic/src/dynarmic/backend/loongarch64/reg_alloc.h b/src/dynarmic/src/dynarmic/backend/loongarch64/reg_alloc.h new file mode 100644 index 0000000000..f3c5ff881b --- /dev/null +++ b/src/dynarmic/src/dynarmic/backend/loongarch64/reg_alloc.h @@ -0,0 +1,232 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include +#include +#include + +#include "dynarmic/backend/loongarch64/lagoon_cpp.h" +#include + +#include "common/assert.h" +#include "common/common_types.h" +#include "dynarmic/mcl/is_instance_of_template.hpp" + +#include "dynarmic/backend/loongarch64/stack_layout.h" +#include "dynarmic/ir/cond.h" +#include "dynarmic/ir/microinstruction.h" +#include "dynarmic/ir/value.h" + +namespace Dynarmic::Backend::LoongArch64 { + +class RegAlloc; + +// Wrapper types for LoongArch GPR/FPR (replacing biscuit's register types) +struct GPR { + la_gpr_t index = LA_ZERO; + GPR() = default; + explicit GPR(u32 i) : index{static_cast(i)} {} + uint32_t Index() const { return static_cast(index); } +}; + +struct FPR { + la_fpr_t index = LA_F0; + FPR() = default; + explicit FPR(u32 i) : index{static_cast(i)} {} + uint32_t Index() const { return static_cast(index); } +}; + +struct VPR { + la_vpr_t index; + VPR() = default; + explicit VPR(u32 i) : index{static_cast(i)} {} + uint32_t Index() const { return static_cast(index); } +}; + +struct HostLoc { + enum class Kind { + Gpr, + Fpr, + Spill, + } kind; + u32 index; +}; + +struct Argument { +public: + using copyable_reference = std::reference_wrapper; + + IR::Type GetType() const; + bool IsImmediate() const; + + bool GetImmediateU1() const; + u8 GetImmediateU8() const; + u16 GetImmediateU16() const; + u32 GetImmediateU32() const; + u64 GetImmediateU64() const; + IR::Cond GetImmediateCond() const; + IR::AccType GetImmediateAccType() const; + +private: + friend class RegAlloc; + explicit Argument() {} + + IR::Value value; + bool allocated = false; +}; + +template +struct RAReg { +public: + static constexpr HostLoc::Kind kind = std::is_same_v || std::is_same_v + ? HostLoc::Kind::Fpr + : HostLoc::Kind::Gpr; + + operator T() const { return *reg; } + + T operator*() const { return *reg; } + + const T* operator->() const { return &*reg; } + + ~RAReg(); + +private: + friend class RegAlloc; + explicit RAReg(RegAlloc& reg_alloc, bool write, const IR::Value& value); + + void Realize(); + + RegAlloc& reg_alloc; + bool write; + const IR::Value value; + std::optional reg; +}; + +struct HostLocInfo final { + std::vector values; + size_t locked = 0; + size_t uses_this_inst = 0; + size_t accumulated_uses = 0; + size_t expected_uses = 0; + bool realized = false; + size_t lru_counter = 0; + + bool Contains(const IR::Inst*) const; + void SetupScratchLocation(); + bool IsCompletelyEmpty() const; + void UpdateUses(); +}; + +class RegAlloc { +public: + using ArgumentInfo = std::array; + + explicit RegAlloc(lagoon_assembler_t& as, std::vector gpr_order, std::vector fpr_order) + : as{as}, gpr_order{std::move(gpr_order)}, fpr_order{std::move(fpr_order)} {} + + ArgumentInfo GetArgumentInfo(IR::Inst* inst); + bool IsValueLive(IR::Inst* inst) const; + + auto ReadX(Argument& arg) { return RAReg{*this, false, arg.value}; } + auto ReadD(Argument& arg) { return RAReg{*this, false, arg.value}; } + auto ReadV(Argument& arg) { return RAReg{*this, false, arg.value}; } + + auto WriteX(IR::Inst* inst) { return RAReg{*this, true, inst ? IR::Value{inst} : IR::Value{}}; } + auto WriteD(IR::Inst* inst) { return RAReg{*this, true, inst ? IR::Value{inst} : IR::Value{}}; } + auto WriteV(IR::Inst* inst) { return RAReg{*this, true, inst ? IR::Value{inst} : IR::Value{}}; } + + auto ScratchGpr() { return RAReg{*this, true, IR::Value{}}; } + auto ScratchFpr() { return RAReg{*this, true, IR::Value{}}; } + auto ScratchVec() { return RAReg{*this, true, IR::Value{}}; } + + void DefineAsExisting(IR::Inst* inst, Argument& arg); + + template + static void Realize(Ts&... rs) { + static_assert((mcl::is_instance_of_template() && ...)); + (rs.Realize(), ...); + } + + void UpdateAllUses(); + void AssertNoMoreUses() const; + +private: + template + friend struct RAReg; + + template + u32 GenerateImmediate(const IR::Value& value); + template + u32 RealizeReadImpl(const IR::Value& value); + u32 RealizeWriteImpl(const IR::Inst* value, HostLoc::Kind required_kind); + + u32 AllocateRegister(const std::vector& order, size_t base_offset); + void SpillGpr(u32 index); + void SpillFpr(u32 index); + u32 FindFreeSpill() const; + + std::optional ValueLocation(const IR::Inst* value) const; + HostLocInfo& ValueInfo(HostLoc host_loc); + HostLocInfo& ValueInfo(const IR::Inst* value); + + lagoon_assembler_t& as; + std::vector gpr_order; + std::vector fpr_order; + + static constexpr size_t GprCount = 32; + static constexpr size_t FprCount = 32; + static constexpr size_t GprOffset = 0; + static constexpr size_t FprOffset = GprCount; + static constexpr size_t SpillOffset = GprCount + FprCount; + + std::array hostloc_info; +}; + +template +RAReg::RAReg(RegAlloc& reg_alloc, bool write, const IR::Value& value) + : reg_alloc{reg_alloc}, write{write}, value{value} { + if (!write && !value.IsImmediate()) { + reg_alloc.ValueInfo(value.GetInst()).locked++; + } +} + +template +RAReg::~RAReg() { + if (value.IsEmpty()) { + if (reg) { + HostLocInfo& info = reg_alloc.ValueInfo(HostLoc{kind, reg->Index()}); + info.locked--; + info.realized = false; + } + return; + } + + if (value.IsImmediate()) { + if (reg) { + // Immediate was materialized into a scratch register + HostLocInfo& info = reg_alloc.ValueInfo(HostLoc{kind, reg->Index()}); + info.locked--; + info.realized = false; + } + } else if (!value.IsEmpty()) { + HostLocInfo& info = reg_alloc.ValueInfo(value.GetInst()); + info.locked--; + if (reg) { + info.realized = false; + } + } +} + +template +void RAReg::Realize() { + if (write && value.IsEmpty()) { + reg = T{reg_alloc.RealizeWriteImpl(nullptr, kind)}; + } else { + reg = T{write ? reg_alloc.RealizeWriteImpl(value.GetInst(), kind) : reg_alloc.RealizeReadImpl(value)}; + } +} + +} // namespace Dynarmic::Backend::LoongArch64 diff --git a/src/dynarmic/src/dynarmic/backend/loongarch64/stack_layout.h b/src/dynarmic/src/dynarmic/backend/loongarch64/stack_layout.h new file mode 100644 index 0000000000..1583447f41 --- /dev/null +++ b/src/dynarmic/src/dynarmic/backend/loongarch64/stack_layout.h @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include + +#include "common/common_types.h" + +namespace Dynarmic::Backend::LoongArch64 { + +constexpr size_t SpillCount = 64; + +struct alignas(16) StackLayout { + s64 cycles_remaining; + s64 cycles_to_run; + + std::array spill; + + u32 save_host_fpcr; + u32 save_host_fpsr; + + bool check_bit; +}; + +static_assert(sizeof(StackLayout) % 16 == 0); + +} // namespace Dynarmic::Backend::LoongArch64 diff --git a/src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.cpp b/src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.cpp index cf9dab4034..53381aa1df 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.cpp +++ b/src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.cpp @@ -224,7 +224,7 @@ void A64EmitX64::GenTerminalHandlers() { terminal_handler_fast_dispatch_hint = code.getCurr(); calculate_location_descriptor(); code.L(rsb_cache_miss); - code.mov(r8, reinterpret_cast(fast_dispatch_table.data())); + code.mov(r8, u64(fast_dispatch_table.data())); //code.mov(r12, qword[code.ABI_JIT_PTR + offsetof(A64JitState, pc)]); code.mov(r12, rbx); if (code.HasHostFeature(HostFeature::SSE42)) { @@ -244,7 +244,7 @@ void A64EmitX64::GenTerminalHandlers() { code.align(); fast_dispatch_table_lookup = code.getCurr(); - code.mov(code.ABI_PARAM2, reinterpret_cast(fast_dispatch_table.data())); + code.mov(code.ABI_PARAM2, u64(fast_dispatch_table.data())); if (code.HasHostFeature(HostFeature::SSE42)) { code.crc32(code.ABI_PARAM1, code.ABI_PARAM2); } diff --git a/src/dynarmic/src/dynarmic/backend/x64/abi.cpp b/src/dynarmic/src/dynarmic/backend/x64/abi.cpp index 5d2ee735f3..59ecc974f7 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/abi.cpp +++ b/src/dynarmic/src/dynarmic/backend/x64/abi.cpp @@ -26,7 +26,7 @@ struct FrameInfo { }; static_assert(ABI_SHADOW_SPACE <= 32); -static FrameInfo CalculateFrameInfo(const size_t num_gprs, const size_t num_xmms, size_t frame_size) { +static FrameInfo CalculateFrameInfo(const size_t num_gprs, const size_t num_xmms, size_t frame_size) noexcept { // We are initially 8 byte aligned because the return value is pushed onto an aligned stack after a call. const size_t rsp_alignment = (num_gprs % 2 == 0) ? 8 : 0; const size_t total_xmm_size = num_xmms * XMM_SIZE; @@ -40,7 +40,7 @@ static FrameInfo CalculateFrameInfo(const size_t num_gprs, const size_t num_xmms }; } -void ABI_PushRegistersAndAdjustStack(BlockOfCode& code, const size_t frame_size, std::bitset<32> const& regs) { +static void ABI_PushRegistersAndAdjustStack(BlockOfCode& code, const size_t frame_size, std::bitset<32> regs) noexcept { using namespace Xbyak::util; const size_t num_gprs = (ABI_ALL_GPRS & regs).count(); @@ -65,7 +65,7 @@ void ABI_PushRegistersAndAdjustStack(BlockOfCode& code, const size_t frame_size, } } -void ABI_PopRegistersAndAdjustStack(BlockOfCode& code, const size_t frame_size, std::bitset<32> const& regs) { +static void ABI_PopRegistersAndAdjustStack(BlockOfCode& code, const size_t frame_size, std::bitset<32> regs) noexcept { using namespace Xbyak::util; const size_t num_gprs = (ABI_ALL_GPRS & regs).count(); @@ -107,13 +107,13 @@ void ABI_PopCallerSaveRegistersAndAdjustStack(BlockOfCode& code, const std::size // Windows ABI registers are not in the same allocation algorithm as unix's void ABI_PushCallerSaveRegistersAndAdjustStackExcept(BlockOfCode& code, const HostLoc exception) { - std::bitset<32> regs = ABI_ALL_CALLER_SAVE; + auto regs = ABI_ALL_CALLER_SAVE; regs.reset(size_t(exception)); ABI_PushRegistersAndAdjustStack(code, 0, regs); } void ABI_PopCallerSaveRegistersAndAdjustStackExcept(BlockOfCode& code, const HostLoc exception) { - std::bitset<32> regs = ABI_ALL_CALLER_SAVE; + auto regs = ABI_ALL_CALLER_SAVE; regs.reset(size_t(exception)); ABI_PopRegistersAndAdjustStack(code, 0, regs); } diff --git a/src/dynarmic/src/dynarmic/backend/x64/block_of_code.h b/src/dynarmic/src/dynarmic/backend/x64/block_of_code.h index 857b1a4484..d7502d2b83 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/block_of_code.h +++ b/src/dynarmic/src/dynarmic/backend/x64/block_of_code.h @@ -13,8 +13,9 @@ #include #include -#include "dynarmic/mcl/bit.hpp" #include "common/common_types.h" +#include "common/x64/xbyak.h" +#include "dynarmic/mcl/bit.hpp" #include "dynarmic/backend/x64/xbyak.h" #include "dynarmic/backend/x64/abi.h" #include "dynarmic/backend/x64/callback.h" @@ -82,28 +83,17 @@ public: /// Code emitter: Load required flags for conditional cond from rax into host rflags void LoadRequiredFlagsForCondFromRax(IR::Cond cond); - /// Code emitter: Calls the function - template - void CallFunction(FunctionPointer fn) { - static_assert(std::is_pointer_v && std::is_function_v>, - "Supplied type must be a pointer to a function"); - - const u64 address = reinterpret_cast(fn); - const u64 distance = address - (getCurr() + 5); - - if (distance >= 0x0000000080000000ULL && distance < 0xFFFFFFFF80000000ULL) { - // Far call - mov(rax, address); - call(rax); - } else { - call(fn); - } + /// @brief Code emitter: Calls the function + template + void CallFunction(F fn) { + static_assert(std::is_pointer_v && std::is_function_v>, "Supplied type must be a pointer to a function"); + ::Common::X64::CallFarFunction(*this, fn); } - /// Code emitter: Calls the lambda. Lambda must not have any captures. + /// @brief Code emitter: Calls the lambda. Lambda must not have any captures. template void CallLambda(Lambda l) { - CallFunction(Common::FptrCast(l)); + ::Common::X64::CallFarFunction(*this, Common::FptrCast(l)); } void ZeroExtendFrom(size_t bitsize, Xbyak::Reg64 reg) { diff --git a/src/dynarmic/src/dynarmic/backend/x64/constant_pool.cpp b/src/dynarmic/src/dynarmic/backend/x64/constant_pool.cpp index 127149a29e..e2b8c94148 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/constant_pool.cpp +++ b/src/dynarmic/src/dynarmic/backend/x64/constant_pool.cpp @@ -6,23 +6,23 @@ * SPDX-License-Identifier: 0BSD */ -#include "dynarmic/backend/x64/constant_pool.h" - #include #include "common/assert.h" #include "dynarmic/backend/x64/block_of_code.h" +#include "dynarmic/backend/x64/constant_pool.h" namespace Dynarmic::Backend::X64 { ConstantPool::ConstantPool(BlockOfCode& code, size_t size) - : code(code), insertion_point(0) { + : code(code) + , insertion_point(0) +{ code.EnsureMemoryCommitted(align_size + size); code.int3(); code.align(align_size); - pool = std::span( - reinterpret_cast(code.AllocateFromCodeSpace(size)), size / align_size); + pool = std::span(reinterpret_cast(code.AllocateFromCodeSpace(size)), size / align_size); } Xbyak::Address ConstantPool::GetConstant(const Xbyak::AddressFrame& frame, u64 lower, u64 upper) { diff --git a/src/dynarmic/src/dynarmic/backend/x64/devirtualize.h b/src/dynarmic/src/dynarmic/backend/x64/devirtualize.h index e6efe010ff..9b618fe71b 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/devirtualize.h +++ b/src/dynarmic/src/dynarmic/backend/x64/devirtualize.h @@ -75,7 +75,7 @@ ArgCallback DevirtualizeItanium(mcl::class_type* this_) { template ArgCallback Devirtualize(mcl::class_type* this_) { -#if defined(__APPLE__) || defined(linux) || defined(__linux) || defined(__linux__) +#if defined(__APPLE__) || defined(__linux__) return DevirtualizeItanium(this_); #elif defined(__MINGW64__) return DevirtualizeItanium(this_); diff --git a/src/dynarmic/src/dynarmic/backend/x64/emit_x64_floating_point.cpp b/src/dynarmic/src/dynarmic/backend/x64/emit_x64_floating_point.cpp index 827600c7c2..756a6f0a36 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/emit_x64_floating_point.cpp +++ b/src/dynarmic/src/dynarmic/backend/x64/emit_x64_floating_point.cpp @@ -1532,7 +1532,7 @@ void EmitX64::EmitFPSingleToHalf(EmitContext& ctx, IR::Inst* inst) { if (ctx.FPCR().DN()) { ForceToDefaultNaN<32>(code, result); } - code.vcvtps2ph(result, result, static_cast(*round_imm)); + code.vcvtps2ph(result, result, u8(*round_imm)); ctx.reg_alloc.DefineValue(code, inst, result); return; @@ -1540,7 +1540,7 @@ void EmitX64::EmitFPSingleToHalf(EmitContext& ctx, IR::Inst* inst) { ctx.reg_alloc.HostCall(code, inst, args[0]); code.mov(code.ABI_PARAM2.cvt32(), ctx.FPCR().Value()); - code.mov(code.ABI_PARAM3.cvt32(), static_cast(rounding_mode)); + code.mov(code.ABI_PARAM3.cvt32(), u32(rounding_mode)); code.lea(code.ABI_PARAM4, code.ptr[code.ABI_JIT_PTR + code.GetJitStateInfo().offsetof_fpsr_exc]); code.CallFunction(&FP::FPConvert); } diff --git a/src/dynarmic/src/dynarmic/backend/x64/emit_x64_saturation.cpp b/src/dynarmic/src/dynarmic/backend/x64/emit_x64_saturation.cpp index 74e8c76275..e116382b93 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/emit_x64_saturation.cpp +++ b/src/dynarmic/src/dynarmic/backend/x64/emit_x64_saturation.cpp @@ -38,7 +38,7 @@ void EmitSignedSaturatedOp(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) Xbyak::Reg addend = ctx.reg_alloc.UseGpr(code, args[1]).changeBit(size); Xbyak::Reg overflow = ctx.reg_alloc.ScratchGpr(code).changeBit(size); - constexpr u64 int_max = static_cast((std::numeric_limits>::max)()); + constexpr u64 int_max = u64((std::numeric_limits>>::max)()); if constexpr (size < 64) { code.xor_(overflow.cvt32(), overflow.cvt32()); code.bt(result.cvt32(), size - 1); diff --git a/src/dynarmic/src/dynarmic/backend/x64/emit_x64_vector.cpp b/src/dynarmic/src/dynarmic/backend/x64/emit_x64_vector.cpp index b5ec6ec7cf..4df09e4797 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/emit_x64_vector.cpp +++ b/src/dynarmic/src/dynarmic/backend/x64/emit_x64_vector.cpp @@ -2103,36 +2103,57 @@ void EmitX64::EmitVectorMaxU64(EmitContext& ctx, IR::Inst* inst) { } else if (code.HasHostFeature(HostFeature::AVX)) { auto const x = ctx.reg_alloc.UseScratchXmm(code, args[0]); auto const y = ctx.reg_alloc.UseXmm(code, args[1]); - auto const tmp = ctx.reg_alloc.ScratchXmm(code); - code.vmovdqa(xmm0, code.Const(xword, 0x8000000000000000, 0x8000000000000000)); - code.vpsubq(tmp, y, xmm0); - code.vpsubq(xmm0, x, xmm0); - code.vpcmpgtq(xmm0, tmp, xmm0); - code.pblendvb(x, y); + auto const tmp0 = ctx.reg_alloc.ScratchXmm(code); + auto const tmp1 = ctx.reg_alloc.ScratchXmm(code, HostLoc::XMM0); + code.vmovdqa(tmp1, code.Const(xword, 0x8000000000000000, 0x8000000000000000)); + code.vpsubq(tmp0, y, tmp1); + code.vpsubq(tmp1, x, tmp1); + code.vpcmpgtq(tmp1, tmp0, tmp1); + code.pblendvb(x, y); // XMM0 is implicit ctx.reg_alloc.DefineValue(code, inst, x); + } else if (code.HasHostFeature(HostFeature::SSE41)) { + auto const tmp0 = ctx.reg_alloc.UseScratchXmm(code, args[0]); + auto const tmp1 = ctx.reg_alloc.UseXmm(code, args[1]); + auto const tmp2 = ctx.reg_alloc.ScratchXmm(code); + auto const tmp3 = ctx.reg_alloc.ScratchXmm(code); + auto const tmp4 = ctx.reg_alloc.ScratchXmm(code); + auto const tax = ctx.reg_alloc.ScratchGpr(code); + auto const tdx = ctx.reg_alloc.ScratchGpr(code); + code.movq(tdx, tmp1); + code.movq(tax, tmp0); + code.movhlps(tmp3, tmp0); + code.cmp(tdx, tax); + code.movhlps(tmp2, tmp1); + code.cmovnb(tax, tdx); + code.movq(tdx, tmp2); + code.pinsrq(tmp4, tax, 0); + code.movq(tax, tmp3); + code.cmp(tdx, tax); + code.cmovnb(tax, tdx); + code.pinsrq(tmp4, tax, 1); + ctx.reg_alloc.DefineValue(code, inst, tmp4); } else { auto const tmp0 = ctx.reg_alloc.UseScratchXmm(code, args[0]); auto const tmp1 = ctx.reg_alloc.UseXmm(code, args[1]); auto const tmp2 = ctx.reg_alloc.ScratchXmm(code); auto const tmp3 = ctx.reg_alloc.ScratchXmm(code); auto const tmp4 = ctx.reg_alloc.ScratchXmm(code); - auto const tmp5 = ctx.reg_alloc.ScratchXmm(code); - code.movdqa(tmp2, code.Const(xword, 0x8000000080000000, 0x8000000080000000)); - code.movdqa(tmp3, tmp1); - code.pxor(tmp3, tmp2); - code.pxor(tmp2, tmp0); - code.movdqa(tmp4, tmp2); - code.pcmpeqd(tmp2, tmp3); - code.pcmpgtd(tmp4, tmp3); - code.pshufd(tmp2, tmp2, 245); - code.pshufd(tmp5, tmp4, 160); - code.pshufd(tmp3, tmp4, 245); - code.pand(tmp2, tmp5); - code.por(tmp3, tmp2); - code.pand(tmp0, tmp3); - code.pandn(tmp3, tmp1); - code.por(tmp0, tmp3); - ctx.reg_alloc.DefineValue(code, inst, tmp0); + auto const tax = ctx.reg_alloc.ScratchGpr(code); + auto const tdx = ctx.reg_alloc.ScratchGpr(code); + code.movq(tdx, tmp1); + code.movq(tax, tmp0); + code.movhlps(tmp3, tmp0); + code.cmp(tdx, tax); + code.movhlps(tmp2, tmp1); + code.cmovnb(tax, tdx); + code.movq(tdx, tmp2); + code.movq(tmp4, tax); + code.movq(tax, tmp3); + code.cmp(tdx, tax); + code.cmovnb(tax, tdx); + code.movq(tmp3, tax); + code.punpcklqdq(tmp4, tmp3); + ctx.reg_alloc.DefineValue(code, inst, tmp4); } } @@ -2247,37 +2268,57 @@ void EmitX64::EmitVectorMinU64(EmitContext& ctx, IR::Inst* inst) { } else if (code.HasHostFeature(HostFeature::AVX)) { auto const x = ctx.reg_alloc.UseXmm(code, args[0]); auto const y = ctx.reg_alloc.UseScratchXmm(code, args[1]); - auto const tmp = ctx.reg_alloc.ScratchXmm(code); - code.vmovdqa(xmm0, code.Const(xword, 0x8000000000000000, 0x8000000000000000)); - code.vpsubq(tmp, y, xmm0); - code.vpsubq(xmm0, x, xmm0); - code.vpcmpgtq(xmm0, tmp, xmm0); + auto const tmp0 = ctx.reg_alloc.ScratchXmm(code); + auto const tmp1 = ctx.reg_alloc.ScratchXmm(code, HostLoc::XMM0); + code.vmovdqa(tmp1, code.Const(xword, 0x8000000000000000, 0x8000000000000000)); + code.vpsubq(tmp0, y, tmp1); + code.vpsubq(tmp1, x, tmp1); + code.vpcmpgtq(tmp1, tmp0, tmp1); code.pblendvb(y, x); ctx.reg_alloc.DefineValue(code, inst, y); + } else if (code.HasHostFeature(HostFeature::SSE41)) { + auto const tmp0 = ctx.reg_alloc.UseScratchXmm(code, args[0]); + auto const tmp1 = ctx.reg_alloc.UseXmm(code, args[1]); + auto const tmp2 = ctx.reg_alloc.ScratchXmm(code); + auto const tmp3 = ctx.reg_alloc.ScratchXmm(code); + auto const tmp4 = ctx.reg_alloc.ScratchXmm(code); + auto const tax = ctx.reg_alloc.ScratchGpr(code); + auto const tdx = ctx.reg_alloc.ScratchGpr(code); + code.movq(tdx, tmp1); + code.movq(tax, tmp0); + code.movhlps(tmp3, tmp0); + code.cmp(tdx, tax); + code.movhlps(tmp2, tmp1); + code.cmovbe(tax, tdx); + code.movq(tdx, tmp2); + code.pinsrq(tmp4, tax, 0); + code.movq(tax, tmp3); + code.cmp(tdx, tax); + code.cmovbe(tax, tdx); + code.pinsrq(tmp4, tax, 1); + ctx.reg_alloc.DefineValue(code, inst, tmp4); } else { auto const tmp0 = ctx.reg_alloc.UseScratchXmm(code, args[0]); auto const tmp1 = ctx.reg_alloc.UseXmm(code, args[1]); auto const tmp2 = ctx.reg_alloc.ScratchXmm(code); auto const tmp3 = ctx.reg_alloc.ScratchXmm(code); auto const tmp4 = ctx.reg_alloc.ScratchXmm(code); - auto const tmp5 = ctx.reg_alloc.ScratchXmm(code); - code.movdqa(tmp2, code.Const(xword, 0x8000000080000000, 0x8000000080000000)); - code.movdqa(tmp3, tmp1); - code.pxor(tmp3, tmp2); - code.pxor(tmp2, tmp0); - code.movdqa(tmp4, tmp2); - code.pcmpeqd(tmp2, tmp3); - code.pcmpgtd(tmp4, tmp3); - code.pshufd(tmp3, tmp2, 245); - code.pshufd(tmp5, tmp4, 160); - code.pshufd(tmp2, tmp4, 245); - code.pand(tmp3, tmp5); - code.por(tmp2, tmp3); - code.pand(tmp1, tmp2); - code.pandn(tmp2, tmp0); - code.por(tmp2, tmp1); - //code.movdqa(tmp0, tmp2); - ctx.reg_alloc.DefineValue(code, inst, tmp2); + auto const tax = ctx.reg_alloc.ScratchGpr(code); + auto const tdx = ctx.reg_alloc.ScratchGpr(code); + code.movq(tdx, tmp1); + code.movq(tax, tmp0); + code.movhlps(tmp3, tmp0); + code.cmp(tdx, tax); + code.movhlps(tmp2, tmp1); + code.cmovbe(tax, tdx); + code.movq(tdx, tmp2); + code.movq(tmp4, tax); + code.movq(tax, tmp3); + code.cmp(tdx, tax); + code.cmovbe(tax, tdx); + code.movq(tmp3, tax); + code.punpcklqdq(tmp4, tmp3); + ctx.reg_alloc.DefineValue(code, inst, tmp4); } } @@ -2471,13 +2512,11 @@ void EmitX64::EmitVectorNarrow64(EmitContext& ctx, IR::Inst* inst) { ctx.reg_alloc.DefineValue(code, inst, result); } else { - auto const a = ctx.reg_alloc.UseScratchXmm(code, args[0]); - auto const zeros = ctx.reg_alloc.ScratchXmm(code); - - code.pxor(zeros, zeros); - code.shufps(a, zeros, 0b00001000); - - ctx.reg_alloc.DefineValue(code, inst, a); + auto const a = ctx.reg_alloc.UseScratchXmm(code, args[0]); + auto const zeros = ctx.reg_alloc.ScratchXmm(code); + code.pxor(zeros, zeros); + code.shufps(a, zeros, 0b00001000); + ctx.reg_alloc.DefineValue(code, inst, a); } } @@ -2490,11 +2529,11 @@ void EmitX64::EmitVectorNot(EmitContext& ctx, IR::Inst* inst) { code.vpternlogq(result, operand, operand, u8(~Tern::c)); ctx.reg_alloc.DefineValue(code, inst, result); } else { - auto const xmm_a = ctx.reg_alloc.UseScratchXmm(code, args[0]); - auto const xmm_b = ctx.reg_alloc.ScratchXmm(code); - code.pcmpeqw(xmm_b, xmm_b); - code.pxor(xmm_a, xmm_b); - ctx.reg_alloc.DefineValue(code, inst, xmm_a); + auto const xmm_a = ctx.reg_alloc.UseScratchXmm(code, args[0]); + auto const xmm_b = ctx.reg_alloc.ScratchXmm(code); + code.pcmpeqw(xmm_b, xmm_b); + code.pxor(xmm_a, xmm_b); + ctx.reg_alloc.DefineValue(code, inst, xmm_a); } } @@ -2504,11 +2543,9 @@ void EmitX64::EmitVectorOr(EmitContext& ctx, IR::Inst* inst) { void EmitX64::EmitVectorPairedAddLower8(EmitContext& ctx, IR::Inst* inst) { auto args = ctx.reg_alloc.GetArgumentInfo(inst); - auto const xmm_a = ctx.reg_alloc.UseScratchXmm(code, args[0]); auto const xmm_b = ctx.reg_alloc.UseXmm(code, args[1]); auto const tmp = ctx.reg_alloc.ScratchXmm(code); - code.punpcklqdq(xmm_a, xmm_b); code.movdqa(tmp, xmm_a); code.psllw(xmm_a, 8); @@ -2516,7 +2553,6 @@ void EmitX64::EmitVectorPairedAddLower8(EmitContext& ctx, IR::Inst* inst) { code.pxor(tmp, tmp); code.psrlw(xmm_a, 8); code.packuswb(xmm_a, tmp); - ctx.reg_alloc.DefineValue(code, inst, xmm_a); } @@ -3553,7 +3589,7 @@ void EmitX64::EmitVectorPopulationCount(EmitContext& ctx, IR::Inst* inst) { EmitOneArgumentFallback(code, ctx, inst, [](VectorArray& result, const VectorArray& a) { std::transform(a.begin(), a.end(), result.begin(), [](u8 val) { - return static_cast(mcl::bit::count_ones(val)); + return static_cast(std::popcount(val)); }); }); } diff --git a/src/dynarmic/src/dynarmic/backend/x64/emit_x64_vector_floating_point.cpp b/src/dynarmic/src/dynarmic/backend/x64/emit_x64_vector_floating_point.cpp index 926653a920..68ffb2475b 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/emit_x64_vector_floating_point.cpp +++ b/src/dynarmic/src/dynarmic/backend/x64/emit_x64_vector_floating_point.cpp @@ -1657,20 +1657,10 @@ void EmitFPVectorRoundInt(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) { if constexpr (fsize != 16) { if (code.HasHostFeature(HostFeature::SSE41) && rounding != FP::RoundingMode::ToNearest_TieAwayFromZero && !exact) { - const u8 round_imm = [&]() -> u8 { - switch (rounding) { - case FP::RoundingMode::ToNearest_TieEven: return 0b00; - case FP::RoundingMode::TowardsPlusInfinity: return 0b10; - case FP::RoundingMode::TowardsMinusInfinity: return 0b01; - case FP::RoundingMode::TowardsZero: return 0b11; - default: UNREACHABLE(); - } - }(); - + const auto round_imm = ConvertRoundingModeToX64Immediate(rounding); EmitTwoOpVectorOperation(code, ctx, inst, [&](const Xbyak::Xmm& result, const Xbyak::Xmm& xmm_a) { - FCODE(roundp)(result, xmm_a, round_imm); + FCODE(roundp)(result, xmm_a, *round_imm); }); - return; } } @@ -2002,19 +1992,7 @@ void EmitFPVectorToFixed(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) { auto args = ctx.reg_alloc.GetArgumentInfo(inst); const Xbyak::Xmm src = ctx.reg_alloc.UseScratchXmm(code, args[0]); MaybeStandardFPSCRValue(code, ctx, fpcr_controlled, [&] { - const int round_imm = [&] { - switch (rounding) { - case FP::RoundingMode::ToNearest_TieEven: - default: - return 0b00; - case FP::RoundingMode::TowardsPlusInfinity: - return 0b10; - case FP::RoundingMode::TowardsMinusInfinity: - return 0b01; - case FP::RoundingMode::TowardsZero: - return 0b11; - } - }(); + const auto round_imm = ConvertRoundingModeToX64Immediate(rounding); const auto perform_conversion = [&code, &ctx](const Xbyak::Xmm& src) { // MSVC doesn't allow us to use a [&] capture, so we have to do this instead. (void)ctx; @@ -2046,7 +2024,7 @@ void EmitFPVectorToFixed(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) { FCODE(mulp)(src, GetVectorOf(code, scale_factor)); } - FCODE(roundp)(src, src, u8(round_imm)); + FCODE(roundp)(src, src, u8(*round_imm)); const Xbyak::Xmm nan_mask = xmm0; if (code.HasHostFeature(HostFeature::AVX512_OrthoFloat)) { static constexpr u32 nan_to_zero = FixupLUT(FpFixup::PosZero, FpFixup::PosZero); diff --git a/src/dynarmic/src/dynarmic/backend/x64/hostloc.h b/src/dynarmic/src/dynarmic/backend/x64/hostloc.h index 7191a0ceba..d62090d612 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/hostloc.h +++ b/src/dynarmic/src/dynarmic/backend/x64/hostloc.h @@ -8,8 +8,6 @@ #pragma once #include -#include - #include "common/assert.h" #include "common/common_types.h" #include "dynarmic/backend/x64/xbyak.h" diff --git a/src/dynarmic/src/dynarmic/backend/x64/xbyak.h b/src/dynarmic/src/dynarmic/backend/x64/xbyak.h index ee74794fb4..38401678e6 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/xbyak.h +++ b/src/dynarmic/src/dynarmic/backend/x64/xbyak.h @@ -3,13 +3,11 @@ #pragma once -#include -#include - -// TODO: Defining this crashes e v e r y t h i n g -// #define XBYAK_STD_UNORDERED_SET ankerl::unordered_dense::set -// #define XBYAK_STD_UNORDERED_MAP ankerl::unordered_dense::map -// #define XBYAK_STD_UNORDERED_MULTIMAP boost::unordered_multimap - +// You must ensure this matches with src/common/x64/xbyak.h on root dir +#include +#include +#define XBYAK_STD_UNORDERED_SET ankerl::unordered_dense::set +#define XBYAK_STD_UNORDERED_MAP ankerl::unordered_dense::map +#define XBYAK_STD_UNORDERED_MULTIMAP boost::unordered_multimap #include #include diff --git a/src/dynarmic/src/dynarmic/common/atomic.h b/src/dynarmic/src/dynarmic/common/atomic.h index 5eb4288517..8af6626be9 100644 --- a/src/dynarmic/src/dynarmic/common/atomic.h +++ b/src/dynarmic/src/dynarmic/common/atomic.h @@ -36,6 +36,14 @@ inline void And(volatile u32* ptr, u32 value) { #endif } +inline u32 Exchange(volatile u32* ptr, u32 value) { +#ifdef _MSC_VER + return static_cast(_InterlockedExchange(reinterpret_cast(ptr), value)); +#else + return __atomic_exchange_n(ptr, value, __ATOMIC_SEQ_CST); +#endif +} + inline void Barrier() { #ifdef _MSC_VER _ReadWriteBarrier(); diff --git a/src/dynarmic/src/dynarmic/common/context.h b/src/dynarmic/src/dynarmic/common/context.h index e849c9861b..db7af8e0ae 100644 --- a/src/dynarmic/src/dynarmic/common/context.h +++ b/src/dynarmic/src/dynarmic/common/context.h @@ -51,7 +51,25 @@ # endif #endif -#ifdef ARCHITECTURE_arm64 +// Some OSes define generic helper macros for all architectures +#if defined(__NetBSD__) +// NetBSD always has useful macros for everything don't they? +// Basically this special path means that for every arch that NetBSD supports +// it atleast has the macro for _UC_MACHINE defined, thus it will be avoided on the +// other macro branches! +// https://github.com/NetBSD/src/blob/trunk/sys/arch/powerpc/include/mcontext.h +// https://github.com/NetBSD/src/blob/trunk/sys/arch/mips/include/mcontext.h +# define CTX_PC (_UC_MACHINE_PC(ucontext)) +// NetBSD defines a redzone for SP but we don't require nor want that +// ((uc)->uc_mcontext.__gregs[_REG_RSP] - 128) -> this is not desirable due to calcs +#if defined(ARCHITECTURE_x86_64) +# define CTX_SP (mctx.__gregs[_REG_RSP]) +#else +# define CTX_SP (_UC_MACHINE_SP(ucontext)) +#endif +#endif + +#if defined(ARCHITECTURE_arm64) # if defined(__APPLE__) # define CTX_PC (mctx->__ss.__pc) # define CTX_SP (mctx->__ss.__sp) @@ -76,9 +94,7 @@ # define CTX_LR (mctx.mc_gpregs.gp_lr) # define CTX_X(i) (mctx.mc_gpregs.gp_x[i]) # define CTX_Q(i) (mctx.mc_fpregs.fp_q[i]) -# elif defined(__NetBSD__) -# define CTX_PC (mctx.mc_gpregs.gp_elr) -# define CTX_SP (mctx.mc_gpregs.gp_sp) +# elif defined(__NetBSD__) // SP + PC defined above # define CTX_LR (mctx.mc_gpregs.gp_lr) # define CTX_X(i) (mctx.mc_gpregs.gp_x[i]) # define CTX_Q(i) (mctx.mc_fpregs.fp_q[i]) @@ -92,35 +108,29 @@ # else # error "unknown platform" # endif -#elif defined(__NetBSD__) -// NetBSD always has useful macros for everything don't they? -// Basically this special path means that for every arch that NetBSD supports -// it atleast has the macro for _UC_MACHINE defined, thus it will be avoided on the -// other macro branches! -// https://github.com/NetBSD/src/blob/trunk/sys/arch/powerpc/include/mcontext.h -// https://github.com/NetBSD/src/blob/trunk/sys/arch/mips/include/mcontext.h -# define CTX_PC (_UC_MACHINE_PC(ucontext)) -# define CTX_SP (_UC_MACHINE_SP(ucontext)) #elif defined(ARCHITECTURE_x86_64) # if defined(__APPLE__) -# define CTX_RIP (mctx->__ss.__rip) -# define CTX_RSP (mctx->__ss.__rsp) +# define CTX_PC (mctx->__ss.__rip) +# define CTX_SP (mctx->__ss.__rsp) # elif defined(__linux__) -# define CTX_RIP (mctx.gregs[REG_RIP]) -# define CTX_RSP (mctx.gregs[REG_RSP]) +# define CTX_PC (mctx.gregs[REG_RIP]) +# define CTX_SP (mctx.gregs[REG_RSP]) # elif defined(__FreeBSD__) -# define CTX_RIP (mctx.mc_rip) -# define CTX_RSP (mctx.mc_rsp) +# define CTX_PC (mctx.mc_rip) +# define CTX_SP (mctx.mc_rsp) # elif defined(__OpenBSD__) // https://github.com/openbsd/src/blob/master/sys/arch/x86_64/include/signal.h -# define CTX_RIP (ucontext->sc_rip) -# define CTX_RSP (ucontext->sc_rsp) +# define CTX_PC (ucontext->sc_rip) +# define CTX_SP (ucontext->sc_rsp) # elif defined(__sun__) -# define CTX_RIP (mctx.gregs[REG_RIP]) -# define CTX_RSP (mctx.gregs[REG_RSP]) +# define CTX_PC (mctx.gregs[REG_RIP]) +# define CTX_SP (mctx.gregs[REG_RSP]) # elif defined(__DragonFly__) -# define CTX_RIP (mctx.mc_rip) -# define CTX_RSP (mctx.mc_rsp) +# define CTX_PC (mctx.mc_rip) +# define CTX_SP (mctx.mc_rsp) +# elif defined(__managarm__) +# define CTX_PC (mctx.__pollution(gregs)[REG_RIP]) +# define CTX_SP (mctx.__pollution(gregs)[REG_RSP]) # else # error "unknown platform" # endif diff --git a/src/dynarmic/src/dynarmic/common/fp/op/FPToFixed.cpp b/src/dynarmic/src/dynarmic/common/fp/op/FPToFixed.cpp index d920f2d900..7c322050c3 100644 --- a/src/dynarmic/src/dynarmic/common/fp/op/FPToFixed.cpp +++ b/src/dynarmic/src/dynarmic/common/fp/op/FPToFixed.cpp @@ -78,7 +78,7 @@ u64 FPToFixed(size_t ibits, FPT op, size_t fbits, bool unsigned_, FPCR fpcr, Rou } // Detect Overflow - const int min_exponent_for_overflow = static_cast(ibits) - static_cast(mcl::bit::highest_set_bit(value.mantissa + (round_up ? Safe::LogicalShiftRight(1, exponent) : 0))) - (unsigned_ ? 0 : 1); + const int min_exponent_for_overflow = int(ibits) - int(mcl::bit::highest_set_bit(value.mantissa + (round_up ? Safe::LogicalShiftRight(1, exponent) : 0))) - (unsigned_ ? 0 : 1); if (exponent >= min_exponent_for_overflow) { // Positive overflow if (unsigned_ || !sign) { @@ -87,10 +87,10 @@ u64 FPToFixed(size_t ibits, FPT op, size_t fbits, bool unsigned_, FPCR fpcr, Rou } // Negative overflow - const u64 min_value = Safe::Negate(static_cast(1) << (ibits - 1)); + const u64 min_value = Safe::Negate(u64(1) << (ibits - 1)); if (!(exponent == min_exponent_for_overflow && int_result == min_value)) { FPProcessException(FPExc::InvalidOp, fpcr, fpsr); - return static_cast(1) << (ibits - 1); + return u64(1) << (ibits - 1); } } diff --git a/src/dynarmic/src/dynarmic/common/llvm_disassemble.cpp b/src/dynarmic/src/dynarmic/common/llvm_disassemble.cpp index 6aff6d6cc7..6896b43d62 100644 --- a/src/dynarmic/src/dynarmic/common/llvm_disassemble.cpp +++ b/src/dynarmic/src/dynarmic/common/llvm_disassemble.cpp @@ -7,7 +7,6 @@ */ #include - #include #ifdef DYNARMIC_USE_LLVM @@ -16,7 +15,6 @@ #endif #include "common/assert.h" -#include #include "common/common_types.h" #include "dynarmic/common/llvm_disassemble.h" diff --git a/src/dynarmic/src/dynarmic/common/spin_lock_loongarch64.cpp b/src/dynarmic/src/dynarmic/common/spin_lock_loongarch64.cpp new file mode 100644 index 0000000000..6efd05bc26 --- /dev/null +++ b/src/dynarmic/src/dynarmic/common/spin_lock_loongarch64.cpp @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "dynarmic/common/spin_lock.h" + +namespace Dynarmic { + +void SpinLock::Lock() noexcept { +} + +void SpinLock::Unlock() noexcept { +} + +} // namespace Dynarmic diff --git a/src/dynarmic/src/dynarmic/common/spin_lock_x64.cpp b/src/dynarmic/src/dynarmic/common/spin_lock_x64.cpp index e3f1721132..f1898a92ef 100644 --- a/src/dynarmic/src/dynarmic/common/spin_lock_x64.cpp +++ b/src/dynarmic/src/dynarmic/common/spin_lock_x64.cpp @@ -26,6 +26,7 @@ void EmitSpinLockLock(Xbyak::CodeGenerator& code, Xbyak::Reg64 ptr, Xbyak::Reg32 Xbyak::Label start, loop; code.jmp(start, code.T_NEAR); code.L(loop); + if (waitpkg) { // TODO: this is because we lack regalloc - so better to be safe :( code.push(Xbyak::util::rax); @@ -33,7 +34,14 @@ void EmitSpinLockLock(Xbyak::CodeGenerator& code, Xbyak::Reg64 ptr, Xbyak::Reg32 code.push(Xbyak::util::rdx); // TODO: This clobbers EAX and EDX did we tell the regalloc? // ARM ptr for address-monitoring - code.umonitor(ptr); + + // XBYAK BUG: code.umonitor(ptr); see issue #255 + // replace once xbyak has been fixed + code.db(0xF3); + if (ptr.getIdx() >= 8) code.db(0x41); + code.db(0x0F); code.db(0xAE); + code.db(uint8_t((3 << 6) | ((6 & 7) << 3) | (ptr.getIdx() & 7))); + // tmp.bit[0] = 0: C0.1 | Slow Wakup | Better Savings // tmp.bit[0] = 1: C0.2 | Fast Wakup | Lesser Savings // edx:eax is implicitly used as a 64-bit deadline timestamp @@ -82,6 +90,8 @@ std::once_flag flag; std::optional impl; void SpinLockImpl::Initialize() noexcept { + // Needed for W^X systems (i.e SElinux, OpenBSD) + code.setProtectMode(Xbyak::CodeArray::ProtectMode::PROTECT_RW); Xbyak::Reg64 const ABI_PARAM1 = Backend::X64::HostLocToReg64(Backend::X64::ABI_PARAM1); code.align(); lock = code.getCurr(); @@ -91,6 +101,7 @@ void SpinLockImpl::Initialize() noexcept { unlock = code.getCurr(); EmitSpinLockUnlock(code, ABI_PARAM1, code.eax); code.ret(); + code.setProtectMode(Xbyak::CodeArray::ProtectMode::PROTECT_RE); } void SpinLockImpl::GlobalInitialize() noexcept { diff --git a/src/dynarmic/src/dynarmic/frontend/A32/translate/impl/a32_translate_impl.h b/src/dynarmic/src/dynarmic/frontend/A32/translate/impl/a32_translate_impl.h index 82e919720e..dd88b79237 100644 --- a/src/dynarmic/src/dynarmic/frontend/A32/translate/impl/a32_translate_impl.h +++ b/src/dynarmic/src/dynarmic/frontend/A32/translate/impl/a32_translate_impl.h @@ -52,7 +52,7 @@ struct TranslatorVisitor final { u32 imm32 = imm8.ZeroExtend(); auto carry_out = carry_in; if (rotate) { - imm32 = mcl::bit::rotate_right(imm8.ZeroExtend(), rotate * 2); + imm32 = std::rotr(imm8.ZeroExtend(), rotate * 2); carry_out = ir.Imm1(mcl::bit::get_bit<31>(imm32)); } return {imm32, carry_out}; @@ -81,7 +81,7 @@ struct TranslatorVisitor final { }(); return {imm32, carry_in}; } - const u32 imm32 = mcl::bit::rotate_right((1 << 7) | imm12.Bits<0, 6>(), imm12.Bits<7, 11>()); + const u32 imm32 = std::rotr((1 << 7) | imm12.Bits<0, 6>(), imm12.Bits<7, 11>()); return {imm32, ir.Imm1(mcl::bit::get_bit<31>(imm32))}; } diff --git a/src/dynarmic/src/dynarmic/frontend/A32/translate/impl/asimd_misc.cpp b/src/dynarmic/src/dynarmic/frontend/A32/translate/impl/asimd_misc.cpp index 6ccfe1e3bc..d255ad8b2c 100644 --- a/src/dynarmic/src/dynarmic/frontend/A32/translate/impl/asimd_misc.cpp +++ b/src/dynarmic/src/dynarmic/frontend/A32/translate/impl/asimd_misc.cpp @@ -79,7 +79,7 @@ bool TranslatorVisitor::asimd_VDUP_scalar(bool D, Imm<4> imm4, size_t Vd, bool Q return UndefinedInstruction(); } - const size_t imm4_lsb = mcl::bit::lowest_set_bit(imm4.ZeroExtend()); + const size_t imm4_lsb = std::countr_zero(imm4.ZeroExtend()); const size_t esize = 8u << imm4_lsb; const size_t index = imm4.ZeroExtend() >> (imm4_lsb + 1); const auto d = ToVector(Q, Vd, D); diff --git a/src/dynarmic/src/dynarmic/frontend/A32/translate/impl/load_store.cpp b/src/dynarmic/src/dynarmic/frontend/A32/translate/impl/load_store.cpp index d7c667aecf..208e7d1122 100644 --- a/src/dynarmic/src/dynarmic/frontend/A32/translate/impl/load_store.cpp +++ b/src/dynarmic/src/dynarmic/frontend/A32/translate/impl/load_store.cpp @@ -799,7 +799,7 @@ static bool LDMHelper(A32::IREmitter& ir, bool W, Reg n, RegList list, IR::U32 s // LDM {!}, bool TranslatorVisitor::arm_LDM(Cond cond, bool W, Reg n, RegList list) { - if (n == Reg::PC || mcl::bit::count_ones(list) < 1) { + if (n == Reg::PC || std::popcount(list) < 1) { return UnpredictableInstruction(); } if (W && mcl::bit::get_bit(static_cast(n), list)) { @@ -811,13 +811,13 @@ bool TranslatorVisitor::arm_LDM(Cond cond, bool W, Reg n, RegList list) { } const auto start_address = ir.GetRegister(n); - const auto writeback_address = ir.Add(start_address, ir.Imm32(u32(mcl::bit::count_ones(list) * 4))); + const auto writeback_address = ir.Add(start_address, ir.Imm32(u32(std::popcount(list) * 4))); return LDMHelper(ir, W, n, list, start_address, writeback_address); } // LDMDA {!}, bool TranslatorVisitor::arm_LDMDA(Cond cond, bool W, Reg n, RegList list) { - if (n == Reg::PC || mcl::bit::count_ones(list) < 1) { + if (n == Reg::PC || std::popcount(list) < 1) { return UnpredictableInstruction(); } if (W && mcl::bit::get_bit(static_cast(n), list)) { @@ -828,14 +828,14 @@ bool TranslatorVisitor::arm_LDMDA(Cond cond, bool W, Reg n, RegList list) { return true; } - const auto start_address = ir.Sub(ir.GetRegister(n), ir.Imm32(u32(4 * mcl::bit::count_ones(list) - 4))); + const auto start_address = ir.Sub(ir.GetRegister(n), ir.Imm32(u32(4 * std::popcount(list) - 4))); const auto writeback_address = ir.Sub(start_address, ir.Imm32(4)); return LDMHelper(ir, W, n, list, start_address, writeback_address); } // LDMDB {!}, bool TranslatorVisitor::arm_LDMDB(Cond cond, bool W, Reg n, RegList list) { - if (n == Reg::PC || mcl::bit::count_ones(list) < 1) { + if (n == Reg::PC || std::popcount(list) < 1) { return UnpredictableInstruction(); } if (W && mcl::bit::get_bit(static_cast(n), list)) { @@ -846,14 +846,14 @@ bool TranslatorVisitor::arm_LDMDB(Cond cond, bool W, Reg n, RegList list) { return true; } - const auto start_address = ir.Sub(ir.GetRegister(n), ir.Imm32(u32(4 * mcl::bit::count_ones(list)))); + const auto start_address = ir.Sub(ir.GetRegister(n), ir.Imm32(u32(4 * std::popcount(list)))); const auto writeback_address = start_address; return LDMHelper(ir, W, n, list, start_address, writeback_address); } // LDMIB {!}, bool TranslatorVisitor::arm_LDMIB(Cond cond, bool W, Reg n, RegList list) { - if (n == Reg::PC || mcl::bit::count_ones(list) < 1) { + if (n == Reg::PC || std::popcount(list) < 1) { return UnpredictableInstruction(); } if (W && mcl::bit::get_bit(static_cast(n), list)) { @@ -865,7 +865,7 @@ bool TranslatorVisitor::arm_LDMIB(Cond cond, bool W, Reg n, RegList list) { } const auto start_address = ir.Add(ir.GetRegister(n), ir.Imm32(4)); - const auto writeback_address = ir.Add(ir.GetRegister(n), ir.Imm32(u32(4 * mcl::bit::count_ones(list)))); + const auto writeback_address = ir.Add(ir.GetRegister(n), ir.Imm32(u32(4 * std::popcount(list)))); return LDMHelper(ir, W, n, list, start_address, writeback_address); } @@ -896,7 +896,7 @@ static bool STMHelper(A32::IREmitter& ir, bool W, Reg n, RegList list, IR::U32 s // STM {!}, bool TranslatorVisitor::arm_STM(Cond cond, bool W, Reg n, RegList list) { - if (n == Reg::PC || mcl::bit::count_ones(list) < 1) { + if (n == Reg::PC || std::popcount(list) < 1) { return UnpredictableInstruction(); } @@ -905,13 +905,13 @@ bool TranslatorVisitor::arm_STM(Cond cond, bool W, Reg n, RegList list) { } const auto start_address = ir.GetRegister(n); - const auto writeback_address = ir.Add(start_address, ir.Imm32(u32(mcl::bit::count_ones(list) * 4))); + const auto writeback_address = ir.Add(start_address, ir.Imm32(u32(std::popcount(list) * 4))); return STMHelper(ir, W, n, list, start_address, writeback_address); } // STMDA {!}, bool TranslatorVisitor::arm_STMDA(Cond cond, bool W, Reg n, RegList list) { - if (n == Reg::PC || mcl::bit::count_ones(list) < 1) { + if (n == Reg::PC || std::popcount(list) < 1) { return UnpredictableInstruction(); } @@ -919,14 +919,14 @@ bool TranslatorVisitor::arm_STMDA(Cond cond, bool W, Reg n, RegList list) { return true; } - const auto start_address = ir.Sub(ir.GetRegister(n), ir.Imm32(u32(4 * mcl::bit::count_ones(list) - 4))); + const auto start_address = ir.Sub(ir.GetRegister(n), ir.Imm32(u32(4 * std::popcount(list) - 4))); const auto writeback_address = ir.Sub(start_address, ir.Imm32(4)); return STMHelper(ir, W, n, list, start_address, writeback_address); } // STMDB {!}, bool TranslatorVisitor::arm_STMDB(Cond cond, bool W, Reg n, RegList list) { - if (n == Reg::PC || mcl::bit::count_ones(list) < 1) { + if (n == Reg::PC || std::popcount(list) < 1) { return UnpredictableInstruction(); } @@ -934,14 +934,14 @@ bool TranslatorVisitor::arm_STMDB(Cond cond, bool W, Reg n, RegList list) { return true; } - const auto start_address = ir.Sub(ir.GetRegister(n), ir.Imm32(u32(4 * mcl::bit::count_ones(list)))); + const auto start_address = ir.Sub(ir.GetRegister(n), ir.Imm32(u32(4 * std::popcount(list)))); const auto writeback_address = start_address; return STMHelper(ir, W, n, list, start_address, writeback_address); } // STMIB {!}, bool TranslatorVisitor::arm_STMIB(Cond cond, bool W, Reg n, RegList list) { - if (n == Reg::PC || mcl::bit::count_ones(list) < 1) { + if (n == Reg::PC || std::popcount(list) < 1) { return UnpredictableInstruction(); } @@ -950,7 +950,7 @@ bool TranslatorVisitor::arm_STMIB(Cond cond, bool W, Reg n, RegList list) { } const auto start_address = ir.Add(ir.GetRegister(n), ir.Imm32(4)); - const auto writeback_address = ir.Add(ir.GetRegister(n), ir.Imm32(u32(4 * mcl::bit::count_ones(list)))); + const auto writeback_address = ir.Add(ir.GetRegister(n), ir.Imm32(u32(4 * std::popcount(list)))); return STMHelper(ir, W, n, list, start_address, writeback_address); } diff --git a/src/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb16.cpp b/src/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb16.cpp index a8c75e22b9..082cd30db2 100644 --- a/src/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb16.cpp +++ b/src/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb16.cpp @@ -688,7 +688,7 @@ bool TranslatorVisitor::thumb16_NOP() { // IT{{{}}} bool TranslatorVisitor::thumb16_IT(Imm<8> imm8) { ASSERT((imm8.Bits<0, 3>() != 0b0000) && "Decode Error"); - if (imm8.Bits<4, 7>() == 0b1111 || (imm8.Bits<4, 7>() == 0b1110 && mcl::bit::count_ones(imm8.Bits<0, 3>()) != 1)) { + if (imm8.Bits<4, 7>() == 0b1111 || (imm8.Bits<4, 7>() == 0b1110 && std::popcount(imm8.Bits<0, 3>()) != 1)) { return UnpredictableInstruction(); } if (ir.current_location.IT().IsInITBlock()) { @@ -738,11 +738,11 @@ bool TranslatorVisitor::thumb16_PUSH(bool M, RegList reg_list) { if (M) { reg_list |= 1 << 14; } - if (mcl::bit::count_ones(reg_list) < 1) { + if (std::popcount(reg_list) < 1) { return UnpredictableInstruction(); } - const u32 num_bytes_to_push = static_cast(4 * mcl::bit::count_ones(reg_list)); + const u32 num_bytes_to_push = static_cast(4 * std::popcount(reg_list)); const auto final_address = ir.Sub(ir.GetRegister(Reg::SP), ir.Imm32(num_bytes_to_push)); auto address = final_address; for (size_t i = 0; i < 16; i++) { @@ -764,7 +764,7 @@ bool TranslatorVisitor::thumb16_POP(bool P, RegList reg_list) { if (P) { reg_list |= 1 << 15; } - if (mcl::bit::count_ones(reg_list) < 1) { + if (std::popcount(reg_list) < 1) { return UnpredictableInstruction(); } @@ -851,10 +851,10 @@ bool TranslatorVisitor::thumb16_BKPT(Imm<8> /*imm8*/) { // STM !, bool TranslatorVisitor::thumb16_STMIA(Reg n, RegList reg_list) { - if (mcl::bit::count_ones(reg_list) == 0) { + if (std::popcount(reg_list) == 0) { return UnpredictableInstruction(); } - if (mcl::bit::get_bit(static_cast(n), reg_list) && n != static_cast(mcl::bit::lowest_set_bit(reg_list))) { + if (mcl::bit::get_bit(static_cast(n), reg_list) && n != static_cast(std::countr_zero(reg_list))) { return UnpredictableInstruction(); } @@ -873,7 +873,7 @@ bool TranslatorVisitor::thumb16_STMIA(Reg n, RegList reg_list) { // LDM !, bool TranslatorVisitor::thumb16_LDMIA(Reg n, RegList reg_list) { - if (mcl::bit::count_ones(reg_list) == 0) { + if (std::popcount(reg_list) == 0) { return UnpredictableInstruction(); } diff --git a/src/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_load_store_multiple.cpp b/src/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_load_store_multiple.cpp index b68a2cb7c5..e829c6fb42 100644 --- a/src/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_load_store_multiple.cpp +++ b/src/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_load_store_multiple.cpp @@ -53,7 +53,7 @@ static bool STMHelper(A32::IREmitter& ir, bool W, Reg n, u32 list, const IR::U32 bool TranslatorVisitor::thumb32_LDMDB(bool W, Reg n, Imm<16> reg_list) { const auto regs_imm = reg_list.ZeroExtend(); - const auto num_regs = static_cast(mcl::bit::count_ones(regs_imm)); + const auto num_regs = static_cast(std::popcount(regs_imm)); if (n == Reg::PC || num_regs < 2) { return UnpredictableInstruction(); @@ -78,7 +78,7 @@ bool TranslatorVisitor::thumb32_LDMDB(bool W, Reg n, Imm<16> reg_list) { bool TranslatorVisitor::thumb32_LDMIA(bool W, Reg n, Imm<16> reg_list) { const auto regs_imm = reg_list.ZeroExtend(); - const auto num_regs = static_cast(mcl::bit::count_ones(regs_imm)); + const auto num_regs = static_cast(std::popcount(regs_imm)); if (n == Reg::PC || num_regs < 2) { return UnpredictableInstruction(); @@ -111,7 +111,7 @@ bool TranslatorVisitor::thumb32_PUSH(Imm<15> reg_list) { bool TranslatorVisitor::thumb32_STMIA(bool W, Reg n, Imm<15> reg_list) { const auto regs_imm = reg_list.ZeroExtend(); - const auto num_regs = static_cast(mcl::bit::count_ones(regs_imm)); + const auto num_regs = static_cast(std::popcount(regs_imm)); if (n == Reg::PC || num_regs < 2) { return UnpredictableInstruction(); @@ -130,7 +130,7 @@ bool TranslatorVisitor::thumb32_STMIA(bool W, Reg n, Imm<15> reg_list) { bool TranslatorVisitor::thumb32_STMDB(bool W, Reg n, Imm<15> reg_list) { const auto regs_imm = reg_list.ZeroExtend(); - const auto num_regs = static_cast(mcl::bit::count_ones(regs_imm)); + const auto num_regs = static_cast(std::popcount(regs_imm)); if (n == Reg::PC || num_regs < 2) { return UnpredictableInstruction(); diff --git a/src/dynarmic/src/dynarmic/frontend/A64/translate/impl/impl.cpp b/src/dynarmic/src/dynarmic/frontend/A64/translate/impl/impl.cpp index f3ecd7c604..998fa4c13b 100644 --- a/src/dynarmic/src/dynarmic/frontend/A64/translate/impl/impl.cpp +++ b/src/dynarmic/src/dynarmic/frontend/A64/translate/impl/impl.cpp @@ -55,7 +55,7 @@ std::optional TranslatorVisitor::DecodeBitMasks(boo const size_t esize = size_t{1} << len; const u64 welem = mcl::bit::ones(S + 1); const u64 telem = mcl::bit::ones(d + 1); - const u64 wmask = mcl::bit::rotate_right(mcl::bit::replicate_element(esize, welem), R); + const u64 wmask = std::rotr(mcl::bit::replicate_element(esize, welem), R); const u64 tmask = mcl::bit::replicate_element(esize, telem); return BitMasks{wmask, tmask}; diff --git a/src/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_copy.cpp b/src/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_copy.cpp index 9354b54fa5..0d6ec29417 100644 --- a/src/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_copy.cpp +++ b/src/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_copy.cpp @@ -13,7 +13,7 @@ namespace Dynarmic::A64 { bool TranslatorVisitor::DUP_elt_1(Imm<5> imm5, Vec Vn, Vec Vd) { - const size_t size = mcl::bit::lowest_set_bit(imm5.ZeroExtend()); + const size_t size = std::countr_zero(imm5.ZeroExtend()); if (size > 3) { return ReservedValue(); } @@ -30,7 +30,7 @@ bool TranslatorVisitor::DUP_elt_1(Imm<5> imm5, Vec Vn, Vec Vd) { } bool TranslatorVisitor::DUP_elt_2(bool Q, Imm<5> imm5, Vec Vn, Vec Vd) { - const size_t size = mcl::bit::lowest_set_bit(imm5.ZeroExtend()); + const size_t size = std::countr_zero(imm5.ZeroExtend()); if (size > 3) { return ReservedValue(); } @@ -51,7 +51,7 @@ bool TranslatorVisitor::DUP_elt_2(bool Q, Imm<5> imm5, Vec Vn, Vec Vd) { } bool TranslatorVisitor::DUP_gen(bool Q, Imm<5> imm5, Reg Rn, Vec Vd) { - const size_t size = mcl::bit::lowest_set_bit(imm5.ZeroExtend()); + const size_t size = std::countr_zero(imm5.ZeroExtend()); if (size > 3) { return ReservedValue(); } @@ -73,7 +73,7 @@ bool TranslatorVisitor::DUP_gen(bool Q, Imm<5> imm5, Reg Rn, Vec Vd) { } bool TranslatorVisitor::SMOV(bool Q, Imm<5> imm5, Vec Vn, Reg Rd) { - const size_t size = mcl::bit::lowest_set_bit(imm5.ZeroExtend()); + const size_t size = std::countr_zero(imm5.ZeroExtend()); if (size == 2 && !Q) { return UnallocatedEncoding(); } @@ -96,7 +96,7 @@ bool TranslatorVisitor::SMOV(bool Q, Imm<5> imm5, Vec Vn, Reg Rd) { } bool TranslatorVisitor::UMOV(bool Q, Imm<5> imm5, Vec Vn, Reg Rd) { - const size_t size = mcl::bit::lowest_set_bit(imm5.ZeroExtend()); + const size_t size = std::countr_zero(imm5.ZeroExtend()); if (size < 3 && Q) { return UnallocatedEncoding(); } @@ -123,7 +123,7 @@ bool TranslatorVisitor::UMOV(bool Q, Imm<5> imm5, Vec Vn, Reg Rd) { } bool TranslatorVisitor::INS_gen(Imm<5> imm5, Reg Rn, Vec Vd) { - const size_t size = mcl::bit::lowest_set_bit(imm5.ZeroExtend()); + const size_t size = std::countr_zero(imm5.ZeroExtend()); if (size > 3) { return ReservedValue(); } @@ -140,7 +140,7 @@ bool TranslatorVisitor::INS_gen(Imm<5> imm5, Reg Rn, Vec Vd) { } bool TranslatorVisitor::INS_elt(Imm<5> imm5, Imm<4> imm4, Vec Vn, Vec Vd) { - const size_t size = mcl::bit::lowest_set_bit(imm5.ZeroExtend()); + const size_t size = std::countr_zero(imm5.ZeroExtend()); if (size > 3) { return ReservedValue(); } diff --git a/src/dynarmic/src/dynarmic/frontend/decoder/matcher.h b/src/dynarmic/src/dynarmic/frontend/decoder/matcher.h index 49095f67e3..77fb7d941f 100644 --- a/src/dynarmic/src/dynarmic/frontend/decoder/matcher.h +++ b/src/dynarmic/src/dynarmic/frontend/decoder/matcher.h @@ -9,6 +9,7 @@ #pragma once #include +#include #include "common/assert.h" @@ -31,6 +32,10 @@ public: , expected{expected} {} + constexpr Matcher(std::tuple t) noexcept + : Matcher(std::get<0>(t), std::get<1>(t)) + {} + /// @brief Gets the mask for this instruction. constexpr inline T GetMask() const noexcept { return mask; diff --git a/src/dynarmic/src/dynarmic/ir/opt_passes.cpp b/src/dynarmic/src/dynarmic/ir/opt_passes.cpp index 999d4c49bc..fb51ba4998 100644 --- a/src/dynarmic/src/dynarmic/ir/opt_passes.cpp +++ b/src/dynarmic/src/dynarmic/ir/opt_passes.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include "boost/container/small_vector.hpp" @@ -28,7 +29,6 @@ #include "dynarmic/ir/opt_passes.h" #include "dynarmic/ir/type.h" #include "dynarmic/mcl/bit.hpp" -#include "dynarmic/mcl/bit.hpp" namespace Dynarmic::Optimization { @@ -1074,12 +1074,12 @@ static void ConstantPropagation(IR::Block& block) { break; case Op::BitRotateRight32: if (FoldShifts(inst)) { - ReplaceUsesWith(inst, true, mcl::bit::rotate_right(inst.GetArg(0).GetU32(), inst.GetArg(1).GetU8())); + ReplaceUsesWith(inst, true, std::rotr(inst.GetArg(0).GetU32(), inst.GetArg(1).GetU8())); } break; case Op::BitRotateRight64: if (FoldShifts(inst)) { - ReplaceUsesWith(inst, false, mcl::bit::rotate_right(inst.GetArg(0).GetU64(), inst.GetArg(1).GetU8())); + ReplaceUsesWith(inst, false, std::rotr(inst.GetArg(0).GetU64(), inst.GetArg(1).GetU8())); } break; case Op::LogicalShiftLeftMasked32: @@ -1114,12 +1114,12 @@ static void ConstantPropagation(IR::Block& block) { break; case Op::RotateRightMasked32: if (inst.AreAllArgsImmediates()) { - ReplaceUsesWith(inst, true, mcl::bit::rotate_right(inst.GetArg(0).GetU32(), inst.GetArg(1).GetU32())); + ReplaceUsesWith(inst, true, std::rotr(inst.GetArg(0).GetU32(), inst.GetArg(1).GetU32())); } break; case Op::RotateRightMasked64: if (inst.AreAllArgsImmediates()) { - ReplaceUsesWith(inst, false, mcl::bit::rotate_right(inst.GetArg(0).GetU64(), inst.GetArg(1).GetU64())); + ReplaceUsesWith(inst, false, std::rotr(inst.GetArg(0).GetU64(), inst.GetArg(1).GetU64())); } break; case Op::Add32: diff --git a/src/dynarmic/src/dynarmic/mcl/bit.hpp b/src/dynarmic/src/dynarmic/mcl/bit.hpp index bbedffd8ae..ea4d6c057f 100644 --- a/src/dynarmic/src/dynarmic/mcl/bit.hpp +++ b/src/dynarmic/src/dynarmic/mcl/bit.hpp @@ -8,26 +8,15 @@ #include #include #include +#include +#include #include "common/common_types.h" #include "common/assert.h" namespace mcl { -namespace detail { -template -concept SameHelper = std::is_same_v; -} // namespace detail -template -concept SameAs = detail::SameHelper && detail::SameHelper; -template -concept IsAnyOf = (SameAs || ...); -/// Integral upon which bit operations can be safely performed. -template -concept BitIntegral = IsAnyOf; - template constexpr std::size_t bitsizeof = CHAR_BIT * sizeof(T); - } // namespace mcl namespace mcl::bit { @@ -71,58 +60,37 @@ constexpr u64 swap_words_64(u64 value) { | ((value & 0x00000000ffffffffull) << 32); } -template -constexpr T rotate_right(T x, size_t amount) { - amount %= bitsizeof; - if (amount == 0) { - return x; - } - return static_cast((x >> amount) | (x << (bitsizeof - amount))); -} - -template -constexpr T rotate_left(T x, size_t amount) { - amount %= bitsizeof; - if (amount == 0) { - return x; - } - return static_cast((x << amount) | (x >> (bitsizeof - amount))); -} - /// Create a mask with `count` number of one bits. -template +template constexpr T ones() { static_assert(count <= bitsizeof, "count larger than bitsize of T"); - if constexpr (count == 0) { return 0; } else { - return static_cast(~static_cast(0)) >> (bitsizeof - count); + return T(~T(0)) >> (bitsizeof - count); } } /// Create a mask with `count` number of one bits. -template +template constexpr T ones(size_t count) { ASSERT(count <= bitsizeof && "count larger than bitsize of T"); - if (count == 0) { + if (count == 0) return 0; - } - return static_cast(~static_cast(0)) >> (bitsizeof - count); + return T(~T(0)) >> (bitsizeof - count); } /// Create a mask of type T for bits [begin_bit, end_bit] inclusive. -template +template constexpr T mask() { static_assert(begin_bit <= end_bit, "invalid bit range (position of beginning bit cannot be greater than that of end bit)"); static_assert(begin_bit < bitsizeof, "begin_bit must be smaller than size of T"); static_assert(end_bit < bitsizeof, "end_bit must be smaller than size of T"); - return ones() << begin_bit; } /// Create a mask of type T for bits [begin_bit, end_bit] inclusive. -template +template constexpr T mask(size_t begin_bit, size_t end_bit) { ASSERT(begin_bit <= end_bit && "invalid bit range (position of beginning bit cannot be greater than that of end bit)"); ASSERT(begin_bit < bitsizeof && "begin_bit must be smaller than size of T"); @@ -131,101 +99,100 @@ constexpr T mask(size_t begin_bit, size_t end_bit) { } /// Extract bits [begin_bit, end_bit] inclusive from value of type T. -template +template constexpr T get_bits(T value) { constexpr T m = mask(); return (value & m) >> begin_bit; } /// Extract bits [begin_bit, end_bit] inclusive from value of type T. -template +template constexpr T get_bits(size_t begin_bit, size_t end_bit, T value) { const T m = mask(begin_bit, end_bit); return (value & m) >> begin_bit; } /// Clears bits [begin_bit, end_bit] inclusive of value of type T. -template +template constexpr T clear_bits(T value) { constexpr T m = mask(); return value & ~m; } /// Clears bits [begin_bit, end_bit] inclusive of value of type T. -template +template constexpr T clear_bits(size_t begin_bit, size_t end_bit, T value) { const T m = mask(begin_bit, end_bit); return value & ~m; } /// Modifies bits [begin_bit, end_bit] inclusive of value of type T. -template +template constexpr T set_bits(T value, T new_bits) { constexpr T m = mask(); return (value & ~m) | ((new_bits << begin_bit) & m); } /// Modifies bits [begin_bit, end_bit] inclusive of value of type T. -template +template constexpr T set_bits(size_t begin_bit, size_t end_bit, T value, T new_bits) { const T m = mask(begin_bit, end_bit); return (value & ~m) | ((new_bits << begin_bit) & m); } /// Extract bit at bit_position from value of type T. -template +template constexpr bool get_bit(T value) { constexpr T m = mask(); return (value & m) != 0; } /// Extract bit at bit_position from value of type T. -template +template constexpr bool get_bit(size_t bit_position, T value) { const T m = mask(bit_position, bit_position); return (value & m) != 0; } /// Clears bit at bit_position of value of type T. -template +template constexpr T clear_bit(T value) { constexpr T m = mask(); return value & ~m; } /// Clears bit at bit_position of value of type T. -template +template constexpr T clear_bit(size_t bit_position, T value) { const T m = mask(bit_position, bit_position); return value & ~m; } /// Modifies bit at bit_position of value of type T. -template +template constexpr T set_bit(T value, bool new_bit) { constexpr T m = mask(); - return (value & ~m) | (new_bit ? m : static_cast(0)); + return (value & ~m) | (new_bit ? m : T(0)); } /// Modifies bit at bit_position of value of type T. -template +template constexpr T set_bit(size_t bit_position, T value, bool new_bit) { const T m = mask(bit_position, bit_position); - return (value & ~m) | (new_bit ? m : static_cast(0)); + return (value & ~m) | (new_bit ? m : T(0)); } /// Sign-extends a value that has bit_count bits to the full bitwidth of type T. -template +template constexpr T sign_extend(T value) { static_assert(bit_count != 0, "cannot sign-extend zero-sized value"); - using S = std::make_signed_t; constexpr size_t shift_amount = bitsizeof - bit_count; - return static_cast(static_cast(value << shift_amount) >> shift_amount); + return T(S(value << shift_amount) >> shift_amount); } /// Sign-extends a value that has bit_count bits to the full bitwidth of type T. -template +template constexpr T sign_extend(size_t bit_count, T value) { ASSERT(bit_count != 0 && "cannot sign-extend zero-sized value"); using S = std::make_signed_t; @@ -234,78 +201,42 @@ constexpr T sign_extend(size_t bit_count, T value) { } /// Replicate an element across a value of type T. -template +template constexpr T replicate_element(T value) { static_assert(element_size <= bitsizeof, "element_size is too large"); static_assert(bitsizeof % element_size == 0, "bitsize of T not divisible by element_size"); - if constexpr (element_size == bitsizeof) { return value; } else { - return replicate_element(static_cast(value | (value << element_size))); + return replicate_element(T(value | (value << element_size))); } } /// Replicate an element of type U across a value of type T. -template +template constexpr T replicate_element(T value) { static_assert(bitsizeof <= bitsizeof, "element_size is too large"); - return replicate_element, T>(value); } /// Replicate an element across a value of type T. -template +template constexpr T replicate_element(size_t element_size, T value) { ASSERT(element_size <= bitsizeof && "element_size is too large"); ASSERT(bitsizeof % element_size == 0 && "bitsize of T not divisible by element_size"); if (element_size == bitsizeof) return value; - return replicate_element(element_size * 2, static_cast(value | (value << element_size))); + return replicate_element(element_size * 2, T(value | (value << element_size))); } -template +template constexpr bool most_significant_bit(T value) { return get_bit - 1, T>(value); } -template -inline size_t count_ones(T x) { - return std::bitset>(x).count(); -} - -template -constexpr size_t count_leading_zeros(T x) { - size_t result = bitsizeof; - while (x != 0) { - x >>= 1; - result--; - } - return result; -} - -template +template constexpr int highest_set_bit(T x) { - int result = -1; - while (x != 0) { - x >>= 1; - result++; - } - return result; -} - -template -constexpr size_t lowest_set_bit(T x) { - if (x == 0) { - return bitsizeof; - } - - size_t result = 0; - while ((x & 1) == 0) { - x >>= 1; - result++; - } - return result; + return std::bit_width(x) - 1; } } // namespace mcl::bit diff --git a/src/dynarmic/src/dynarmic/mcl/integer_of_size.hpp b/src/dynarmic/src/dynarmic/mcl/integer_of_size.hpp index 1d54e9e3ef..6f05ea8eac 100644 --- a/src/dynarmic/src/dynarmic/mcl/integer_of_size.hpp +++ b/src/dynarmic/src/dynarmic/mcl/integer_of_size.hpp @@ -17,25 +17,25 @@ struct integer_of_size_impl {}; template<> struct integer_of_size_impl<8> { using unsigned_type = u8; - using signed_type = s8; + using signed_type = std::make_signed_t; }; template<> struct integer_of_size_impl<16> { using unsigned_type = u16; - using signed_type = s16; + using signed_type = std::make_signed_t; }; template<> struct integer_of_size_impl<32> { using unsigned_type = u32; - using signed_type = s32; + using signed_type = std::make_signed_t; }; template<> struct integer_of_size_impl<64> { using unsigned_type = u64; - using signed_type = s64; + using signed_type = std::make_signed_t; }; } // namespace detail diff --git a/src/dynarmic/src/dynarmic/mcl/is_instance_of_template.hpp b/src/dynarmic/src/dynarmic/mcl/is_instance_of_template.hpp index 0cb143430d..272b15add3 100644 --- a/src/dynarmic/src/dynarmic/mcl/is_instance_of_template.hpp +++ b/src/dynarmic/src/dynarmic/mcl/is_instance_of_template.hpp @@ -10,23 +10,12 @@ namespace mcl { -/// A metavalue (of type VT and value v). -template using value = std::integral_constant; -/// A metavalue of type size_t (and value v). -template using size_value = value; -/// A metavalue of type bool (and value v). (Aliases to std::bool_constant.) -template using bool_value = value; -/// true metavalue (Aliases to std::true_type). -using true_type = bool_value; -/// false metavalue (Aliases to std::false_type). -using false_type = bool_value; - /// Is type T an instance of template class C? template class, class> -struct is_instance_of_template : false_type {}; +struct is_instance_of_template : std::false_type {}; template class C, class... As> -struct is_instance_of_template> : true_type {}; +struct is_instance_of_template> : std::true_type {}; /// Is type T an instance of template class C? template class C, class T> diff --git a/src/dynarmic/tests/A32/fuzz_arm.cpp b/src/dynarmic/tests/A32/fuzz_arm.cpp index e2546de635..922f1e4f26 100644 --- a/src/dynarmic/tests/A32/fuzz_arm.cpp +++ b/src/dynarmic/tests/A32/fuzz_arm.cpp @@ -665,7 +665,7 @@ TEST_CASE("A32: Test thumb IT instruction", "[thumb]") { A32::ITState it_state = [&] { while (true) { const u16 imm8 = RandInt(0, 0xFF); - if (mcl::bit::get_bits<0, 3>(imm8) == 0b0000 || mcl::bit::get_bits<4, 7>(imm8) == 0b1111 || (mcl::bit::get_bits<4, 7>(imm8) == 0b1110 && mcl::bit::count_ones(mcl::bit::get_bits<0, 3>(imm8)) != 1)) { + if (mcl::bit::get_bits<0, 3>(imm8) == 0b0000 || mcl::bit::get_bits<4, 7>(imm8) == 0b1111 || (mcl::bit::get_bits<4, 7>(imm8) == 0b1110 && std::popcount(mcl::bit::get_bits<0, 3>(imm8)) != 1)) { continue; } instructions.push_back(0b1011111100000000 | imm8); diff --git a/src/dynarmic/tests/A64/a64.cpp b/src/dynarmic/tests/A64/a64.cpp index 9bd9c36a86..99cc9d3d4d 100644 --- a/src/dynarmic/tests/A64/a64.cpp +++ b/src/dynarmic/tests/A64/a64.cpp @@ -2587,11 +2587,11 @@ TEST_CASE("A64: Manual Vector Min/Max U64 (Optimizer Test)", "[a64]") { // MaxU64 pattern: (a > b) ? a : b code.CMHI(V2.D2(), V0.D2(), V1.D2()); // V2 = Mask (A > B) - code.BSL(V2.B16(), V0.B16(), V1.B16()); // V2 = Resul + code.BSL(V2.B16(), V0.B16(), V1.B16()); // V2 = Result // MinU64 pattern: (a > b) ? b : a code.CMHI(V3.D2(), V0.D2(), V1.D2()); // V3 = Mask (A > B) - code.BSL(V3.B16(), V1.B16(), V0.B16()); // V3 = Resul + code.BSL(V3.B16(), V1.B16(), V0.B16()); // V3 = Result jit.SetPC(0); jit.SetVector(0, {100, 20}); @@ -2603,3 +2603,32 @@ TEST_CASE("A64: Manual Vector Min/Max U64 (Optimizer Test)", "[a64]") { CHECK(jit.GetVector(2) == Vector{100, 200}); CHECK(jit.GetVector(3) == Vector{50, 20}); } + +TEST_CASE("A64: Rounding", "[a64]") { + A64TestEnv env; + A64::UserConfig jit_user_config{}; + jit_user_config.callbacks = &env; + A64::Jit jit{jit_user_config}; + + oaknut::VectorCodeGenerator code{env.code_mem, nullptr}; + + code.FRINTN(V1.S4(), V0.S4()); // ToNearest_TieEven + code.FRINTM(V2.S4(), V0.S4()); // TowardsMinusInfinity + code.FRINTP(V3.S4(), V0.S4()); // TowardsPlusInfinity + code.FRINTZ(V4.S4(), V0.S4()); // TowardsZero + code.FRINTA(V5.S4(), V0.S4()); // ToNearest_TieAwayFromZero + code.FRINTX(V6.S4(), V0.S4()); // ToNearest_TieAwayFromZero + + jit.SetPC(0); + jit.SetVector(0, {0x4001e17c4001e17c, 0x4001e17c4001e17c}); + env.ticks_left = env.code_mem.size(); + CheckedRun([&]() { jit.Run(); }); + + CHECK(jit.GetVector(0) == Vector{0x4001e17c4001e17c, 0x4001e17c4001e17c}); + CHECK(jit.GetVector(1) == Vector{0x4000000040000000, 0x4000000040000000}); + CHECK(jit.GetVector(2) == Vector{0x4000000040000000, 0x4000000040000000}); + CHECK(jit.GetVector(3) == Vector{0x4040000040400000, 0x4040000040400000}); + CHECK(jit.GetVector(4) == Vector{0x4000000040000000, 0x4000000040000000}); + CHECK(jit.GetVector(5) == Vector{0x4000000040000000, 0x4000000040000000}); + CHECK(jit.GetVector(6) == Vector{0x4000000040000000, 0x4000000040000000}); +} diff --git a/src/dynarmic/tests/A64/testenv.h b/src/dynarmic/tests/A64/testenv.h index c25790e1c9..1d260f5460 100644 --- a/src/dynarmic/tests/A64/testenv.h +++ b/src/dynarmic/tests/A64/testenv.h @@ -17,24 +17,19 @@ using Vector = Dynarmic::A64::Vector; class A64TestEnv : public Dynarmic::A64::UserCallbacks { public: - u64 ticks_left = 0; - - bool code_mem_modified_by_guest = false; - u64 code_mem_start_address = 0; - std::vector code_mem; - ankerl::unordered_dense::map modified_memory; - std::vector interrupts; + std::vector code_mem; + u64 ticks_left = 0; + u64 code_mem_start_address = 0; + bool code_mem_modified_by_guest = false; bool IsInCodeMem(u64 vaddr) const { return vaddr >= code_mem_start_address && vaddr < code_mem_start_address + code_mem.size() * 4; } std::optional MemoryReadCode(u64 vaddr) override { - if (!IsInCodeMem(vaddr)) { + if (!IsInCodeMem(vaddr)) return 0x14000000; // B . - } - const size_t index = (vaddr - code_mem_start_address) / 4; return code_mem[index]; } @@ -43,10 +38,9 @@ public: if (IsInCodeMem(vaddr)) { return reinterpret_cast(code_mem.data())[vaddr - code_mem_start_address]; } - if (auto iter = modified_memory.find(vaddr); iter != modified_memory.end()) { - return iter->second; - } - return static_cast(vaddr); + if (auto const it = modified_memory.find(vaddr); it != modified_memory.end()) + return it->second; + return u8(vaddr); } std::uint16_t MemoryRead16(u64 vaddr) override { return u16(MemoryRead8(vaddr)) | u16(MemoryRead8(vaddr + 1)) << 8; @@ -68,16 +62,16 @@ public: modified_memory[vaddr] = value; } void MemoryWrite16(u64 vaddr, std::uint16_t value) override { - MemoryWrite8(vaddr, static_cast(value)); - MemoryWrite8(vaddr + 1, static_cast(value >> 8)); + MemoryWrite8(vaddr, u8(value)); + MemoryWrite8(vaddr + 1, u8(value >> 8)); } void MemoryWrite32(u64 vaddr, std::uint32_t value) override { - MemoryWrite16(vaddr, static_cast(value)); - MemoryWrite16(vaddr + 2, static_cast(value >> 16)); + MemoryWrite16(vaddr, u16(value)); + MemoryWrite16(vaddr + 2, u16(value >> 16)); } void MemoryWrite64(u64 vaddr, std::uint64_t value) override { - MemoryWrite32(vaddr, static_cast(value)); - MemoryWrite32(vaddr + 4, static_cast(value >> 32)); + MemoryWrite32(vaddr, u32(value)); + MemoryWrite32(vaddr + 4, u32(value >> 32)); } void MemoryWrite128(u64 vaddr, Vector value) override { MemoryWrite64(vaddr, value[0]); @@ -139,12 +133,12 @@ public: template T read(u64 vaddr) { T value; - memcpy(&value, backing_memory + vaddr, sizeof(T)); + std::memcpy(&value, backing_memory + vaddr, sizeof(T)); return value; } template void write(u64 vaddr, const T& value) { - memcpy(backing_memory + vaddr, &value, sizeof(T)); + std::memcpy(backing_memory + vaddr, &value, sizeof(T)); } std::optional MemoryReadCode(u64 vaddr) override { diff --git a/src/dynarmic/tests/x64_cpu_info.cpp b/src/dynarmic/tests/x64_cpu_info.cpp index 8e67f0fc39..8837e64ae0 100644 --- a/src/dynarmic/tests/x64_cpu_info.cpp +++ b/src/dynarmic/tests/x64_cpu_info.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + /* This file is part of the dynarmic project. * Copyright (c) 2020 MerryMage * SPDX-License-Identifier: 0BSD @@ -11,7 +14,7 @@ #include #include -#include +#include "dynarmic/backend/x64/xbyak.h" TEST_CASE("Host CPU supports", "[a64]") { using Cpu = Xbyak::util::Cpu; @@ -36,8 +39,6 @@ TEST_CASE("Host CPU supports", "[a64]") { X(tAMX_TILE), X(tAVX), X(tAVX2), - X(tAVX512_4FMAPS), - X(tAVX512_4VNNIW), X(tAVX512_BF16), X(tAVX512_BITALG), X(tAVX512_FP16), @@ -50,10 +51,8 @@ TEST_CASE("Host CPU supports", "[a64]") { X(tAVX512BW), X(tAVX512CD), X(tAVX512DQ), - X(tAVX512ER), X(tAVX512F), X(tAVX512IFMA), - X(tAVX512PF), X(tAVX512VBMI), X(tAVX512VL), X(tAVX_VNNI), @@ -81,7 +80,6 @@ TEST_CASE("Host CPU supports", "[a64]") { X(tPCLMULQDQ), X(tPOPCNT), X(tPREFETCHW), - X(tPREFETCHWT1), X(tRDRAND), X(tRDSEED), X(tRDTSCP), diff --git a/src/frontend_common/config.cpp b/src/frontend_common/config.cpp index f275727c17..b44a146b38 100644 --- a/src/frontend_common/config.cpp +++ b/src/frontend_common/config.cpp @@ -238,33 +238,27 @@ void Config::ReadControlValues() { void Config::ReadMotionTouchValues() { Settings::values.touch_from_button_maps.clear(); int num_touch_from_button_maps = BeginArray(std::string("touch_from_button_maps")); - if (num_touch_from_button_maps > 0) { for (int i = 0; i < num_touch_from_button_maps; ++i) { SetArrayIndex(i); - Settings::TouchFromButtonMap map; map.name = ReadStringSetting(std::string("name"), std::string("default")); - - const int num_touch_maps = BeginArray(std::string("entries")); - map.buttons.reserve(num_touch_maps); + int const num_touch_maps = BeginArray(std::string("entries")); + map.buttons.resize(num_touch_maps); for (int j = 0; j < num_touch_maps; j++) { SetArrayIndex(j); - std::string touch_mapping = ReadStringSetting(std::string("bind")); - map.buttons.emplace_back(std::move(touch_mapping)); + map.buttons[j] = ReadStringSetting(std::string("bind")); } EndArray(); // entries Settings::values.touch_from_button_maps.emplace_back(std::move(map)); } } else { - Settings::values.touch_from_button_maps.emplace_back( - Settings::TouchFromButtonMap{"default", {}}); + Settings::values.touch_from_button_maps.emplace_back(Settings::TouchFromButtonMap{"default", {}}); num_touch_from_button_maps = 1; } EndArray(); // touch_from_button_maps - Settings::values.touch_from_button_map_index = std::clamp( - Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1); + Settings::values.touch_from_button_map_index = (std::min)(Settings::values.touch_from_button_map_index.GetValue(), u32(num_touch_from_button_maps - 1)); } void Config::ReadCoreValues() { @@ -501,15 +495,12 @@ void Config::SaveMotionTouchValues() { BeginArray(std::string("touch_from_button_maps")); for (std::size_t p = 0; p < Settings::values.touch_from_button_maps.size(); ++p) { SetArrayIndex(int(p)); - WriteStringSetting(std::string("name"), Settings::values.touch_from_button_maps[p].name, - std::make_optional(std::string("default"))); - + WriteStringSetting(std::string("name"), Settings::values.touch_from_button_maps[p].name, std::make_optional(std::string("default"))); BeginArray(std::string("entries")); for (std::size_t q = 0; q < Settings::values.touch_from_button_maps[p].buttons.size(); ++q) { SetArrayIndex(int(q)); - WriteStringSetting(std::string("bind"), - Settings::values.touch_from_button_maps[p].buttons[q]); + WriteStringSetting(std::string("bind"), Settings::values.touch_from_button_maps[p].buttons[q]); } EndArray(); // entries } @@ -638,8 +629,7 @@ void Config::SaveDisabledAddOnValues() { BeginArray(std::string("disabled")); for (std::size_t j = 0; j < elem.second.size(); ++j) { SetArrayIndex(int(j)); - WriteStringSetting(std::string("d"), elem.second[j], - std::make_optional(std::string(""))); + WriteStringSetting(std::string("d"), elem.second[j], std::make_optional(std::string(""))); } EndArray(); // disabled ++i; @@ -733,21 +723,18 @@ s64 Config::ReadIntegerSetting(const std::string& key, const std::optional std::string full_key = GetFullKey(key, false); if (!default_value.has_value()) { try { - return std::stoll( - std::string(config->GetValue(GetSection().c_str(), full_key.c_str(), "0"))); + return std::stoll(std::string(config->GetValue(GetSection().c_str(), full_key.c_str(), "0"))); } catch (...) { return 0; } } s64 result = 0; - if (config->GetBoolValue(GetSection().c_str(), - std::string(full_key).append("\\default").c_str(), true)) { + if (config->GetBoolValue(GetSection().c_str(), std::string(full_key).append("\\default").c_str(), true)) { result = default_value.value(); } else { try { - result = std::stoll(std::string(config->GetValue( - GetSection().c_str(), full_key.c_str(), ToString(default_value.value()).c_str()))); + result = std::stoll(std::string(config->GetValue(GetSection().c_str(), full_key.c_str(), ToString(default_value.value()).c_str()))); } catch (...) { result = default_value.value(); } @@ -919,14 +906,12 @@ void Config::ReadSettingGeneric(Settings::BasicSetting* const setting) { bool use_global = true; if (setting->Switchable() && !global) { - use_global = - ReadBooleanSetting(std::string(key).append("\\use_global"), std::make_optional(true)); + use_global = ReadBooleanSetting(std::string(key).append("\\use_global"), std::make_optional(true)); setting->SetGlobal(use_global); } if (global || !use_global) { - const bool is_default = - ReadBooleanSetting(std::string(key).append("\\default"), std::make_optional(true)); + const bool is_default = ReadBooleanSetting(std::string(key).append("\\default"), std::make_optional(true)); if (!is_default) { setting->LoadString(ReadStringSetting(key, default_value)); } else { @@ -1009,7 +994,7 @@ std::string Config::AdjustOutputString(const std::string& string) { // Windows requires that two forward slashes are used at the start of a path for unmapped // network drives so we have to watch for that here -#ifndef ANDROID +#ifndef __ANDROID__ if (string.substr(0, 2) == "//") { boost::replace_all(adjusted_string, "//", "/"); adjusted_string.insert(0, "/"); @@ -1050,10 +1035,9 @@ std::string Config::GetFullKey(const std::string& key, bool skipArrayIndex) { int Config::BeginArray(const std::string& array) { array_stack.push_back(ConfigArray{AdjustKey(array), 0, 0}); - const int size = config->GetLongValue(GetSection().c_str(), - GetFullKey(std::string("size"), true).c_str(), 0); - array_stack.back().size = size; - return size; + const int size = config->GetLongValue(GetSection().c_str(), GetFullKey(std::string("size"), true).c_str(), 0); + array_stack.back().size = (std::max)(0, size); + return array_stack.back().size; } void Config::EndArray() { @@ -1071,7 +1055,7 @@ void Config::EndArray() { // Edge-case where the first array created doesn't have a name config->SetValue(GetSection().c_str(), std::string("size").c_str(), ToString(size).c_str()); } else { - const auto key = GetFullKey(std::string("size"), true); + auto const key = GetFullKey(std::string("size"), true); config->SetValue(GetSection().c_str(), key.c_str(), ToString(size).c_str()); } diff --git a/src/frontend_common/data_manager.h b/src/frontend_common/data_manager.h index afb7cb0d2b..b43067d984 100644 --- a/src/frontend_common/data_manager.h +++ b/src/frontend_common/data_manager.h @@ -1,8 +1,7 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later -#ifndef DATA_MANAGER_H -#define DATA_MANAGER_H +#pragma once #include "common/common_types.h" #include @@ -21,5 +20,3 @@ std::string ReadableBytesSize(u64 size) noexcept; u64 DataDirSize(DataDir dir); }; // namespace FrontendCommon::DataManager - -#endif // DATA_MANAGER_H diff --git a/src/frontend_common/firmware_manager.cpp b/src/frontend_common/firmware_manager.cpp index 79af202ed8..c7f1177ee2 100644 --- a/src/frontend_common/firmware_manager.cpp +++ b/src/frontend_common/firmware_manager.cpp @@ -13,7 +13,7 @@ #include "core/crypto/key_manager.h" #include "frontend_common/content_manager.h" -#ifdef ANDROID +#ifdef __ANDROID__ #include #include #include @@ -25,7 +25,7 @@ FirmwareManager::InstallKeys(std::string location, std::string extension) { const auto keys_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::KeysDir); -#ifdef ANDROID +#ifdef __ANDROID__ JNIEnv *env = Common::Android::GetEnvForThread(); jstring jsrc = Common::Android::ToJString(env, location); diff --git a/src/frontend_common/firmware_manager.h b/src/frontend_common/firmware_manager.h index 94469e9295..4c1297431d 100644 --- a/src/frontend_common/firmware_manager.h +++ b/src/frontend_common/firmware_manager.h @@ -1,8 +1,7 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later -#ifndef FIRMWARE_MANAGER_H -#define FIRMWARE_MANAGER_H +#pragma once #include "common/common_types.h" #include "core/core.h" @@ -107,5 +106,3 @@ inline std::pair GetFirmwareVersion // TODO(crueter): GET AS STRING } - -#endif diff --git a/src/frontend_common/mod_manager.cpp b/src/frontend_common/mod_manager.cpp index 35d7068f2f..b1adaebf91 100644 --- a/src/frontend_common/mod_manager.cpp +++ b/src/frontend_common/mod_manager.cpp @@ -3,7 +3,6 @@ #include #include -#include #include #include "common/fs/fs.h" #include "common/fs/fs_types.h" diff --git a/src/frontend_common/mod_manager.h b/src/frontend_common/mod_manager.h index 57fdbc3533..79c9e8ade7 100644 --- a/src/frontend_common/mod_manager.h +++ b/src/frontend_common/mod_manager.h @@ -5,6 +5,7 @@ #include #include +#include #include "common/common_types.h" namespace FrontendCommon { diff --git a/src/hid_core/frontend/emulated_controller.cpp b/src/hid_core/frontend/emulated_controller.cpp index 604414c470..3ebb9133dd 100644 --- a/src/hid_core/frontend/emulated_controller.cpp +++ b/src/hid_core/frontend/emulated_controller.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project @@ -108,10 +108,10 @@ void EmulatedController::ReloadFromSettings() { // Other or debug controller should always be a pro controller if (npad_id_type != NpadIdType::Other) { SetNpadStyleIndex(MapSettingsTypeToNPad(player.controller_type)); - original_npad_type = npad_type; + original_npad_type = npad_type.load(); } else { SetNpadStyleIndex(NpadStyleIndex::Fullkey); - original_npad_type = npad_type; + original_npad_type = npad_type.load(); } // Disable special features before disconnecting @@ -179,7 +179,7 @@ void EmulatedController::LoadDevices() { if (npad_id_type == NpadIdType::Player1 || npad_id_type == NpadIdType::Handheld) { camera_params[1] = Common::ParamPackage{"engine:camera,camera:1"}; nfc_params[0] = Common::ParamPackage{"engine:virtual_amiibo,nfc:1"}; -#ifndef ANDROID +#ifndef __ANDROID__ ring_params[1] = Common::ParamPackage{"engine:joycon,axis_x:100,axis_y:101"}; #else android_params = Common::ParamPackage{"engine:android,port:100"}; @@ -577,10 +577,9 @@ void EmulatedController::UnloadInput() { } void EmulatedController::EnableConfiguration() { - std::unique_lock lock1{connect_mutex}, lock2{npad_mutex}; - is_configuring = true; - tmp_is_connected = is_connected; - tmp_npad_type = npad_type; + is_configuring.store(true); + tmp_is_connected.store(is_connected); + tmp_npad_type.store(npad_type); } void EmulatedController::DisableConfiguration() { @@ -600,7 +599,7 @@ void EmulatedController::DisableConfiguration() { Disconnect(); } SetNpadStyleIndex(tmp_npad_type); - original_npad_type = tmp_npad_type; + original_npad_type.store(tmp_npad_type); } // Apply temporary connected status to the real controller @@ -614,19 +613,16 @@ void EmulatedController::DisableConfiguration() { } void EmulatedController::EnableSystemButtons() { - std::unique_lock lock{mutex}; system_buttons_enabled = true; } void EmulatedController::DisableSystemButtons() { - std::unique_lock lock{mutex}; system_buttons_enabled = false; controller.home_button_state.raw = 0; controller.capture_button_state.raw = 0; } void EmulatedController::ResetSystemButtons() { - std::unique_lock lock{mutex}; controller.home_button_state.home.Assign(false); controller.capture_button_state.capture.Assign(false); } @@ -763,8 +759,7 @@ void EmulatedController::StartMotionCalibration() { } } -void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback, std::size_t index, - Common::UUID uuid) { +void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback, std::size_t index, Common::UUID uuid) { const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type); const auto& player = Settings::values.players.GetValue()[player_index]; @@ -772,7 +767,6 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback return; } - std::unique_lock lock{mutex}; bool value_changed = false; const auto new_status = TransformToButton(callback); auto& current_status = controller.button_values[index]; @@ -818,7 +812,6 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback controller.debug_pad_button_state.raw = 0; controller.home_button_state.raw = 0; controller.capture_button_state.raw = 0; - lock.unlock(); TriggerOnChange(ControllerTriggerType::Button, false); return; } @@ -922,8 +915,6 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback break; } - lock.unlock(); - if (!is_connected) { if (npad_type == NpadStyleIndex::Handheld) { if (npad_id_type == NpadIdType::Handheld) { @@ -952,7 +943,6 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback, auto trigger_guard = SCOPE_GUARD { TriggerOnChange(ControllerTriggerType::Stick, !is_configuring); }; - std::unique_lock lock{mutex}; const auto stick_value = TransformToStick(callback); // Only read stick values that have the same uuid or are over the threshold to avoid flapping @@ -1009,7 +999,6 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac auto trigger_guard = SCOPE_GUARD { TriggerOnChange(ControllerTriggerType::Trigger, !is_configuring); }; - std::unique_lock lock{mutex}; const auto trigger_value = TransformToTrigger(callback); // Only read trigger values that have the same uuid or are pressed once @@ -1057,7 +1046,6 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback SCOPE_EXIT { TriggerOnChange(ControllerTriggerType::Motion, !is_configuring); }; - std::unique_lock lock{mutex}; auto& raw_status = controller.motion_values[index].raw_status; auto& emulated = controller.motion_values[index].emulated; @@ -1093,7 +1081,6 @@ void EmulatedController::SetColors(const Common::Input::CallbackStatus& callback auto trigger_guard = SCOPE_GUARD { TriggerOnChange(ControllerTriggerType::Color, !is_configuring); }; - std::unique_lock lock{mutex}; controller.color_values[index] = TransformToColor(callback); if (is_configuring) { @@ -1136,15 +1123,13 @@ void EmulatedController::SetColors(const Common::Input::CallbackStatus& callback } } -void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callback, - std::size_t index) { +void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callback, std::size_t index) { if (index >= controller.battery_values.size()) { return; } SCOPE_EXIT { TriggerOnChange(ControllerTriggerType::Battery, !is_configuring); }; - std::unique_lock lock{mutex}; controller.battery_values[index] = TransformToBattery(callback); if (is_configuring) { @@ -1209,47 +1194,33 @@ void EmulatedController::SetCamera(const Common::Input::CallbackStatus& callback SCOPE_EXIT { TriggerOnChange(ControllerTriggerType::IrSensor, !is_configuring); }; - std::unique_lock lock{mutex}; controller.camera_values = TransformToCamera(callback); - - if (is_configuring) { - return; + if (!is_configuring) { + controller.camera_state.sample++; + controller.camera_state.format = Core::IrSensor::ImageTransferProcessorFormat(controller.camera_values.format); + controller.camera_state.data = controller.camera_values.data; } - - controller.camera_state.sample++; - controller.camera_state.format = - static_cast(controller.camera_values.format); - controller.camera_state.data = controller.camera_values.data; } void EmulatedController::SetRingAnalog(const Common::Input::CallbackStatus& callback) { SCOPE_EXIT { TriggerOnChange(ControllerTriggerType::RingController, !is_configuring); }; - std::unique_lock lock{mutex}; const auto force_value = TransformToStick(callback); - controller.ring_analog_value = force_value.x; - - if (is_configuring) { - return; + if (!is_configuring) { + controller.ring_analog_state.force = force_value.x.value; } - - controller.ring_analog_state.force = force_value.x.value; } void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) { SCOPE_EXIT { TriggerOnChange(ControllerTriggerType::Nfc, !is_configuring); }; - std::unique_lock lock{mutex}; controller.nfc_values = TransformToNfc(callback); - - if (is_configuring) { - return; + if (!is_configuring) { + controller.nfc_state = controller.nfc_values; } - - controller.nfc_state = controller.nfc_values; } bool EmulatedController::SetVibration(bool should_vibrate) { @@ -1258,7 +1229,6 @@ bool EmulatedController::SetVibration(bool should_vibrate) { vibration_value.high_amplitude = 1.0f; vibration_value.low_amplitude = 1.0f; } - return SetVibration(DeviceIndex::Left, vibration_value); } @@ -1268,7 +1238,6 @@ bool EmulatedController::SetVibration(u32 slot, Core::HID::VibrationGcErmCommand vibration_value.high_amplitude = 1.0f; vibration_value.low_amplitude = 1.0f; } - return SetVibration(DeviceIndex::Left, vibration_value); } @@ -1632,7 +1601,7 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles) // Attempt to reconnect with the original type if (npad_type != original_npad_type) { Disconnect(); - const auto current_npad_type = npad_type; + const auto current_npad_type = npad_type.load(); SetNpadStyleIndex(original_npad_type); if (IsControllerSupported()) { Connect(); @@ -1650,7 +1619,7 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles) // Fallback Fullkey controllers to Pro controllers if (IsControllerFullkey() && supported_style_tag.fullkey) { - LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type); + LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type.load()); SetNpadStyleIndex(NpadStyleIndex::Fullkey); Connect(); return; @@ -1658,7 +1627,7 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles) // Fallback Dual joycon controllers to Pro controllers if (npad_type == NpadStyleIndex::JoyconDual && supported_style_tag.fullkey) { - LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type); + LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type.load()); SetNpadStyleIndex(NpadStyleIndex::Fullkey); Connect(); return; @@ -1666,19 +1635,16 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles) // Fallback Pro controllers to Dual joycon if (npad_type == NpadStyleIndex::Fullkey && supported_style_tag.joycon_dual) { - LOG_WARNING(Service_HID, "Reconnecting controller type {} as Dual Joycons", npad_type); + LOG_WARNING(Service_HID, "Reconnecting controller type {} as Dual Joycons", npad_type.load()); SetNpadStyleIndex(NpadStyleIndex::JoyconDual); Connect(); return; } - - LOG_ERROR(Service_HID, "Controller type {} is not supported. Disconnecting controller", - npad_type); + LOG_ERROR(Service_HID, "Controller type {} is not supported. Disconnecting controller", npad_type.load()); } bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const { - std::unique_lock lock{mutex}; - const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type; + const auto type = is_configuring.load() && use_temporary_value ? tmp_npad_type.load() : npad_type.load(); switch (type) { case NpadStyleIndex::Fullkey: case NpadStyleIndex::GameCube: @@ -1693,39 +1659,26 @@ bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const { } bool EmulatedController::IsControllerSupported(bool use_temporary_value) const { - std::unique_lock lock{mutex}; - const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type; + const auto type = is_configuring.load() && use_temporary_value ? tmp_npad_type.load() : npad_type.load(); switch (type) { - case NpadStyleIndex::Fullkey: - return supported_style_tag.fullkey.As(); - case NpadStyleIndex::Handheld: - return supported_style_tag.handheld.As(); - case NpadStyleIndex::JoyconDual: - return supported_style_tag.joycon_dual.As(); - case NpadStyleIndex::JoyconLeft: - return supported_style_tag.joycon_left.As(); - case NpadStyleIndex::JoyconRight: - return supported_style_tag.joycon_right.As(); - case NpadStyleIndex::GameCube: - return supported_style_tag.gamecube.As(); - case NpadStyleIndex::Pokeball: - return supported_style_tag.palma.As(); - case NpadStyleIndex::NES: - return supported_style_tag.lark.As(); - case NpadStyleIndex::SNES: - return supported_style_tag.lucia.As(); - case NpadStyleIndex::N64: - return supported_style_tag.lagoon.As(); - case NpadStyleIndex::SegaGenesis: - return supported_style_tag.lager.As(); - default: - return false; + case NpadStyleIndex::Fullkey: return supported_style_tag.fullkey.As(); + case NpadStyleIndex::Handheld: return supported_style_tag.handheld.As(); + case NpadStyleIndex::JoyconDual: return supported_style_tag.joycon_dual.As(); + case NpadStyleIndex::JoyconLeft: return supported_style_tag.joycon_left.As(); + case NpadStyleIndex::JoyconRight: return supported_style_tag.joycon_right.As(); + case NpadStyleIndex::GameCube: return supported_style_tag.gamecube.As(); + case NpadStyleIndex::Pokeball: return supported_style_tag.palma.As(); + case NpadStyleIndex::NES: return supported_style_tag.lark.As(); + case NpadStyleIndex::SNES: return supported_style_tag.lucia.As(); + case NpadStyleIndex::N64: return supported_style_tag.lagoon.As(); + case NpadStyleIndex::SegaGenesis: return supported_style_tag.lager.As(); + default: return false; } } void EmulatedController::Connect(bool use_temporary_value) { if (!IsControllerSupported(use_temporary_value)) { - const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type; + const auto type = is_configuring.load() && use_temporary_value ? tmp_npad_type.load() : npad_type.load(); LOG_ERROR(Service_HID, "Controller type {} is not supported", type); return; } @@ -1733,12 +1686,10 @@ void EmulatedController::Connect(bool use_temporary_value) { auto trigger_guard = SCOPE_GUARD { TriggerOnChange(ControllerTriggerType::Connected, !is_configuring); }; - std::unique_lock lock1{connect_mutex}, lock2{mutex}; if (is_configuring) { tmp_is_connected = true; return; } - if (is_connected) { trigger_guard.Cancel(); return; @@ -1750,12 +1701,10 @@ void EmulatedController::Disconnect() { auto trigger_guard = SCOPE_GUARD { TriggerOnChange(ControllerTriggerType::Disconnected, !is_configuring); }; - std::unique_lock lock1{connect_mutex}, lock2{mutex}; if (is_configuring) { tmp_is_connected = false; return; } - if (!is_connected) { trigger_guard.Cancel(); return; @@ -1764,19 +1713,16 @@ void EmulatedController::Disconnect() { } bool EmulatedController::IsConnected(bool get_temporary_value) const { - std::shared_lock lock{connect_mutex}; if (get_temporary_value && is_configuring) return tmp_is_connected; return is_connected; } NpadIdType EmulatedController::GetNpadIdType() const { - std::shared_lock lock{mutex}; return npad_id_type; } NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) const { - std::shared_lock lock{npad_mutex}; if (get_temporary_value && is_configuring) return tmp_npad_type; return npad_type; @@ -1786,8 +1732,6 @@ void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) { auto trigger_guard = SCOPE_GUARD { TriggerOnChange(ControllerTriggerType::Type, !is_configuring); }; - std::unique_lock lock1{mutex}, lock2{npad_mutex}; - if (is_configuring) { if (tmp_npad_type == npad_type_) { trigger_guard.Cancel(); @@ -1796,14 +1740,12 @@ void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) { tmp_npad_type = npad_type_; return; } - if (npad_type == npad_type_) { trigger_guard.Cancel(); return; } if (is_connected) { - LOG_WARNING(Service_HID, "Controller {} type changed while it's connected", - Service::HID::NpadIdTypeToIndex(npad_id_type)); + LOG_WARNING(Service_HID, "Controller {} type changed while it's connected", Service::HID::NpadIdTypeToIndex(npad_id_type)); } npad_type = npad_type_; } @@ -1832,37 +1774,30 @@ LedPattern EmulatedController::GetLedPattern() const { } ButtonValues EmulatedController::GetButtonsValues() const { - std::unique_lock lock{mutex}; return controller.button_values; } SticksValues EmulatedController::GetSticksValues() const { - std::unique_lock lock{mutex}; return controller.stick_values; } TriggerValues EmulatedController::GetTriggersValues() const { - std::unique_lock lock{mutex}; return controller.trigger_values; } ControllerMotionValues EmulatedController::GetMotionValues() const { - std::unique_lock lock{mutex}; return controller.motion_values; } ColorValues EmulatedController::GetColorsValues() const { - std::unique_lock lock{mutex}; return controller.color_values; } BatteryValues EmulatedController::GetBatteryValues() const { - std::unique_lock lock{mutex}; return controller.battery_values; } CameraValues EmulatedController::GetCameraValues() const { - std::unique_lock lock{mutex}; return controller.camera_values; } @@ -1871,72 +1806,54 @@ RingAnalogValue EmulatedController::GetRingSensorValues() const { } HomeButtonState EmulatedController::GetHomeButtons() const { - std::unique_lock lock{mutex}; - if (is_configuring) { + if (is_configuring) return {}; - } return controller.home_button_state; } CaptureButtonState EmulatedController::GetCaptureButtons() const { - std::unique_lock lock{mutex}; - if (is_configuring) { + if (is_configuring) return {}; - } return controller.capture_button_state; } NpadButtonState EmulatedController::GetNpadButtons() const { - std::unique_lock lock{mutex}; - if (is_configuring) { + if (is_configuring) return {}; - } return {controller.npad_button_state.raw & GetTurboButtonMask()}; } DebugPadButton EmulatedController::GetDebugPadButtons() const { - std::unique_lock lock{mutex}; - if (is_configuring) { + if (is_configuring) return {}; - } return controller.debug_pad_button_state; } AnalogSticks EmulatedController::GetSticks() const { - std::unique_lock lock{mutex}; - - if (is_configuring) { + if (is_configuring) return {}; - } - return controller.analog_stick_state; } NpadGcTriggerState EmulatedController::GetTriggers() const { - std::unique_lock lock{mutex}; - if (is_configuring) { + if (is_configuring) return {}; - } return controller.gc_trigger_state; } MotionState EmulatedController::GetMotions() const { - std::unique_lock lock{mutex}; return controller.motion_state; } ControllerColors EmulatedController::GetColors() const { - std::unique_lock lock{mutex}; return controller.colors_state; } BatteryLevelState EmulatedController::GetBattery() const { - std::unique_lock lock{mutex}; return controller.battery_state; } const CameraState& EmulatedController::GetCamera() const { - std::unique_lock lock{mutex}; return controller.camera_state; } @@ -1945,15 +1862,14 @@ RingSensorForce EmulatedController::GetRingSensorForce() const { } const NfcState& EmulatedController::GetNfc() const { - std::unique_lock lock{mutex}; return controller.nfc_state; } NpadColor EmulatedController::GetNpadColor(u32 color) { return { - .r = static_cast((color >> 16) & 0xFF), - .g = static_cast((color >> 8) & 0xFF), - .b = static_cast(color & 0xFF), + .r = u8((color >> 16) & 0xFF), + .g = u8((color >> 8) & 0xFF), + .b = u8(color & 0xFF), .a = 0xff, }; } diff --git a/src/hid_core/frontend/emulated_controller.h b/src/hid_core/frontend/emulated_controller.h index e4256faa3e..bee8fb8b3e 100644 --- a/src/hid_core/frontend/emulated_controller.h +++ b/src/hid_core/frontend/emulated_controller.h @@ -11,8 +11,10 @@ #include #include #include -#include #include +#include + +#include #include "common/common_types.h" #include "common/input.h" @@ -576,13 +578,7 @@ private: NpadButton GetTurboButtonMask() const; const NpadIdType npad_id_type; - NpadStyleIndex npad_type{NpadStyleIndex::None}; - NpadStyleIndex original_npad_type{NpadStyleIndex::None}; NpadStyleTag supported_style_tag{NpadStyleSet::All}; - bool is_connected{false}; - bool is_configuring{false}; - bool is_initialized{false}; - bool system_buttons_enabled{true}; f32 motion_sensitivity{Core::HID::MotionInput::IsAtRestStandard}; u32 turbo_button_state{0}; std::size_t nfc_handles{0}; @@ -591,9 +587,16 @@ private: std::array last_vibration_timepoint{}; std::array controller_connected{}; + // Atomically synched values + std::atomic npad_type{HID::NpadStyleIndex::None}; + std::atomic original_npad_type{HID::NpadStyleIndex::None}; // Temporary values to avoid doing changes while the controller is in configuring mode - NpadStyleIndex tmp_npad_type{NpadStyleIndex::None}; - bool tmp_is_connected{false}; + std::atomic tmp_npad_type{HID::NpadStyleIndex::None}; + std::atomic tmp_is_connected{false}; + std::atomic is_connected{false}; + std::atomic is_configuring{false}; + std::atomic is_initialized{false}; + std::atomic system_buttons_enabled{true}; ButtonParams button_params; StickParams stick_params; @@ -632,10 +635,7 @@ private: StickDevices virtual_stick_devices; ControllerMotionDevices virtual_motion_devices; - mutable std::shared_mutex mutex; - mutable std::shared_mutex callback_mutex; - mutable std::shared_mutex npad_mutex; - mutable std::shared_mutex connect_mutex; + mutable std::mutex callback_mutex; ankerl::unordered_dense::map callback_list; int last_callback_key = 0; diff --git a/src/hid_core/hid_core.cpp b/src/hid_core/hid_core.cpp index 410c84afbc..12cd0c1f27 100644 --- a/src/hid_core/hid_core.cpp +++ b/src/hid_core/hid_core.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -10,18 +13,20 @@ namespace Core::HID { -HIDCore::HIDCore() - : player_1{std::make_unique(NpadIdType::Player1)}, - player_2{std::make_unique(NpadIdType::Player2)}, - player_3{std::make_unique(NpadIdType::Player3)}, - player_4{std::make_unique(NpadIdType::Player4)}, - player_5{std::make_unique(NpadIdType::Player5)}, - player_6{std::make_unique(NpadIdType::Player6)}, - player_7{std::make_unique(NpadIdType::Player7)}, - player_8{std::make_unique(NpadIdType::Player8)}, - other{std::make_unique(NpadIdType::Other)}, - handheld{std::make_unique(NpadIdType::Handheld)}, - console{std::make_unique()}, devices{std::make_unique()} {} +HIDCore::HIDCore(Kernel::KernelCore& kernel_) + : player_1{std::make_unique(NpadIdType::Player1)} + , player_2{std::make_unique(NpadIdType::Player2)} + , player_3{std::make_unique(NpadIdType::Player3)} + , player_4{std::make_unique(NpadIdType::Player4)} + , player_5{std::make_unique(NpadIdType::Player5)} + , player_6{std::make_unique(NpadIdType::Player6)} + , player_7{std::make_unique(NpadIdType::Player7)} + , player_8{std::make_unique(NpadIdType::Player8)} + , other{std::make_unique(NpadIdType::Other)} + , handheld{std::make_unique(NpadIdType::Handheld)} + , console{std::make_unique()}, devices{std::make_unique()} + , kernel{kernel_} +{} HIDCore::~HIDCore() = default; diff --git a/src/hid_core/hid_core.h b/src/hid_core/hid_core.h index dae29c5062..7da11897ca 100644 --- a/src/hid_core/hid_core.h +++ b/src/hid_core/hid_core.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -8,6 +11,10 @@ #include "common/common_funcs.h" #include "hid_core/hid_types.h" +namespace Kernel { +class KernelCore; +} + namespace Core::HID { class EmulatedConsole; class EmulatedController; @@ -18,7 +25,7 @@ namespace Core::HID { class HIDCore { public: - explicit HIDCore(); + explicit HIDCore(Kernel::KernelCore& kernel); ~HIDCore(); YUZU_NON_COPYABLE(HIDCore); @@ -69,7 +76,6 @@ public: /// Number of emulated controllers static constexpr std::size_t available_controllers{10}; -private: std::unique_ptr player_1; std::unique_ptr player_2; std::unique_ptr player_3; @@ -82,6 +88,7 @@ private: std::unique_ptr handheld; std::unique_ptr console; std::unique_ptr devices; + Kernel::KernelCore& kernel; NpadStyleTag supported_style_tag{NpadStyleSet::All}; NpadIdType last_active_controller{NpadIdType::Handheld}; }; diff --git a/src/hid_core/hidbus/ringcon.cpp b/src/hid_core/hidbus/ringcon.cpp index 1927a6f856..b942cad480 100644 --- a/src/hid_core/hidbus/ringcon.cpp +++ b/src/hid_core/hidbus/ringcon.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project @@ -141,12 +141,12 @@ bool RingController::SetCommand(std::span data) { case RingConCommands::ReadRepCount: case RingConCommands::ReadTotalPushCount: ASSERT_MSG(data.size() == 0x4, "data.size is not 0x4 bytes"); - send_command_async_event->Signal(); + send_command_async_event->Signal(system.Kernel()); return true; case RingConCommands::ResetRepCount: ASSERT_MSG(data.size() == 0x4, "data.size is not 0x4 bytes"); total_rep_count = 0; - send_command_async_event->Signal(); + send_command_async_event->Signal(system.Kernel()); return true; case RingConCommands::SaveCalData: { ASSERT_MSG(data.size() == 0x14, "data.size is not 0x14 bytes"); @@ -154,14 +154,14 @@ bool RingController::SetCommand(std::span data) { SaveCalData save_info{}; std::memcpy(&save_info, data.data(), sizeof(SaveCalData)); user_calibration = save_info.calibration; - send_command_async_event->Signal(); + send_command_async_event->Signal(system.Kernel()); return true; } default: LOG_ERROR(Service_HID, "Command not implemented {}", command); command = RingConCommands::Error; // Signal a reply to avoid softlocking the game - send_command_async_event->Signal(); + send_command_async_event->Signal(system.Kernel()); return false; } } diff --git a/src/hid_core/resource_manager.cpp b/src/hid_core/resource_manager.cpp index 0c10d1bec9..d4d62ea4ba 100644 --- a/src/hid_core/resource_manager.cpp +++ b/src/hid_core/resource_manager.cpp @@ -91,7 +91,7 @@ ResourceManager::~ResourceManager() { system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event); system.CoreTiming().UnscheduleEvent(motion_update_event); system.CoreTiming().UnscheduleEvent(touch_update_event); - input_event->Finalize(); + input_event->Finalize(system.Kernel()); }; void ResourceManager::Initialize() { @@ -486,7 +486,7 @@ void ResourceManager::UpdateControllers(std::chrono::nanoseconds ns_late) { void ResourceManager::UpdateNpad(std::chrono::nanoseconds ns_late) { auto& core_timing = system.CoreTiming(); - npad->OnUpdate(core_timing); + npad->OnUpdate(system.Kernel(), core_timing); } void ResourceManager::UpdateMouseKeyboard(std::chrono::nanoseconds ns_late) { diff --git a/src/hid_core/resources/abstracted_pad/abstract_battery_handler.cpp b/src/hid_core/resources/abstracted_pad/abstract_battery_handler.cpp index d5f37247e1..a80f8271f9 100644 --- a/src/hid_core/resources/abstracted_pad/abstract_battery_handler.cpp +++ b/src/hid_core/resources/abstracted_pad/abstract_battery_handler.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project @@ -16,7 +16,9 @@ namespace Service::HID { -NpadAbstractBatteryHandler::NpadAbstractBatteryHandler() {} +NpadAbstractBatteryHandler::NpadAbstractBatteryHandler(Kernel::KernelCore& kernel_) + : kernel{kernel_} +{} NpadAbstractBatteryHandler::~NpadAbstractBatteryHandler() = default; diff --git a/src/hid_core/resources/abstracted_pad/abstract_battery_handler.h b/src/hid_core/resources/abstracted_pad/abstract_battery_handler.h index 85ac5eb72f..9c270aa92d 100644 --- a/src/hid_core/resources/abstracted_pad/abstract_battery_handler.h +++ b/src/hid_core/resources/abstracted_pad/abstract_battery_handler.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later @@ -7,6 +10,10 @@ #include "core/hle/result.h" #include "hid_core/hid_types.h" +namespace Kernel { +class KernelCore; +} + namespace Service::HID { struct AppletResourceHolder; class NpadAbstractedPadHolder; @@ -15,7 +22,7 @@ class NpadAbstractPropertiesHandler; /// Handles Npad request from HID interfaces class NpadAbstractBatteryHandler final { public: - explicit NpadAbstractBatteryHandler(); + explicit NpadAbstractBatteryHandler(Kernel::KernelCore& kernel_); ~NpadAbstractBatteryHandler(); void SetAbstractPadHolder(NpadAbstractedPadHolder* holder); @@ -44,6 +51,7 @@ private: Core::HID::NpadPowerInfo left_battery{}; Core::HID::NpadPowerInfo right_battery{}; bool has_new_battery_data{}; + Kernel::KernelCore& kernel; }; } // namespace Service::HID diff --git a/src/hid_core/resources/abstracted_pad/abstract_button_handler.cpp b/src/hid_core/resources/abstracted_pad/abstract_button_handler.cpp index 36deb25c94..239fc7656e 100644 --- a/src/hid_core/resources/abstracted_pad/abstract_button_handler.cpp +++ b/src/hid_core/resources/abstracted_pad/abstract_button_handler.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project @@ -16,7 +16,9 @@ namespace Service::HID { -NpadAbstractButtonHandler::NpadAbstractButtonHandler() {} +NpadAbstractButtonHandler::NpadAbstractButtonHandler(Kernel::KernelCore& kernel_) + : kernel{kernel_} +{} NpadAbstractButtonHandler::~NpadAbstractButtonHandler() = default; diff --git a/src/hid_core/resources/abstracted_pad/abstract_button_handler.h b/src/hid_core/resources/abstracted_pad/abstract_button_handler.h index 01eafe96df..6e49471b5e 100644 --- a/src/hid_core/resources/abstracted_pad/abstract_button_handler.h +++ b/src/hid_core/resources/abstracted_pad/abstract_button_handler.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later @@ -7,6 +10,10 @@ #include "core/hle/result.h" #include "hid_core/hid_types.h" +namespace Kernel { +class KernelCore; +} + namespace Service::HID { struct NpadSharedMemoryEntry; @@ -17,7 +24,7 @@ class NpadAbstractPropertiesHandler; /// Handles Npad request from HID interfaces class NpadAbstractButtonHandler final { public: - explicit NpadAbstractButtonHandler(); + explicit NpadAbstractButtonHandler(Kernel::KernelCore& kernel_); ~NpadAbstractButtonHandler(); void SetAbstractPadHolder(NpadAbstractedPadHolder* holder); @@ -70,6 +77,7 @@ private: u64 gc_sampling_number{}; GcTrigger gc_trigger_state{}; + Kernel::KernelCore& kernel; }; } // namespace Service::HID diff --git a/src/hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.cpp b/src/hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.cpp index 16ec2257fb..a2aaa1d8ec 100644 --- a/src/hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.cpp +++ b/src/hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project @@ -14,7 +14,9 @@ namespace Service::HID { -NpadAbstractIrSensorHandler::NpadAbstractIrSensorHandler() {} +NpadAbstractIrSensorHandler::NpadAbstractIrSensorHandler(Kernel::KernelCore& kernel_) + : kernel{kernel_} +{} NpadAbstractIrSensorHandler::~NpadAbstractIrSensorHandler() = default; @@ -52,7 +54,7 @@ void NpadAbstractIrSensorHandler::UpdateIrSensorState() { if (sensor_state == previous_state) { return; } - ir_sensor_event->Signal(); + ir_sensor_event->Signal(kernel); return; } @@ -77,7 +79,7 @@ void NpadAbstractIrSensorHandler::UpdateIrSensorState() { if (sensor_state == previous_state) { return; } - ir_sensor_event->Signal(); + ir_sensor_event->Signal(kernel); return; } @@ -86,7 +88,7 @@ void NpadAbstractIrSensorHandler::UpdateIrSensorState() { return; } - ir_sensor_event->Signal(); + ir_sensor_event->Signal(kernel); return; } @@ -105,7 +107,7 @@ Result NpadAbstractIrSensorHandler::ActivateIrSensor(bool is_enabled) { } sensor_state = NpadIrSensorState::Available; } - ir_sensor_event->Signal(); + ir_sensor_event->Signal(kernel); return ResultSuccess; } diff --git a/src/hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.h b/src/hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.h index 9978115116..799d83e82f 100644 --- a/src/hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.h +++ b/src/hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later @@ -12,6 +15,7 @@ class EmulatedController; } namespace Kernel { +class KernelCore; class KEvent; class KReadableEvent; } // namespace Kernel @@ -30,7 +34,7 @@ class NpadAbstractPropertiesHandler; /// Handles Npad request from HID interfaces class NpadAbstractIrSensorHandler final { public: - explicit NpadAbstractIrSensorHandler(); + explicit NpadAbstractIrSensorHandler(Kernel::KernelCore& kernel_); ~NpadAbstractIrSensorHandler(); void SetAbstractPadHolder(NpadAbstractedPadHolder* holder); @@ -56,5 +60,6 @@ private: Kernel::KEvent* ir_sensor_event{nullptr}; Core::HID::EmulatedController* xcd_handle{}; NpadIrSensorState sensor_state{}; + Kernel::KernelCore& kernel; }; } // namespace Service::HID diff --git a/src/hid_core/resources/abstracted_pad/abstract_led_handler.cpp b/src/hid_core/resources/abstracted_pad/abstract_led_handler.cpp index 813d3d3188..94d895509b 100644 --- a/src/hid_core/resources/abstracted_pad/abstract_led_handler.cpp +++ b/src/hid_core/resources/abstracted_pad/abstract_led_handler.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project @@ -15,7 +15,9 @@ namespace Service::HID { -NpadAbstractLedHandler::NpadAbstractLedHandler() {} +NpadAbstractLedHandler::NpadAbstractLedHandler(Kernel::KernelCore& kernel_) + : kernel{kernel_} +{} NpadAbstractLedHandler::~NpadAbstractLedHandler() = default; diff --git a/src/hid_core/resources/abstracted_pad/abstract_led_handler.h b/src/hid_core/resources/abstracted_pad/abstract_led_handler.h index 09528129b6..7ff12a9e75 100644 --- a/src/hid_core/resources/abstracted_pad/abstract_led_handler.h +++ b/src/hid_core/resources/abstracted_pad/abstract_led_handler.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later @@ -7,6 +10,10 @@ #include "core/hle/result.h" #include "hid_core/hid_types.h" +namespace Kernel { +class KernelCore; +} + namespace Service::HID { struct AppletResourceHolder; class NpadAbstractedPadHolder; @@ -15,7 +22,7 @@ class NpadAbstractPropertiesHandler; /// Handles Npad request from HID interfaces class NpadAbstractLedHandler final { public: - explicit NpadAbstractLedHandler(); + explicit NpadAbstractLedHandler(Kernel::KernelCore& kernel_); ~NpadAbstractLedHandler(); void SetAbstractPadHolder(NpadAbstractedPadHolder* holder); @@ -39,5 +46,6 @@ private: Core::HID::LedPattern left_pattern{0, 0, 0, 0}; Core::HID::LedPattern right_pattern{0, 0, 0, 0}; u64 led_interval{}; + Kernel::KernelCore& kernel; }; } // namespace Service::HID diff --git a/src/hid_core/resources/abstracted_pad/abstract_mcu_handler.cpp b/src/hid_core/resources/abstracted_pad/abstract_mcu_handler.cpp index 2b763d8e0f..d87446030a 100644 --- a/src/hid_core/resources/abstracted_pad/abstract_mcu_handler.cpp +++ b/src/hid_core/resources/abstracted_pad/abstract_mcu_handler.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project @@ -12,7 +12,9 @@ namespace Service::HID { -NpadAbstractMcuHandler::NpadAbstractMcuHandler() {} +NpadAbstractMcuHandler::NpadAbstractMcuHandler(Kernel::KernelCore& kernel_) + : kernel{kernel_} +{} NpadAbstractMcuHandler::~NpadAbstractMcuHandler() = default; diff --git a/src/hid_core/resources/abstracted_pad/abstract_mcu_handler.h b/src/hid_core/resources/abstracted_pad/abstract_mcu_handler.h index 9902dd03a7..44be82cd43 100644 --- a/src/hid_core/resources/abstracted_pad/abstract_mcu_handler.h +++ b/src/hid_core/resources/abstracted_pad/abstract_mcu_handler.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later @@ -7,6 +10,10 @@ #include "core/hle/result.h" #include "hid_core/hid_types.h" +namespace Kernel { +class KernelCore; +} + namespace Service::HID { struct IAbstractedPad; class NpadAbstractedPadHolder; @@ -28,7 +35,7 @@ static_assert(sizeof(NpadMcuHolder) == 0x10, "NpadMcuHolder is an invalid size") /// Handles Npad request from HID interfaces class NpadAbstractMcuHandler final { public: - explicit NpadAbstractMcuHandler(); + explicit NpadAbstractMcuHandler(Kernel::KernelCore& kernel_); ~NpadAbstractMcuHandler(); void SetAbstractPadHolder(NpadAbstractedPadHolder* holder); @@ -48,5 +55,6 @@ private: s32 ref_counter{}; std::array mcu_holder{}; + Kernel::KernelCore& kernel; }; } // namespace Service::HID diff --git a/src/hid_core/resources/abstracted_pad/abstract_nfc_handler.cpp b/src/hid_core/resources/abstracted_pad/abstract_nfc_handler.cpp index 80f5f3ba62..9990add4cc 100644 --- a/src/hid_core/resources/abstracted_pad/abstract_nfc_handler.cpp +++ b/src/hid_core/resources/abstracted_pad/abstract_nfc_handler.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project @@ -14,7 +14,9 @@ namespace Service::HID { -NpadAbstractNfcHandler::NpadAbstractNfcHandler() {} +NpadAbstractNfcHandler::NpadAbstractNfcHandler(Kernel::KernelCore& kernel_) + : kernel{kernel_} +{} NpadAbstractNfcHandler::~NpadAbstractNfcHandler() = default; @@ -48,13 +50,13 @@ void NpadAbstractNfcHandler::UpdateNfcState() { if (count == 0) { if (sensor_state == NpadNfcState::Active) { - nfc_activate_event->Signal(); + nfc_activate_event->Signal(kernel); } if (sensor_state == NpadNfcState::Unavailable) { return; } sensor_state = NpadNfcState::Unavailable; - input_event->Signal(); + input_event->Signal(kernel); return; } @@ -79,19 +81,18 @@ void NpadAbstractNfcHandler::UpdateNfcState() { return; } sensor_state = NpadNfcState::Available; - input_event->Signal(); + input_event->Signal(kernel); return; } if (sensor_state == NpadNfcState::Active) { - nfc_activate_event->Signal(); + nfc_activate_event->Signal(kernel); } if (sensor_state == NpadNfcState::Unavailable) { return; } sensor_state = NpadNfcState::Unavailable; - input_event->Signal(); - return; + input_event->Signal(kernel); } bool NpadAbstractNfcHandler::HasNfcSensor() { @@ -123,7 +124,7 @@ Result NpadAbstractNfcHandler::ActivateNfc(bool is_enabled) { } if (sensor_state != new_state) { sensor_state = new_state; - nfc_activate_event->Signal(); + nfc_activate_event->Signal(kernel); } return ResultSuccess; } diff --git a/src/hid_core/resources/abstracted_pad/abstract_nfc_handler.h b/src/hid_core/resources/abstracted_pad/abstract_nfc_handler.h index 0702722a6b..223a0afd4e 100644 --- a/src/hid_core/resources/abstracted_pad/abstract_nfc_handler.h +++ b/src/hid_core/resources/abstracted_pad/abstract_nfc_handler.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later @@ -8,7 +11,9 @@ #include "hid_core/hid_types.h" namespace Kernel { +class KernelCore; class KReadableEvent; +class KEvent; } enum class NpadNfcState : u32 { @@ -24,7 +29,7 @@ class NpadAbstractPropertiesHandler; /// Handles Npad request from HID interfaces class NpadAbstractNfcHandler final { public: - explicit NpadAbstractNfcHandler(); + explicit NpadAbstractNfcHandler(Kernel::KernelCore& kernel_); ~NpadAbstractNfcHandler(); void SetAbstractPadHolder(NpadAbstractedPadHolder* holder); @@ -53,5 +58,6 @@ private: Kernel::KEvent* input_event{nullptr}; u64 xcd_handle{}; NpadNfcState sensor_state{NpadNfcState::Unavailable}; + Kernel::KernelCore& kernel; }; } // namespace Service::HID diff --git a/src/hid_core/resources/abstracted_pad/abstract_pad.cpp b/src/hid_core/resources/abstracted_pad/abstract_pad.cpp index d7cf2bba9b..b828c9b11e 100644 --- a/src/hid_core/resources/abstracted_pad/abstract_pad.cpp +++ b/src/hid_core/resources/abstracted_pad/abstract_pad.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project @@ -12,7 +12,20 @@ namespace Service::HID { -AbstractPad::AbstractPad() {} +AbstractPad::AbstractPad(Kernel::KernelCore& kernel_) + //: abstract_pad_holder{kernel_} + : properties_handler{kernel_} + , led_handler{kernel_} + , ir_sensor_handler{kernel_} + , nfc_handler{kernel_} + , mcu_handler{kernel_} + , vibration_handler{kernel_} + , sixaxis_handler{kernel_} + , button_handler{kernel_} + , battery_handler{kernel_} + , palma_handler{kernel_} + , kernel{kernel_} +{} AbstractPad::~AbstractPad() = default; diff --git a/src/hid_core/resources/abstracted_pad/abstract_pad.h b/src/hid_core/resources/abstracted_pad/abstract_pad.h index 3297924574..99d886d40c 100644 --- a/src/hid_core/resources/abstracted_pad/abstract_pad.h +++ b/src/hid_core/resources/abstracted_pad/abstract_pad.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later @@ -27,6 +30,10 @@ #include "hid_core/resources/vibration/n64_vibration_device.h" #include "hid_core/resources/vibration/vibration_device.h" +namespace Kernel { +class KernelCore; +} + namespace Service::HID { class AppletResource; class SixAxisResource; @@ -49,7 +56,7 @@ struct HandheldConfig; /// Handles Npad request from HID interfaces class AbstractPad final { public: - explicit AbstractPad(); + explicit AbstractPad(Kernel::KernelCore& kernel_); ~AbstractPad(); void SetExternals(AppletResourceHolder* applet_resource, @@ -88,17 +95,19 @@ public: private: AppletResourceHolder* applet_resource_holder{nullptr}; + + // TODO: fix NpadAbstractedPadHolder abstract_pad_holder{}; - NpadAbstractPropertiesHandler properties_handler{}; - NpadAbstractLedHandler led_handler{}; - NpadAbstractIrSensorHandler ir_sensor_handler{}; - NpadAbstractNfcHandler nfc_handler{}; - NpadAbstractMcuHandler mcu_handler{}; - NpadAbstractVibrationHandler vibration_handler{}; - NpadAbstractSixAxisHandler sixaxis_handler{}; - NpadAbstractButtonHandler button_handler{}; - NpadAbstractBatteryHandler battery_handler{}; - NpadAbstractPalmaHandler palma_handler{}; + NpadAbstractPropertiesHandler properties_handler; + NpadAbstractLedHandler led_handler; + NpadAbstractIrSensorHandler ir_sensor_handler; + NpadAbstractNfcHandler nfc_handler; + NpadAbstractMcuHandler mcu_handler; + NpadAbstractVibrationHandler vibration_handler; + NpadAbstractSixAxisHandler sixaxis_handler; + NpadAbstractButtonHandler button_handler; + NpadAbstractBatteryHandler battery_handler; + NpadAbstractPalmaHandler palma_handler; NpadN64VibrationDevice vibration_n64{}; NpadVibrationDevice vibration_left{}; @@ -114,6 +123,7 @@ private: s32 ref_counter{}; Core::HID::NpadInterfaceType interface_type{Core::HID::NpadInterfaceType::None}; + Kernel::KernelCore& kernel; }; using FullAbstractPad = std::array; diff --git a/src/hid_core/resources/abstracted_pad/abstract_palma_handler.cpp b/src/hid_core/resources/abstracted_pad/abstract_palma_handler.cpp index 7766bedfd0..981f92ea00 100644 --- a/src/hid_core/resources/abstracted_pad/abstract_palma_handler.cpp +++ b/src/hid_core/resources/abstracted_pad/abstract_palma_handler.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project @@ -10,7 +10,9 @@ namespace Service::HID { -NpadAbstractPalmaHandler::NpadAbstractPalmaHandler() {} +NpadAbstractPalmaHandler::NpadAbstractPalmaHandler(Kernel::KernelCore& kernel_) + : kernel{kernel_} +{} NpadAbstractPalmaHandler::~NpadAbstractPalmaHandler() = default; diff --git a/src/hid_core/resources/abstracted_pad/abstract_palma_handler.h b/src/hid_core/resources/abstracted_pad/abstract_palma_handler.h index fbd2e67e53..c09572a7b6 100644 --- a/src/hid_core/resources/abstracted_pad/abstract_palma_handler.h +++ b/src/hid_core/resources/abstracted_pad/abstract_palma_handler.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later @@ -7,6 +10,10 @@ #include "core/hle/result.h" #include "hid_core/hid_types.h" +namespace Kernel { +class KernelCore; +} + namespace Service::HID { class NpadAbstractedPadHolder; class NpadAbstractPropertiesHandler; @@ -14,7 +21,7 @@ class PalmaResource; class NpadAbstractPalmaHandler final { public: - explicit NpadAbstractPalmaHandler(); + explicit NpadAbstractPalmaHandler(Kernel::KernelCore& kernel_); ~NpadAbstractPalmaHandler(); void SetAbstractPadHolder(NpadAbstractedPadHolder* holder); @@ -32,6 +39,7 @@ private: PalmaResource* palma_resource{nullptr}; s32 ref_counter{}; + Kernel::KernelCore& kernel; }; } // namespace Service::HID diff --git a/src/hid_core/resources/abstracted_pad/abstract_properties_handler.cpp b/src/hid_core/resources/abstracted_pad/abstract_properties_handler.cpp index c225670043..db011bc6dd 100644 --- a/src/hid_core/resources/abstracted_pad/abstract_properties_handler.cpp +++ b/src/hid_core/resources/abstracted_pad/abstract_properties_handler.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project @@ -14,7 +14,9 @@ namespace Service::HID { -NpadAbstractPropertiesHandler::NpadAbstractPropertiesHandler() {} +NpadAbstractPropertiesHandler::NpadAbstractPropertiesHandler(Kernel::KernelCore& kernel_) + : kernel{kernel_} +{} NpadAbstractPropertiesHandler::~NpadAbstractPropertiesHandler() = default; diff --git a/src/hid_core/resources/abstracted_pad/abstract_properties_handler.h b/src/hid_core/resources/abstracted_pad/abstract_properties_handler.h index fa68278998..1b8cf4be82 100644 --- a/src/hid_core/resources/abstracted_pad/abstract_properties_handler.h +++ b/src/hid_core/resources/abstracted_pad/abstract_properties_handler.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later @@ -10,6 +13,10 @@ #include "hid_core/hid_types.h" #include "hid_core/resources/npad/npad_types.h" +namespace Kernel { +class KernelCore; +} + namespace Service::HID { struct NpadSharedMemoryEntry; @@ -25,7 +32,7 @@ struct ColorProperties { /// Handles Npad request from HID interfaces class NpadAbstractPropertiesHandler final { public: - explicit NpadAbstractPropertiesHandler(); + explicit NpadAbstractPropertiesHandler(Kernel::KernelCore& kernel_); ~NpadAbstractPropertiesHandler(); void SetAbstractPadHolder(NpadAbstractedPadHolder* holder); @@ -82,5 +89,6 @@ private: ColorProperties fullkey_color{}; ColorProperties left_color{}; ColorProperties right_color{}; + Kernel::KernelCore& kernel; }; } // namespace Service::HID diff --git a/src/hid_core/resources/abstracted_pad/abstract_sixaxis_handler.cpp b/src/hid_core/resources/abstracted_pad/abstract_sixaxis_handler.cpp index a8d2e5b2a0..54055751a2 100644 --- a/src/hid_core/resources/abstracted_pad/abstract_sixaxis_handler.cpp +++ b/src/hid_core/resources/abstracted_pad/abstract_sixaxis_handler.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project @@ -15,7 +15,9 @@ namespace Service::HID { -NpadAbstractSixAxisHandler::NpadAbstractSixAxisHandler() {} +NpadAbstractSixAxisHandler::NpadAbstractSixAxisHandler(Kernel::KernelCore& kernel_) + : kernel{kernel_} +{} NpadAbstractSixAxisHandler::~NpadAbstractSixAxisHandler() = default; diff --git a/src/hid_core/resources/abstracted_pad/abstract_sixaxis_handler.h b/src/hid_core/resources/abstracted_pad/abstract_sixaxis_handler.h index 9c20459e91..c126c25a23 100644 --- a/src/hid_core/resources/abstracted_pad/abstract_sixaxis_handler.h +++ b/src/hid_core/resources/abstracted_pad/abstract_sixaxis_handler.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later @@ -6,6 +9,11 @@ #include "common/common_types.h" #include "core/hle/result.h" #include "hid_core/hid_types.h" +#include "hid_core/resources/shared_memory_format.h" + +namespace Kernel { +class KernelCore; +} namespace Service::HID { class SixAxisResource; @@ -17,7 +25,7 @@ struct NpadSixAxisSensorLifo; /// Handles Npad request from HID interfaces class NpadAbstractSixAxisHandler final { public: - explicit NpadAbstractSixAxisHandler(); + explicit NpadAbstractSixAxisHandler(Kernel::KernelCore& kernel_); ~NpadAbstractSixAxisHandler(); void SetAbstractPadHolder(NpadAbstractedPadHolder* holder); @@ -56,6 +64,7 @@ private: SixAxisResource* six_axis_resource{nullptr}; s32 ref_counter{}; + Kernel::KernelCore& kernel; }; } // namespace Service::HID diff --git a/src/hid_core/resources/abstracted_pad/abstract_vibration_handler.cpp b/src/hid_core/resources/abstracted_pad/abstract_vibration_handler.cpp index 3266422133..a0a37806fc 100644 --- a/src/hid_core/resources/abstracted_pad/abstract_vibration_handler.cpp +++ b/src/hid_core/resources/abstracted_pad/abstract_vibration_handler.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project @@ -19,7 +19,9 @@ namespace Service::HID { -NpadAbstractVibrationHandler::NpadAbstractVibrationHandler() {} +NpadAbstractVibrationHandler::NpadAbstractVibrationHandler(Kernel::KernelCore& kernel_) + : kernel{kernel_} +{} NpadAbstractVibrationHandler::~NpadAbstractVibrationHandler() = default; diff --git a/src/hid_core/resources/abstracted_pad/abstract_vibration_handler.h b/src/hid_core/resources/abstracted_pad/abstract_vibration_handler.h index 8bc8129c24..d57bf27814 100644 --- a/src/hid_core/resources/abstracted_pad/abstract_vibration_handler.h +++ b/src/hid_core/resources/abstracted_pad/abstract_vibration_handler.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later @@ -9,6 +12,10 @@ #include "core/hle/result.h" #include "hid_core/hid_types.h" +namespace Kernel { +class KernelCore; +} + namespace Core::HID { class HIDCore; } @@ -25,7 +32,7 @@ class NpadVibration; /// Keeps track of battery levels and updates npad battery shared memory values class NpadAbstractVibrationHandler final { public: - explicit NpadAbstractVibrationHandler(); + explicit NpadAbstractVibrationHandler(Kernel::KernelCore& kernel_); ~NpadAbstractVibrationHandler(); void SetAbstractPadHolder(NpadAbstractedPadHolder* holder); @@ -55,5 +62,6 @@ private: NpadGcVibrationDevice* gc_vibration_device{nullptr}; NpadVibration* vibration_handler{nullptr}; s32 ref_counter{}; + Kernel::KernelCore& kernel; }; } // namespace Service::HID diff --git a/src/hid_core/resources/applet_resource.cpp b/src/hid_core/resources/applet_resource.cpp index 31480a0e90..a7b9658cac 100644 --- a/src/hid_core/resources/applet_resource.cpp +++ b/src/hid_core/resources/applet_resource.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project @@ -14,7 +14,10 @@ namespace Service::HID { AppletResource::AppletResource(Core::System& system_) : system{system_} {} -AppletResource::~AppletResource() = default; +AppletResource::~AppletResource() { + for (size_t i = 0; i < shared_memory_holder.size(); ++i) + shared_memory_holder[i].Finalize(system); +} Result AppletResource::CreateAppletResource(u64 aruid) { const u64 index = GetIndexFromAruid(aruid); @@ -34,7 +37,7 @@ Result AppletResource::CreateAppletResource(u64 aruid) { return result; } if (shared_memory.GetAddress() == nullptr) { - shared_memory.Finalize(); + shared_memory.Finalize(system); return ResultSharedMemoryNotInitialized; } } @@ -139,7 +142,7 @@ void AppletResource::FreeAppletResourceId(u64 aruid) { if (aruid_data.flag.is_assigned) { aruid_data.shared_memory_format = nullptr; aruid_data.flag.is_assigned.Assign(false); - shared_memory_holder[index].Finalize(); + shared_memory_holder[index].Finalize(system); } } diff --git a/src/hid_core/resources/npad/npad.cpp b/src/hid_core/resources/npad/npad.cpp index 3a191f4539..8e309238d2 100644 --- a/src/hid_core/resources/npad/npad.cpp +++ b/src/hid_core/resources/npad/npad.cpp @@ -29,21 +29,36 @@ namespace Service::HID { NPad::NPad(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service_context_) - : hid_core{hid_core_}, service_context{service_context_}, npad_resource{service_context} { + : hid_core{hid_core_} + , service_context{service_context_} + , npad_resource{hid_core_.kernel, service_context} + , abstracted_pads{{ + AbstractPad{hid_core_.kernel}, + AbstractPad{hid_core_.kernel}, + AbstractPad{hid_core_.kernel}, + AbstractPad{hid_core_.kernel}, + AbstractPad{hid_core_.kernel}, + AbstractPad{hid_core_.kernel}, + AbstractPad{hid_core_.kernel}, + AbstractPad{hid_core_.kernel}, + AbstractPad{hid_core_.kernel}, + AbstractPad{hid_core_.kernel}, + }} +{ for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; ++aruid_index) { for (std::size_t i = 0; i < controller_data[aruid_index].size(); ++i) { auto& controller = controller_data[aruid_index][i]; controller.device = hid_core.GetEmulatedControllerByIndex(i); Core::HID::ControllerUpdateCallback engine_callback{ - .on_change = - [this, i](Core::HID::ControllerTriggerType type) { ControllerUpdate(type, i); }, + .on_change = [this, i](Core::HID::ControllerTriggerType type) { + ControllerUpdate(hid_core.kernel, type, i); + }, .is_npad_service = true, }; controller.callback_key = controller.device->SetCallback(engine_callback); } } for (std::size_t i = 0; i < abstracted_pads.size(); ++i) { - abstracted_pads[i] = AbstractPad{}; abstracted_pads[i].SetNpadId(IndexToNpadIdType(i)); } } @@ -123,10 +138,10 @@ void NPad::FreeAppletResourceId(u64 aruid) { return npad_resource.FreeAppletResourceId(aruid); } -void NPad::ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t controller_idx) { +void NPad::ControllerUpdate(Kernel::KernelCore& kernel, Core::HID::ControllerTriggerType type, std::size_t controller_idx) { if (type == Core::HID::ControllerTriggerType::All) { - ControllerUpdate(Core::HID::ControllerTriggerType::Connected, controller_idx); - ControllerUpdate(Core::HID::ControllerTriggerType::Battery, controller_idx); + ControllerUpdate(kernel, Core::HID::ControllerTriggerType::Connected, controller_idx); + ControllerUpdate(kernel, Core::HID::ControllerTriggerType::Battery, controller_idx); return; } @@ -151,7 +166,7 @@ void NPad::ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t c if (is_connected == controller.is_connected) { return; } - UpdateControllerAt(data->aruid, npad_type, npad_id, is_connected); + UpdateControllerAt(kernel, data->aruid, npad_type, npad_id, is_connected); break; case Core::HID::ControllerTriggerType::Battery: { if (!controller.device->IsConnected()) { @@ -173,7 +188,7 @@ void NPad::ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t c } } -void NPad::InitNewlyAddedController(u64 aruid, Core::HID::NpadIdType npad_id) { +void NPad::InitNewlyAddedController(Kernel::KernelCore& kernel, u64 aruid, Core::HID::NpadIdType npad_id) { auto& controller = GetControllerFromNpadIdType(aruid, npad_id); if (!npad_resource.IsControllerSupported(aruid, controller.device->GetNpadStyleIndex())) { return; @@ -188,7 +203,7 @@ void NPad::InitNewlyAddedController(u64 aruid, Core::HID::NpadIdType npad_id) { return; } if (controller_type == Core::HID::NpadStyleIndex::None) { - npad_resource.SignalStyleSetUpdateEvent(aruid, npad_id); + npad_resource.SignalStyleSetUpdateEvent(kernel, aruid, npad_id); return; } @@ -372,7 +387,7 @@ void NPad::InitNewlyAddedController(u64 aruid, Core::HID::NpadIdType npad_id) { Common::Input::PollingMode::Active); } - npad_resource.SignalStyleSetUpdateEvent(aruid, npad_id); + npad_resource.SignalStyleSetUpdateEvent(kernel, aruid, npad_id); WriteEmptyEntry(controller.shared_memory); hid_core.SetLastActiveController(npad_id); abstracted_pads[NpadIdTypeToIndex(npad_id)].Update(); @@ -399,20 +414,20 @@ void NPad::WriteEmptyEntry(NpadInternalState* npad) { npad->gc_trigger_lifo.WriteNextEntry(dummy_gc_state); } -void NPad::RequestPadStateUpdate(u64 aruid, Core::HID::NpadIdType npad_id) { +void NPad::RequestPadStateUpdate(Kernel::KernelCore& kernel, u64 aruid, Core::HID::NpadIdType npad_id) { std::scoped_lock lock{*applet_resource_holder.shared_mutex}; auto& controller = GetControllerFromNpadIdType(aruid, npad_id); const auto controller_type = controller.device->GetNpadStyleIndex(); if (!controller.device->IsConnected() && controller.is_connected) { - DisconnectNpad(aruid, npad_id); + DisconnectNpad(kernel, aruid, npad_id); return; } if (!controller.device->IsConnected()) { return; } if (controller.device->IsConnected() && !controller.is_connected) { - InitNewlyAddedController(aruid, npad_id); + InitNewlyAddedController(kernel, aruid, npad_id); } // This function is unique to yuzu for the turbo buttons and motion to work properly @@ -468,7 +483,7 @@ void NPad::RequestPadStateUpdate(u64 aruid, Core::HID::NpadIdType npad_id) { } } -void NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { +void NPad::OnUpdate(Kernel::KernelCore& kernel, const Core::Timing::CoreTiming& core_timing) { if (ref_counter == 0) { return; } @@ -510,7 +525,7 @@ void NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { continue; } - RequestPadStateUpdate(aruid, controller.device->GetNpadIdType()); + RequestPadStateUpdate(kernel, aruid, controller.device->GetNpadIdType()); auto& pad_state = controller.npad_pad_state; auto& libnx_state = controller.npad_libnx_state; auto& trigger_state = controller.npad_trigger_state; @@ -627,17 +642,17 @@ void NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { } } -Result NPad::SetSupportedNpadStyleSet(u64 aruid, Core::HID::NpadStyleSet supported_style_set) { +Result NPad::SetSupportedNpadStyleSet(Kernel::KernelCore& kernel, u64 aruid, Core::HID::NpadStyleSet supported_style_set) { std::scoped_lock lock{mutex}; hid_core.SetSupportedStyleTag({supported_style_set}); const Result result = npad_resource.SetSupportedNpadStyleSet(aruid, supported_style_set); if (result.IsSuccess()) { - OnUpdate({}); + OnUpdate(kernel, {}); } return result; } -Result NPad::GetSupportedNpadStyleSet(u64 aruid, +Result NPad::GetSupportedNpadStyleSet(Kernel::KernelCore& kernel, u64 aruid, Core::HID::NpadStyleSet& out_supported_style_set) const { std::scoped_lock lock{mutex}; const Result result = npad_resource.GetSupportedNpadStyleSet(out_supported_style_set, aruid); @@ -650,8 +665,7 @@ Result NPad::GetSupportedNpadStyleSet(u64 aruid, return result; } -Result NPad::GetMaskedSupportedNpadStyleSet( - u64 aruid, Core::HID::NpadStyleSet& out_supported_style_set) const { +Result NPad::GetMaskedSupportedNpadStyleSet(Kernel::KernelCore& kernel, u64 aruid, Core::HID::NpadStyleSet& out_supported_style_set) const { std::scoped_lock lock{mutex}; const Result result = npad_resource.GetMaskedSupportedNpadStyleSet(out_supported_style_set, aruid); @@ -664,8 +678,7 @@ Result NPad::GetMaskedSupportedNpadStyleSet( return result; } -Result NPad::SetSupportedNpadIdType(u64 aruid, - std::span supported_npad_list) { +Result NPad::SetSupportedNpadIdType(Kernel::KernelCore& kernel, u64 aruid, std::span supported_npad_list) { std::scoped_lock lock{mutex}; if (supported_npad_list.size() > MaxSupportedNpadIdTypes) { return ResultInvalidArraySize; @@ -674,7 +687,7 @@ Result NPad::SetSupportedNpadIdType(u64 aruid, Result result = npad_resource.SetSupportedNpadIdType(aruid, supported_npad_list); if (result.IsSuccess()) { - OnUpdate({}); + OnUpdate(kernel, {}); } return result; @@ -690,28 +703,32 @@ Result NPad::GetNpadJoyHoldType(u64 aruid, NpadJoyHoldType& out_hold_type) const return npad_resource.GetNpadJoyHoldType(out_hold_type, aruid); } -Result NPad::SetNpadHandheldActivationMode(u64 aruid, NpadHandheldActivationMode mode) { +Result NPad::SetNpadHandheldActivationMode(Kernel::KernelCore& kernel, u64 aruid, NpadHandheldActivationMode mode) { std::scoped_lock lock{mutex}; Result result = npad_resource.SetNpadHandheldActivationMode(aruid, mode); if (result.IsSuccess()) { - OnUpdate({}); + OnUpdate(kernel, {}); } return result; } -Result NPad::GetNpadHandheldActivationMode(u64 aruid, NpadHandheldActivationMode& out_mode) const { +Result NPad::GetNpadHandheldActivationMode(Kernel::KernelCore& kernel, u64 aruid, NpadHandheldActivationMode& out_mode) const { std::scoped_lock lock{mutex}; return npad_resource.GetNpadHandheldActivationMode(out_mode, aruid); } -bool NPad::SetNpadMode(u64 aruid, Core::HID::NpadIdType& new_npad_id, Core::HID::NpadIdType npad_id, - NpadJoyDeviceType npad_device_type, NpadJoyAssignmentMode assignment_mode) { +bool NPad::SetNpadMode(Kernel::KernelCore& kernel, u64 aruid, Core::HID::NpadIdType& new_npad_id, Core::HID::NpadIdType npad_id, NpadJoyDeviceType npad_device_type, NpadJoyAssignmentMode assignment_mode) { if (!IsNpadIdValid(npad_id)) { LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); return false; } auto& controller = GetControllerFromNpadIdType(aruid, npad_id); + if (!controller.shared_memory) { + LOG_WARNING(Service_HID, "shared_memory is null for npad_id={}", npad_id); + return false; + } + if (controller.shared_memory->assignment_mode != assignment_mode) { controller.shared_memory->assignment_mode = assignment_mode; } @@ -722,17 +739,17 @@ bool NPad::SetNpadMode(u64 aruid, Core::HID::NpadIdType& new_npad_id, Core::HID: if (assignment_mode == NpadJoyAssignmentMode::Dual) { if (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconLeft) { - DisconnectNpad(aruid, npad_id); + DisconnectNpad(kernel, aruid, npad_id); controller.is_dual_left_connected = true; controller.is_dual_right_connected = false; - UpdateControllerAt(aruid, Core::HID::NpadStyleIndex::JoyconDual, npad_id, true); + UpdateControllerAt(kernel, aruid, Core::HID::NpadStyleIndex::JoyconDual, npad_id, true); return false; } if (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight) { - DisconnectNpad(aruid, npad_id); + DisconnectNpad(kernel, aruid, npad_id); controller.is_dual_left_connected = false; controller.is_dual_right_connected = true; - UpdateControllerAt(aruid, Core::HID::NpadStyleIndex::JoyconDual, npad_id, true); + UpdateControllerAt(kernel, aruid, Core::HID::NpadStyleIndex::JoyconDual, npad_id, true); return false; } return false; @@ -746,58 +763,55 @@ bool NPad::SetNpadMode(u64 aruid, Core::HID::NpadIdType& new_npad_id, Core::HID: } if (controller.is_dual_left_connected && !controller.is_dual_right_connected) { - DisconnectNpad(aruid, npad_id); - UpdateControllerAt(aruid, Core::HID::NpadStyleIndex::JoyconLeft, npad_id, true); + DisconnectNpad(kernel, aruid, npad_id); + UpdateControllerAt(kernel, aruid, Core::HID::NpadStyleIndex::JoyconLeft, npad_id, true); return false; } if (!controller.is_dual_left_connected && controller.is_dual_right_connected) { - DisconnectNpad(aruid, npad_id); - UpdateControllerAt(aruid, Core::HID::NpadStyleIndex::JoyconRight, npad_id, true); + DisconnectNpad(kernel, aruid, npad_id); + UpdateControllerAt(kernel, aruid, Core::HID::NpadStyleIndex::JoyconRight, npad_id, true); return false; } // We have two controllers connected to the same npad_id we need to split them new_npad_id = hid_core.GetFirstDisconnectedNpadId(); auto& controller_2 = GetControllerFromNpadIdType(aruid, new_npad_id); - DisconnectNpad(aruid, npad_id); + DisconnectNpad(kernel, aruid, npad_id); if (npad_device_type == NpadJoyDeviceType::Left) { - UpdateControllerAt(aruid, Core::HID::NpadStyleIndex::JoyconLeft, npad_id, true); + UpdateControllerAt(kernel, aruid, Core::HID::NpadStyleIndex::JoyconLeft, npad_id, true); controller_2.is_dual_left_connected = false; controller_2.is_dual_right_connected = true; - UpdateControllerAt(aruid, Core::HID::NpadStyleIndex::JoyconDual, new_npad_id, true); + UpdateControllerAt(kernel, aruid, Core::HID::NpadStyleIndex::JoyconDual, new_npad_id, true); } else { - UpdateControllerAt(aruid, Core::HID::NpadStyleIndex::JoyconRight, npad_id, true); + UpdateControllerAt(kernel, aruid, Core::HID::NpadStyleIndex::JoyconRight, npad_id, true); controller_2.is_dual_left_connected = true; controller_2.is_dual_right_connected = false; - UpdateControllerAt(aruid, Core::HID::NpadStyleIndex::JoyconDual, new_npad_id, true); + UpdateControllerAt(kernel, aruid, Core::HID::NpadStyleIndex::JoyconDual, new_npad_id, true); } return true; } -Result NPad::AcquireNpadStyleSetUpdateEventHandle(u64 aruid, Kernel::KReadableEvent** out_event, - Core::HID::NpadIdType npad_id) { +Result NPad::AcquireNpadStyleSetUpdateEventHandle(Kernel::KernelCore& kernel, u64 aruid, Kernel::KReadableEvent** out_event, Core::HID::NpadIdType npad_id) { std::scoped_lock lock{mutex}; - return npad_resource.AcquireNpadStyleSetUpdateEventHandle(aruid, out_event, npad_id); + return npad_resource.AcquireNpadStyleSetUpdateEventHandle(kernel, aruid, out_event, npad_id); } -void NPad::AddNewControllerAt(u64 aruid, Core::HID::NpadStyleIndex controller, - Core::HID::NpadIdType npad_id) { - UpdateControllerAt(aruid, controller, npad_id, true); +void NPad::AddNewControllerAt(Kernel::KernelCore& kernel, u64 aruid, Core::HID::NpadStyleIndex controller, Core::HID::NpadIdType npad_id) { + UpdateControllerAt(kernel, aruid, controller, npad_id, true); } -void NPad::UpdateControllerAt(u64 aruid, Core::HID::NpadStyleIndex type, - Core::HID::NpadIdType npad_id, bool connected) { +void NPad::UpdateControllerAt(Kernel::KernelCore& kernel, u64 aruid, Core::HID::NpadStyleIndex type, Core::HID::NpadIdType npad_id, bool connected) { auto& controller = GetControllerFromNpadIdType(aruid, npad_id); if (!connected) { - DisconnectNpad(aruid, npad_id); + DisconnectNpad(kernel, aruid, npad_id); return; } controller.device->SetNpadStyleIndex(type); - InitNewlyAddedController(aruid, npad_id); + InitNewlyAddedController(kernel, aruid, npad_id); } -Result NPad::DisconnectNpad(u64 aruid, Core::HID::NpadIdType npad_id) { +Result NPad::DisconnectNpad(Kernel::KernelCore& kernel, u64 aruid, Core::HID::NpadIdType npad_id) { if (!IsNpadIdValid(npad_id)) { LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); return ResultInvalidNpadId; @@ -840,7 +854,7 @@ Result NPad::DisconnectNpad(u64 aruid, Core::HID::NpadIdType npad_id) { controller.is_dual_right_connected = true; controller.is_connected = false; controller.device->Disconnect(); - npad_resource.SignalStyleSetUpdateEvent(aruid, npad_id); + npad_resource.SignalStyleSetUpdateEvent(kernel, aruid, npad_id); WriteEmptyEntry(shared_memory); return ResultSuccess; } @@ -873,8 +887,7 @@ Result NPad::ResetIsSixAxisSensorDeviceNewlyAssigned( return ResultSuccess; } -Result NPad::MergeSingleJoyAsDualJoy(u64 aruid, Core::HID::NpadIdType npad_id_1, - Core::HID::NpadIdType npad_id_2) { +Result NPad::MergeSingleJoyAsDualJoy(Kernel::KernelCore& kernel, u64 aruid, Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2) { if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) { LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1, npad_id_2); @@ -928,11 +941,11 @@ Result NPad::MergeSingleJoyAsDualJoy(u64 aruid, Core::HID::NpadIdType npad_id_1, } // Disconnect the joycons and connect them as dual joycon at the first index. - DisconnectNpad(aruid, npad_id_1); - DisconnectNpad(aruid, npad_id_2); + DisconnectNpad(kernel, aruid, npad_id_1); + DisconnectNpad(kernel, aruid, npad_id_2); controller_1.is_dual_left_connected = true; controller_1.is_dual_right_connected = true; - AddNewControllerAt(aruid, Core::HID::NpadStyleIndex::JoyconDual, npad_id_1); + AddNewControllerAt(kernel, aruid, Core::HID::NpadStyleIndex::JoyconDual, npad_id_1); return ResultSuccess; } @@ -956,11 +969,9 @@ Result NPad::StopLrAssignmentMode(u64 aruid) { return result; } -Result NPad::SwapNpadAssignment(u64 aruid, Core::HID::NpadIdType npad_id_1, - Core::HID::NpadIdType npad_id_2) { +Result NPad::SwapNpadAssignment(Kernel::KernelCore& kernel, u64 aruid, Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2) { if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) { - LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1, - npad_id_2); + LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1, npad_id_2); return ResultInvalidNpadId; } if (npad_id_1 == Core::HID::NpadIdType::Handheld || @@ -982,20 +993,17 @@ Result NPad::SwapNpadAssignment(u64 aruid, Core::HID::NpadIdType npad_id_1, return ResultNpadNotConnected; } - UpdateControllerAt(aruid, type_index_2, npad_id_1, is_connected_2); - UpdateControllerAt(aruid, type_index_1, npad_id_2, is_connected_1); - + UpdateControllerAt(kernel, aruid, type_index_2, npad_id_1, is_connected_2); + UpdateControllerAt(kernel, aruid, type_index_1, npad_id_2, is_connected_1); return ResultSuccess; } -Result NPad::IsUnintendedHomeButtonInputProtectionEnabled(bool& out_is_enabled, u64 aruid, - Core::HID::NpadIdType npad_id) const { +Result NPad::IsUnintendedHomeButtonInputProtectionEnabled(bool& out_is_enabled, u64 aruid, Core::HID::NpadIdType npad_id) const { std::scoped_lock lock{mutex}; return npad_resource.GetHomeProtectionEnabled(out_is_enabled, aruid, npad_id); } -Result NPad::EnableUnintendedHomeButtonInputProtection(u64 aruid, Core::HID::NpadIdType npad_id, - bool is_enabled) { +Result NPad::EnableUnintendedHomeButtonInputProtection(u64 aruid, Core::HID::NpadIdType npad_id, bool is_enabled) { std::scoped_lock lock{mutex}; return npad_resource.SetHomeProtectionEnabled(aruid, npad_id, is_enabled); } @@ -1049,29 +1057,29 @@ Core::HID::NpadButton NPad::GetAndResetPressState() { return static_cast(press_state.exchange(0)); } -Result NPad::ApplyNpadSystemCommonPolicy(u64 aruid) { +Result NPad::ApplyNpadSystemCommonPolicy(Kernel::KernelCore& kernel, u64 aruid) { std::scoped_lock lock{mutex}; const Result result = npad_resource.ApplyNpadSystemCommonPolicy(aruid, false); if (result.IsSuccess()) { - OnUpdate({}); + OnUpdate(kernel, {}); } return result; } -Result NPad::ApplyNpadSystemCommonPolicyFull(u64 aruid) { +Result NPad::ApplyNpadSystemCommonPolicyFull(Kernel::KernelCore& kernel, u64 aruid) { std::scoped_lock lock{mutex}; const Result result = npad_resource.ApplyNpadSystemCommonPolicy(aruid, true); if (result.IsSuccess()) { - OnUpdate({}); + OnUpdate(kernel, {}); } return result; } -Result NPad::ClearNpadSystemCommonPolicy(u64 aruid) { +Result NPad::ClearNpadSystemCommonPolicy(Kernel::KernelCore& kernel, u64 aruid) { std::scoped_lock lock{mutex}; const Result result = npad_resource.ClearNpadSystemCommonPolicy(aruid); if (result.IsSuccess()) { - OnUpdate({}); + OnUpdate(kernel, {}); } return result; } @@ -1139,7 +1147,11 @@ NPad::NpadControllerData& NPad::GetControllerFromNpadIdType(u64 aruid, npad_id = Core::HID::NpadIdType::Player1; } const auto npad_index = NpadIdTypeToIndex(npad_id); - const auto aruid_index = applet_resource_holder.applet_resource->GetIndexFromAruid(aruid); + auto aruid_index = applet_resource_holder.applet_resource->GetIndexFromAruid(aruid); + if (aruid_index >= AruidIndexMax) { + LOG_ERROR(Service_HID, "Invalid aruid:{:016X}", aruid); + aruid_index = 0; + } return controller_data[aruid_index][npad_index]; } @@ -1150,7 +1162,11 @@ const NPad::NpadControllerData& NPad::GetControllerFromNpadIdType( npad_id = Core::HID::NpadIdType::Player1; } const auto npad_index = NpadIdTypeToIndex(npad_id); - const auto aruid_index = applet_resource_holder.applet_resource->GetIndexFromAruid(aruid); + auto aruid_index = applet_resource_holder.applet_resource->GetIndexFromAruid(aruid); + if (aruid_index >= AruidIndexMax) { + LOG_ERROR(Service_HID, "Invalid aruid:{:016X}", aruid); + aruid_index = 0; + } return controller_data[aruid_index][npad_index]; } diff --git a/src/hid_core/resources/npad/npad.h b/src/hid_core/resources/npad/npad.h index 99e761127d..3e10f02a7c 100644 --- a/src/hid_core/resources/npad/npad.h +++ b/src/hid_core/resources/npad/npad.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -61,37 +64,31 @@ public: void FreeAppletResourceId(u64 aruid); // When the controller is requesting an update for the shared memory - void OnUpdate(const Core::Timing::CoreTiming& core_timing); + void OnUpdate(Kernel::KernelCore& kernel, const Core::Timing::CoreTiming& core_timing); - Result SetSupportedNpadStyleSet(u64 aruid, Core::HID::NpadStyleSet supported_style_set); - Result GetSupportedNpadStyleSet(u64 aruid, - Core::HID::NpadStyleSet& out_supported_style_set) const; - Result GetMaskedSupportedNpadStyleSet(u64 aruid, - Core::HID::NpadStyleSet& out_supported_style_set) const; - - Result SetSupportedNpadIdType(u64 aruid, - std::span supported_npad_list); + Result SetSupportedNpadStyleSet(Kernel::KernelCore& kernel, u64 aruid, Core::HID::NpadStyleSet supported_style_set); + Result GetSupportedNpadStyleSet(Kernel::KernelCore& kernel, u64 aruid, Core::HID::NpadStyleSet& out_supported_style_set) const; + Result GetMaskedSupportedNpadStyleSet(Kernel::KernelCore& kernel, u64 aruid, Core::HID::NpadStyleSet& out_supported_style_set) const; + Result SetSupportedNpadIdType(Kernel::KernelCore& kernel, u64 aruid, std::span supported_npad_list); Result SetNpadJoyHoldType(u64 aruid, NpadJoyHoldType hold_type); Result GetNpadJoyHoldType(u64 aruid, NpadJoyHoldType& out_hold_type) const; - Result SetNpadHandheldActivationMode(u64 aruid, NpadHandheldActivationMode mode); - Result GetNpadHandheldActivationMode(u64 aruid, NpadHandheldActivationMode& out_mode) const; + Result SetNpadHandheldActivationMode(Kernel::KernelCore& kernel, u64 aruid, NpadHandheldActivationMode mode); + Result GetNpadHandheldActivationMode(Kernel::KernelCore& kernel, u64 aruid, NpadHandheldActivationMode& out_mode) const; - bool SetNpadMode(u64 aruid, Core::HID::NpadIdType& new_npad_id, Core::HID::NpadIdType npad_id, + bool SetNpadMode(Kernel::KernelCore& kernel, u64 aruid, Core::HID::NpadIdType& new_npad_id, Core::HID::NpadIdType npad_id, NpadJoyDeviceType npad_device_type, NpadJoyAssignmentMode assignment_mode); - Result AcquireNpadStyleSetUpdateEventHandle(u64 aruid, Kernel::KReadableEvent** out_event, + Result AcquireNpadStyleSetUpdateEventHandle(Kernel::KernelCore& kernel, u64 aruid, Kernel::KReadableEvent** out_event, Core::HID::NpadIdType npad_id); // Adds a new controller at an index. - void AddNewControllerAt(u64 aruid, Core::HID::NpadStyleIndex controller, - Core::HID::NpadIdType npad_id); + void AddNewControllerAt(Kernel::KernelCore& kernel, u64 aruid, Core::HID::NpadStyleIndex controller, Core::HID::NpadIdType npad_id); // Adds a new controller at an index with connection status. - void UpdateControllerAt(u64 aruid, Core::HID::NpadStyleIndex controller, - Core::HID::NpadIdType npad_id, bool connected); + void UpdateControllerAt(Kernel::KernelCore& kernel, u64 aruid, Core::HID::NpadStyleIndex controller, Core::HID::NpadIdType npad_id, bool connected); - Result DisconnectNpad(u64 aruid, Core::HID::NpadIdType npad_id); + Result DisconnectNpad(Kernel::KernelCore& kernel, u64 aruid, Core::HID::NpadIdType npad_id); Result IsFirmwareUpdateAvailableForSixAxisSensor( u64 aruid, const Core::HID::SixAxisSensorHandle& sixaxis_handle, @@ -110,20 +107,18 @@ public: void ConnectAllDisconnectedControllers(); void ClearAllControllers(); - Result MergeSingleJoyAsDualJoy(u64 aruid, Core::HID::NpadIdType npad_id_1, - Core::HID::NpadIdType npad_id_2); + Result MergeSingleJoyAsDualJoy(Kernel::KernelCore& kernel, u64 aruid, Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2); Result StartLrAssignmentMode(u64 aruid); Result StopLrAssignmentMode(u64 aruid); - Result SwapNpadAssignment(u64 aruid, Core::HID::NpadIdType npad_id_1, - Core::HID::NpadIdType npad_id_2); + Result SwapNpadAssignment(Kernel::KernelCore& kernel, u64 aruid, Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2); // Logical OR for all buttons presses on all controllers // Specifically for cheat engine and other features. Core::HID::NpadButton GetAndResetPressState(); - Result ApplyNpadSystemCommonPolicy(u64 aruid); - Result ApplyNpadSystemCommonPolicyFull(u64 aruid); - Result ClearNpadSystemCommonPolicy(u64 aruid); + Result ApplyNpadSystemCommonPolicy(Kernel::KernelCore& kernel, u64 aruid); + Result ApplyNpadSystemCommonPolicyFull(Kernel::KernelCore& kernel, u64 aruid); + Result ClearNpadSystemCommonPolicy(Kernel::KernelCore& kernel, u64 aruid); void SetRevision(u64 aruid, NpadRevision revision); NpadRevision GetRevision(u64 aruid); @@ -180,9 +175,9 @@ private: int callback_key{}; }; - void ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t controller_idx); - void InitNewlyAddedController(u64 aruid, Core::HID::NpadIdType npad_id); - void RequestPadStateUpdate(u64 aruid, Core::HID::NpadIdType npad_id); + void ControllerUpdate(Kernel::KernelCore& kernel, Core::HID::ControllerTriggerType type, std::size_t controller_idx); + void InitNewlyAddedController(Kernel::KernelCore& kernel, u64 aruid, Core::HID::NpadIdType npad_id); + void RequestPadStateUpdate(Kernel::KernelCore& kernel, u64 aruid, Core::HID::NpadIdType npad_id); void WriteEmptyEntry(NpadInternalState* npad); NpadControllerData& GetControllerFromHandle( @@ -209,7 +204,6 @@ private: NpadVibration vibration_handler{}; std::atomic press_state{}; - std::array, AruidIndexMax> - controller_data{}; + std::array, AruidIndexMax> controller_data{}; }; } // namespace Service::HID diff --git a/src/hid_core/resources/npad/npad_resource.cpp b/src/hid_core/resources/npad/npad_resource.cpp index 08546f8dc4..54f7acab12 100644 --- a/src/hid_core/resources/npad/npad_resource.cpp +++ b/src/hid_core/resources/npad/npad_resource.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project @@ -13,7 +13,9 @@ namespace Service::HID { -NPadResource::NPadResource(KernelHelpers::ServiceContext& context) : service_context{context} {} +NPadResource::NPadResource(Kernel::KernelCore& kernel_, KernelHelpers::ServiceContext& context) + : service_context{context} +{} NPadResource::~NPadResource() = default; @@ -505,9 +507,7 @@ Result NPadResource::IsAssigningSingleOnSlSrPressEnabled(bool& is_enabled, u64 a return ResultSuccess; } -Result NPadResource::AcquireNpadStyleSetUpdateEventHandle(u64 aruid, - Kernel::KReadableEvent** out_event, - Core::HID::NpadIdType npad_id) { +Result NPadResource::AcquireNpadStyleSetUpdateEventHandle(Kernel::KernelCore& kernel, u64 aruid, Kernel::KReadableEvent** out_event, Core::HID::NpadIdType npad_id) { const u64 aruid_index = GetIndexFromAruid(aruid); if (aruid_index >= AruidIndexMax) { return ResultNpadNotConnected; @@ -526,26 +526,25 @@ Result NPadResource::AcquireNpadStyleSetUpdateEventHandle(u64 aruid, *out_event = &controller_state.style_set_update_event->GetReadableEvent(); if (controller_state.is_styleset_update_event_initialized) { - controller_state.style_set_update_event->Signal(); + controller_state.style_set_update_event->Signal(kernel); } return ResultSuccess; } -Result NPadResource::SignalStyleSetUpdateEvent(u64 aruid, Core::HID::NpadIdType npad_id) { +Result NPadResource::SignalStyleSetUpdateEvent(Kernel::KernelCore& kernel, u64 aruid, Core::HID::NpadIdType npad_id) { const u64 aruid_index = GetIndexFromAruid(aruid); if (aruid_index >= AruidIndexMax) { return ResultNpadNotConnected; } - auto controller = state[aruid_index].controller_state[NpadIdTypeToIndex(npad_id)]; - if (controller.is_styleset_update_event_initialized) { - controller.style_set_update_event->Signal(); + auto& controller_state = state[aruid_index].controller_state[NpadIdTypeToIndex(npad_id)]; + if (controller_state.is_styleset_update_event_initialized) { + controller_state.style_set_update_event->Signal(kernel); } return ResultSuccess; } -Result NPadResource::GetHomeProtectionEnabled(bool& is_enabled, u64 aruid, - Core::HID::NpadIdType npad_id) const { +Result NPadResource::GetHomeProtectionEnabled(bool& is_enabled, u64 aruid, Core::HID::NpadIdType npad_id) const { const u64 aruid_index = GetIndexFromAruid(aruid); if (aruid_index >= AruidIndexMax) { return ResultNpadNotConnected; @@ -555,8 +554,7 @@ Result NPadResource::GetHomeProtectionEnabled(bool& is_enabled, u64 aruid, return ResultSuccess; } -Result NPadResource::SetHomeProtectionEnabled(u64 aruid, Core::HID::NpadIdType npad_id, - bool is_enabled) { +Result NPadResource::SetHomeProtectionEnabled(u64 aruid, Core::HID::NpadIdType npad_id, bool is_enabled) { const u64 aruid_index = GetIndexFromAruid(aruid); if (aruid_index >= AruidIndexMax) { return ResultNpadNotConnected; diff --git a/src/hid_core/resources/npad/npad_resource.h b/src/hid_core/resources/npad/npad_resource.h index 8ee5702fd4..2d381c4642 100644 --- a/src/hid_core/resources/npad/npad_resource.h +++ b/src/hid_core/resources/npad/npad_resource.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later @@ -20,6 +23,7 @@ class System; } namespace Kernel { +class KernelCore; class KReadableEvent; } @@ -37,8 +41,7 @@ struct NpadState { DataStatusFlag flag{}; u64 aruid{}; NPadData data{}; - std::array, MaxSupportedNpadIdTypes> - button_config; + std::array, MaxSupportedNpadIdTypes> button_config; std::array controller_state; NpadRevision npad_revision; }; @@ -46,7 +49,7 @@ struct NpadState { /// Handles Npad request from HID interfaces class NPadResource final { public: - explicit NPadResource(KernelHelpers::ServiceContext& context); + explicit NPadResource(Kernel::KernelCore& kernel, KernelHelpers::ServiceContext& context); ~NPadResource(); NPadData* GetActiveData(); @@ -81,11 +84,9 @@ public: Result GetNpadJoyHoldType(NpadJoyHoldType& hold_type, u64 aruid) const; Result SetNpadHandheldActivationMode(u64 aruid, NpadHandheldActivationMode activation_mode); - Result GetNpadHandheldActivationMode(NpadHandheldActivationMode& activation_mode, - u64 aruid) const; + Result GetNpadHandheldActivationMode(NpadHandheldActivationMode& activation_mode, u64 aruid) const; - Result SetSupportedNpadIdType(u64 aruid, - std::span supported_npad_list); + Result SetSupportedNpadIdType(u64 aruid, std::span supported_npad_list); bool IsControllerSupported(u64 aruid, Core::HID::NpadStyleIndex style_index) const; Result SetLrAssignmentMode(u64 aruid, bool is_enabled); @@ -94,28 +95,20 @@ public: Result SetAssigningSingleOnSlSrPress(u64 aruid, bool is_enabled); Result IsAssigningSingleOnSlSrPressEnabled(bool& is_enabled, u64 aruid) const; - Result AcquireNpadStyleSetUpdateEventHandle(u64 aruid, Kernel::KReadableEvent** out_event, - Core::HID::NpadIdType npad_id); - Result SignalStyleSetUpdateEvent(u64 aruid, Core::HID::NpadIdType npad_id); - - Result GetHomeProtectionEnabled(bool& is_enabled, u64 aruid, - Core::HID::NpadIdType npad_id) const; + Result AcquireNpadStyleSetUpdateEventHandle(Kernel::KernelCore& kernel, u64 aruid, Kernel::KReadableEvent** out_event, Core::HID::NpadIdType npad_id); + Result SignalStyleSetUpdateEvent(Kernel::KernelCore& kernel, u64 aruid, Core::HID::NpadIdType npad_id); + Result GetHomeProtectionEnabled(bool& is_enabled, u64 aruid, Core::HID::NpadIdType npad_id) const; Result SetHomeProtectionEnabled(u64 aruid, Core::HID::NpadIdType npad_id, bool is_enabled); Result SetNpadAnalogStickUseCenterClamp(u64 aruid, bool is_enabled); - Result SetButtonConfig(u64 aruid, Core::HID::NpadIdType npad_id, std::size_t index, - Core::HID::NpadButton button_config); - Core::HID::NpadButton GetButtonConfig(u64 aruid, Core::HID::NpadIdType npad_id, - std::size_t index, Core::HID::NpadButton mask, - bool is_enabled); + Result SetButtonConfig(u64 aruid, Core::HID::NpadIdType npad_id, std::size_t index, Core::HID::NpadButton button_config); + Core::HID::NpadButton GetButtonConfig(u64 aruid, Core::HID::NpadIdType npad_id, std::size_t index, Core::HID::NpadButton mask, bool is_enabled); void ResetButtonConfig(); - Result SetNpadCaptureButtonAssignment(u64 aruid, Core::HID::NpadStyleSet npad_style_set, - Core::HID::NpadButton button_assignment); + Result SetNpadCaptureButtonAssignment(u64 aruid, Core::HID::NpadStyleSet npad_style_set, Core::HID::NpadButton button_assignment); Result ClearNpadCaptureButtonAssignment(u64 aruid); - std::size_t GetNpadCaptureButtonAssignment(std::span out_list, - u64 aruid) const; + std::size_t GetNpadCaptureButtonAssignment(std::span out_list, u64 aruid) const; Result SetNpadSystemExtStateEnabled(u64 aruid, bool is_enabled); @@ -124,9 +117,8 @@ private: AruidRegisterList registration_list{}; std::array state{}; u64 active_data_aruid{}; + KernelHelpers::ServiceContext& service_context; NpadJoyHoldType default_hold_type{}; s32 ref_counter{}; - - KernelHelpers::ServiceContext& service_context; }; } // namespace Service::HID diff --git a/src/hid_core/resources/palma/palma.cpp b/src/hid_core/resources/palma/palma.cpp index 0652455f5f..1a1a3aae18 100644 --- a/src/hid_core/resources/palma/palma.cpp +++ b/src/hid_core/resources/palma/palma.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project @@ -77,7 +77,7 @@ Result Palma::PlayPalmaActivity(const PalmaConnectionHandle& handle, u64 palma_a operation.operation = PackedPalmaOperationType::PlayActivity; operation.result = PalmaResultSuccess; operation.data = {}; - operation_complete_event->Signal(); + operation_complete_event->Signal(hid_core.kernel); return ResultSuccess; } @@ -96,7 +96,7 @@ Result Palma::ReadPalmaStep(const PalmaConnectionHandle& handle) { operation.operation = PackedPalmaOperationType::ReadStep; operation.result = PalmaResultSuccess; operation.data = {}; - operation_complete_event->Signal(); + operation_complete_event->Signal(hid_core.kernel); return ResultSuccess; } @@ -125,7 +125,7 @@ Result Palma::ReadPalmaUniqueCode(const PalmaConnectionHandle& handle) { operation.operation = PackedPalmaOperationType::ReadUniqueCode; operation.result = PalmaResultSuccess; operation.data = {}; - operation_complete_event->Signal(); + operation_complete_event->Signal(hid_core.kernel); return ResultSuccess; } @@ -136,7 +136,7 @@ Result Palma::SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle& handle) { operation.operation = PackedPalmaOperationType::SetUniqueCodeInvalid; operation.result = PalmaResultSuccess; operation.data = {}; - operation_complete_event->Signal(); + operation_complete_event->Signal(hid_core.kernel); return ResultSuccess; } @@ -149,7 +149,7 @@ Result Palma::WritePalmaRgbLedPatternEntry(const PalmaConnectionHandle& handle, operation.operation = PackedPalmaOperationType::WriteRgbLedPatternEntry; operation.result = PalmaResultSuccess; operation.data = {}; - operation_complete_event->Signal(); + operation_complete_event->Signal(hid_core.kernel); return ResultSuccess; } @@ -161,7 +161,7 @@ Result Palma::WritePalmaWaveEntry(const PalmaConnectionHandle& handle, PalmaWave operation.operation = PackedPalmaOperationType::WriteWaveEntry; operation.result = PalmaResultSuccess; operation.data = {}; - operation_complete_event->Signal(); + operation_complete_event->Signal(hid_core.kernel); return ResultSuccess; } @@ -174,7 +174,7 @@ Result Palma::SetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& operation.operation = PackedPalmaOperationType::ReadDataBaseIdentificationVersion; operation.result = PalmaResultSuccess; operation.data[0] = {}; - operation_complete_event->Signal(); + operation_complete_event->Signal(hid_core.kernel); return ResultSuccess; } @@ -186,7 +186,7 @@ Result Palma::GetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& operation.result = PalmaResultSuccess; operation.data = {}; operation.data[0] = static_cast(database_id_version); - operation_complete_event->Signal(); + operation_complete_event->Signal(hid_core.kernel); return ResultSuccess; } diff --git a/src/hid_core/resources/shared_memory_holder.cpp b/src/hid_core/resources/shared_memory_holder.cpp index ada593d8ba..c94829821a 100644 --- a/src/hid_core/resources/shared_memory_holder.cpp +++ b/src/hid_core/resources/shared_memory_holder.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later @@ -9,17 +12,10 @@ #include "hid_core/resources/shared_memory_holder.h" namespace Service::HID { -SharedMemoryHolder::SharedMemoryHolder() {} - -SharedMemoryHolder::~SharedMemoryHolder() { - Finalize(); -} Result SharedMemoryHolder::Initialize(Core::System& system) { shared_memory = Kernel::KSharedMemory::Create(system.Kernel()); - const Result result = shared_memory->Initialize( - system.DeviceMemory(), nullptr, Kernel::Svc::MemoryPermission::None, - Kernel::Svc::MemoryPermission::Read, sizeof(SharedMemoryFormat)); + const Result result = shared_memory->Initialize(system.Kernel(), system.DeviceMemory(), nullptr, Kernel::Svc::MemoryPermission::None, Kernel::Svc::MemoryPermission::Read, sizeof(SharedMemoryFormat)); if (result.IsError()) { return result; } @@ -31,9 +27,9 @@ Result SharedMemoryHolder::Initialize(Core::System& system) { return ResultSuccess; } -void SharedMemoryHolder::Finalize() { +void SharedMemoryHolder::Finalize(Core::System& system) { if (address != nullptr) { - shared_memory->Close(); + shared_memory->Close(system.Kernel()); } is_created = false; is_mapped = false; diff --git a/src/hid_core/resources/shared_memory_holder.h b/src/hid_core/resources/shared_memory_holder.h index 943407c00a..5fcaddecc0 100644 --- a/src/hid_core/resources/shared_memory_holder.h +++ b/src/hid_core/resources/shared_memory_holder.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later @@ -19,17 +22,13 @@ struct SharedMemoryFormat; // This is nn::hid::detail::SharedMemoryHolder class SharedMemoryHolder { public: - SharedMemoryHolder(); - ~SharedMemoryHolder(); - Result Initialize(Core::System& system); - void Finalize(); + void Finalize(Core::System& system); bool IsMapped(); SharedMemoryFormat* GetAddress(); Kernel::KSharedMemory* GetHandle(); -private: bool is_owner{}; bool is_created{}; bool is_mapped{}; diff --git a/src/hid_core/resources/touch_screen/touch_screen_resource.cpp b/src/hid_core/resources/touch_screen/touch_screen_resource.cpp index 5d77fe5719..d340f186b3 100644 --- a/src/hid_core/resources/touch_screen/touch_screen_resource.cpp +++ b/src/hid_core/resources/touch_screen/touch_screen_resource.cpp @@ -486,27 +486,17 @@ void TouchResource::ReadTouchInput() { SanitizeInput(current_touch_state); std::scoped_lock lock{*input_mutex}; - if (current_touch_state.entry_count == previous_touch_state.entry_count) { - if (current_touch_state.entry_count < 1) { - return; - } + if (current_touch_state.entry_count == previous_touch_state.entry_count && current_touch_state.entry_count >= 1) { bool has_moved = false; - for (std::size_t i = 0; i < static_cast(current_touch_state.entry_count); - i++) { - s32 delta_x = std::abs(static_cast(current_touch_state.states[i].position.x) - - static_cast(previous_touch_state.states[i].position.x)); - s32 delta_y = std::abs(static_cast(current_touch_state.states[i].position.y) - - static_cast(previous_touch_state.states[i].position.y)); - if (delta_x > 1 || delta_y > 1) { - has_moved = true; - } + for (std::size_t i = 0; !has_moved && i < std::size_t(current_touch_state.entry_count); i++) { + s32 delta_x = std::abs(s32(current_touch_state.states[i].position.x) - s32(previous_touch_state.states[i].position.x)); + s32 delta_y = std::abs(s32(current_touch_state.states[i].position.y) - s32(previous_touch_state.states[i].position.y)); + has_moved |= (delta_x > 1 || delta_y > 1); } - if (!has_moved) { - return; + if (has_moved) { + input_event->Signal(system.Kernel()); } } - - input_event->Signal(); } void TouchResource::OnTouchUpdate(s64 timestamp) { diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt index 406ee967b0..91dd8558ab 100644 --- a/src/input_common/CMakeLists.txt +++ b/src/input_common/CMakeLists.txt @@ -79,8 +79,8 @@ else() helpers/joycon_protocol/rumble.cpp helpers/joycon_protocol/rumble.h) - target_link_libraries(input_common PRIVATE SDL2::SDL2) - target_compile_definitions(input_common PRIVATE HAVE_SDL2) + target_link_libraries(input_common PRIVATE SDL3::SDL3) + target_compile_definitions(input_common PRIVATE HAVE_SDL3) endif() if (ENABLE_LIBUSB) diff --git a/src/input_common/drivers/joycon.h b/src/input_common/drivers/joycon.h index 112e970e15..820c4b2eaa 100644 --- a/src/input_common/drivers/joycon.h +++ b/src/input_common/drivers/joycon.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -6,7 +9,7 @@ #include #include #include -#include +#include #include "input_common/input_engine.h" diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp index 948f650fb1..ad854110ef 100644 --- a/src/input_common/drivers/sdl_driver.cpp +++ b/src/input_common/drivers/sdl_driver.cpp @@ -15,29 +15,79 @@ namespace InputCommon { namespace { Common::UUID GetGUID(SDL_Joystick* joystick) { - const SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick); + const SDL_GUID guid = SDL_GetJoystickGUID(joystick); std::array data{}; std::memcpy(data.data(), guid.data, sizeof(data)); // Clear controller name crc std::memset(data.data() + 2, 0, sizeof(u16)); return Common::UUID{data}; } + +using GamepadBindings = std::vector; + +SDL_GamepadBinding EmptyBinding() { + SDL_GamepadBinding binding{}; + binding.input_type = SDL_GAMEPAD_BINDTYPE_NONE; + return binding; +} + +GamepadBindings GetBindings(SDL_Gamepad* controller) { + if (controller == nullptr) { + return {}; + } + + int binding_count = 0; + SDL_GamepadBinding** bindings = SDL_GetGamepadBindings(controller, &binding_count); + if (bindings == nullptr) { + return {}; + } + + GamepadBindings cached_bindings{}; + cached_bindings.reserve(static_cast(binding_count)); + for (int i = 0; i < binding_count; ++i) { + if (const auto* current = bindings[i]) { + cached_bindings.emplace_back(*current); + } + } + SDL_free(bindings); + return cached_bindings; +} + +template +SDL_GamepadBinding FindBinding(const GamepadBindings& bindings, Predicate matches) { + const auto it = std::find_if(bindings.begin(), bindings.end(), matches); + return it != bindings.end() ? *it : EmptyBinding(); +} + +SDL_GamepadBinding GetBindingForButton(const GamepadBindings& bindings, SDL_GamepadButton button) { + return FindBinding(bindings, [button](const SDL_GamepadBinding& current) { + return current.output_type == SDL_GAMEPAD_BINDTYPE_BUTTON && + current.output.button == static_cast(button); + }); +} + +SDL_GamepadBinding GetBindingForAxis(const GamepadBindings& bindings, SDL_GamepadAxis axis) { + return FindBinding(bindings, [axis](const SDL_GamepadBinding& current) { + return current.output_type == SDL_GAMEPAD_BINDTYPE_AXIS && + current.output.axis.axis == static_cast(axis); + }); +} } // Anonymous namespace -static int SDLEventWatcher(void* user_data, SDL_Event* event) { +static bool SDLEventWatcher(void* user_data, SDL_Event* event) { auto* const sdl_state = static_cast(user_data); sdl_state->HandleGameControllerEvent(*event); - return 0; + return true; } class SDLJoystick { public: SDLJoystick(Common::UUID guid_, int port_, SDL_Joystick* joystick, - SDL_GameController* game_controller) - : guid{guid_}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose}, - sdl_controller{game_controller, &SDL_GameControllerClose} { + SDL_Gamepad* game_controller) + : guid{guid_}, port{port_}, sdl_joystick{joystick, &SDL_CloseJoystick}, + sdl_controller{game_controller, &SDL_CloseGamepad} { EnableMotion(); } @@ -45,30 +95,49 @@ public: if (!sdl_controller) { return; } - SDL_GameController* controller = sdl_controller.get(); + SDL_Gamepad* controller = sdl_controller.get(); if (HasMotion()) { - SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_ACCEL, SDL_FALSE); - SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_GYRO, SDL_FALSE); + SDL_SetGamepadSensorEnabled(controller, SDL_SENSOR_ACCEL, false); + SDL_SetGamepadSensorEnabled(controller, SDL_SENSOR_GYRO, false); } - has_accel = SDL_GameControllerHasSensor(controller, SDL_SENSOR_ACCEL) == SDL_TRUE; - has_gyro = SDL_GameControllerHasSensor(controller, SDL_SENSOR_GYRO) == SDL_TRUE; + has_accel = SDL_GamepadHasSensor(controller, SDL_SENSOR_ACCEL); + has_gyro = SDL_GamepadHasSensor(controller, SDL_SENSOR_GYRO); if (has_accel) { - SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_ACCEL, SDL_TRUE); + if (!SDL_SetGamepadSensorEnabled(controller, SDL_SENSOR_ACCEL, true)) { + LOG_WARNING(Input, "Failed to enable accelerometer sensor: {}", SDL_GetError()); + } } if (has_gyro) { - SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_GYRO, SDL_TRUE); + if (!SDL_SetGamepadSensorEnabled(controller, SDL_SENSOR_GYRO, true)) { + LOG_WARNING(Input, "Failed to enable gyroscope sensor: {}", SDL_GetError()); + } } + + LOG_INFO(Input, "Controller motion capabilities: accel={} gyro={}", has_accel, has_gyro); } bool HasMotion() const { return has_gyro || has_accel; } - bool UpdateMotion(SDL_ControllerSensorEvent event) { + bool UpdateMotion(SDL_GamepadSensorEvent event) { constexpr float gravity_constant = 9.80665f; std::scoped_lock lock{mutex}; - const u64 time_difference = event.timestamp - last_motion_update; - last_motion_update = event.timestamp; + const u64 sensor_timestamp = event.sensor_timestamp != 0 ? event.sensor_timestamp + : event.timestamp; + + if (last_motion_update == 0) { + last_motion_update = sensor_timestamp; + return false; + } + if (sensor_timestamp < last_motion_update) { + return false; + } + + // SDL3 reports sensor timestamps in nanoseconds, while the input stack expects + // delta timestamps in microseconds. + const u64 time_difference = (sensor_timestamp - last_motion_update) / 1000; + last_motion_update = sensor_timestamp; switch (event.sensor) { case SDL_SENSOR_ACCEL: { motion.accel_x = -event.data[0] / gravity_constant; @@ -102,7 +171,7 @@ public: } motion_error_count = 0; - motion.delta_timestamp = time_difference * 1000; + motion.delta_timestamp = time_difference; return true; } @@ -136,26 +205,41 @@ public: f32 high_amplitude = vibration.high_amplitude * high_frequency_scale; if (sdl_controller) { - return SDL_GameControllerRumble(sdl_controller.get(), static_cast(low_amplitude), - static_cast(high_amplitude), - rumble_max_duration_ms) != -1; + return SDL_RumbleGamepad(sdl_controller.get(), static_cast(low_amplitude), + static_cast(high_amplitude), rumble_max_duration_ms); } else if (sdl_joystick) { - return SDL_JoystickRumble(sdl_joystick.get(), static_cast(low_amplitude), + return SDL_RumbleJoystick(sdl_joystick.get(), static_cast(low_amplitude), static_cast(high_amplitude), - rumble_max_duration_ms) != -1; + rumble_max_duration_ms); } return false; } bool HasHDRumble() const { + constexpr Uint16 valve_vendor_id = 0x28DE; + const auto is_known_hd_type = [](SDL_GamepadType type) { + return type == SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO || + type == SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT || + type == SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT || + type == SDL_GAMEPAD_TYPE_PS5; + }; + + // Valve hardware doesn't have any enums in SDL, so we have to support it manually. + // Since they have HD rumble, we can assume that all their hardware supports it, even if we can't detect the exact type. if (sdl_controller) { - const auto type = SDL_GameControllerGetType(sdl_controller.get()); - return (type == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO) || - (type == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT) || - (type == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT) || - (type == SDL_CONTROLLER_TYPE_PS5); + if (is_known_hd_type(SDL_GetGamepadType(sdl_controller.get())) || + SDL_GetGamepadVendor(sdl_controller.get()) == valve_vendor_id) { + return true; + } } + + if (sdl_joystick) { + if (SDL_GetJoystickVendor(sdl_joystick.get()) == valve_vendor_id) { + return true; + } + } + return false; } @@ -201,11 +285,11 @@ public: return sdl_joystick.get(); } - SDL_GameController* GetSDLGameController() const { + SDL_Gamepad* GetSDLGameController() const { return sdl_controller.get(); } - void SetSDLJoystick(SDL_Joystick* joystick, SDL_GameController* controller) { + void SetSDLJoystick(SDL_Joystick* joystick, SDL_Gamepad* controller) { sdl_joystick.reset(joystick); sdl_controller.reset(controller); } @@ -232,20 +316,36 @@ public: return false; } - Common::Input::BatteryLevel GetBatteryLevel(SDL_JoystickPowerLevel battery_level) { - switch (battery_level) { - case SDL_JOYSTICK_POWER_EMPTY: - return Common::Input::BatteryLevel::Empty; - case SDL_JOYSTICK_POWER_LOW: - return Common::Input::BatteryLevel::Low; - case SDL_JOYSTICK_POWER_MEDIUM: - return Common::Input::BatteryLevel::Medium; - case SDL_JOYSTICK_POWER_FULL: - case SDL_JOYSTICK_POWER_MAX: - return Common::Input::BatteryLevel::Full; - case SDL_JOYSTICK_POWER_WIRED: + Common::Input::BatteryLevel GetBatteryLevel(SDL_PowerState battery_level, int percent) { + if (battery_level == SDL_POWERSTATE_CHARGING) { return Common::Input::BatteryLevel::Charging; - case SDL_JOYSTICK_POWER_UNKNOWN: + } + + if (percent >= 0 && percent <= 100) { + if (percent <= 5) { + return Common::Input::BatteryLevel::Empty; + } + if (percent <= 20) { + return Common::Input::BatteryLevel::Critical; + } + if (percent <= 40) { + return Common::Input::BatteryLevel::Low; + } + if (percent <= 70) { + return Common::Input::BatteryLevel::Medium; + } + return Common::Input::BatteryLevel::Full; + } + + switch (battery_level) { + case SDL_POWERSTATE_ON_BATTERY: + return Common::Input::BatteryLevel::Medium; + case SDL_POWERSTATE_NO_BATTERY: + return Common::Input::BatteryLevel::None; + case SDL_POWERSTATE_CHARGED: + return Common::Input::BatteryLevel::Full; + case SDL_POWERSTATE_ERROR: + case SDL_POWERSTATE_UNKNOWN: default: return Common::Input::BatteryLevel::None; } @@ -253,28 +353,28 @@ public: std::string GetControllerName() const { if (sdl_controller) { - switch (SDL_GameControllerGetType(sdl_controller.get())) { - case SDL_CONTROLLER_TYPE_XBOX360: + switch (SDL_GetGamepadType(sdl_controller.get())) { + case SDL_GAMEPAD_TYPE_XBOX360: return "Xbox 360 Controller"; - case SDL_CONTROLLER_TYPE_XBOXONE: + case SDL_GAMEPAD_TYPE_XBOXONE: return "Xbox One Controller"; - case SDL_CONTROLLER_TYPE_PS3: + case SDL_GAMEPAD_TYPE_PS3: return "DualShock 3 Controller"; - case SDL_CONTROLLER_TYPE_PS4: + case SDL_GAMEPAD_TYPE_PS4: return "DualShock 4 Controller"; - case SDL_CONTROLLER_TYPE_PS5: + case SDL_GAMEPAD_TYPE_PS5: return "DualSense Controller"; default: break; } - const auto name = SDL_GameControllerName(sdl_controller.get()); + const auto name = SDL_GetGamepadName(sdl_controller.get()); if (name) { return name; } } if (sdl_joystick) { - const auto name = SDL_JoystickName(sdl_joystick.get()); + const auto name = SDL_GetJoystickName(sdl_joystick.get()); if (name) { return name; } @@ -286,8 +386,8 @@ public: private: Common::UUID guid; int port; - std::unique_ptr sdl_joystick; - std::unique_ptr sdl_controller; + std::unique_ptr sdl_joystick; + std::unique_ptr sdl_controller; mutable std::mutex mutex; u64 last_motion_update{}; @@ -323,7 +423,10 @@ std::shared_ptr SDLDriver::GetSDLJoystickByGUID(const std::string& } std::shared_ptr SDLDriver::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) { - auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id); + auto sdl_joystick = SDL_GetJoystickFromID(sdl_id); + if (sdl_joystick == nullptr) { + return nullptr; + } const auto guid = GetGUID(sdl_joystick); std::scoped_lock lock{joystick_map_mutex}; @@ -345,34 +448,70 @@ std::shared_ptr SDLDriver::GetSDLJoystickBySDLID(SDL_JoystickID sdl return *vec_it; } -void SDLDriver::InitJoystick(int joystick_index) { - SDL_Joystick* sdl_joystick = SDL_JoystickOpen(joystick_index); - SDL_GameController* sdl_gamecontroller = nullptr; +std::shared_ptr SDLDriver::GetSDLJoystickByGamepadID(SDL_JoystickID sdl_id) { + auto* const sdl_gamepad = SDL_GetGamepadFromID(sdl_id); + if (sdl_gamepad == nullptr) { + return nullptr; + } - if (SDL_IsGameController(joystick_index)) { - sdl_gamecontroller = SDL_GameControllerOpen(joystick_index); + auto* const sdl_joystick = SDL_GetGamepadJoystick(sdl_gamepad); + if (sdl_joystick == nullptr) { + return nullptr; + } + + const auto guid = GetGUID(sdl_joystick); + + std::scoped_lock lock{joystick_map_mutex}; + const auto map_it = joystick_map.find(guid); + if (map_it == joystick_map.end()) { + return nullptr; + } + + const auto vec_it = std::find_if(map_it->second.begin(), map_it->second.end(), + [sdl_joystick, sdl_gamepad](const auto& joystick) { + return joystick->GetSDLJoystick() == sdl_joystick || + joystick->GetSDLGameController() == sdl_gamepad; + }); + + if (vec_it == map_it->second.end()) { + return nullptr; + } + + return *vec_it; +} + +void SDLDriver::InitJoystick(SDL_JoystickID joystick_id) { + SDL_Joystick* sdl_joystick = SDL_OpenJoystick(joystick_id); + SDL_Gamepad* sdl_gamecontroller = nullptr; + int battery_percent = -1; + SDL_PowerState battery_state = SDL_POWERSTATE_UNKNOWN; + + if (SDL_IsGamepad(joystick_id)) { + sdl_gamecontroller = SDL_OpenGamepad(joystick_id); } if (!sdl_joystick) { - LOG_ERROR(Input, "Failed to open joystick {}", joystick_index); + LOG_ERROR(Input, "Failed to open joystick {}", joystick_id); return; } + battery_state = SDL_GetJoystickPowerInfo(sdl_joystick, &battery_percent); + const auto guid = GetGUID(sdl_joystick); if (Settings::values.enable_joycon_driver) { if (guid.uuid[5] == 0x05 && guid.uuid[4] == 0x7e && (guid.uuid[8] == 0x06 || guid.uuid[8] == 0x07)) { - LOG_WARNING(Input, "Preferring joycon driver for device index {}", joystick_index); - SDL_JoystickClose(sdl_joystick); + LOG_WARNING(Input, "Preferring joycon driver for device index {}", joystick_id); + SDL_CloseJoystick(sdl_joystick); return; } } if (Settings::values.enable_procon_driver) { if (guid.uuid[5] == 0x05 && guid.uuid[4] == 0x7e && guid.uuid[8] == 0x09) { - LOG_WARNING(Input, "Preferring joycon driver for device index {}", joystick_index); - SDL_JoystickClose(sdl_joystick); + LOG_WARNING(Input, "Preferring joycon driver for device index {}", joystick_id); + SDL_CloseJoystick(sdl_joystick); return; } } @@ -382,6 +521,8 @@ void SDLDriver::InitJoystick(int joystick_index) { auto joystick = std::make_shared(guid, 0, sdl_joystick, sdl_gamecontroller); PreSetController(joystick->GetPadIdentifier()); joystick->EnableMotion(); + SetBattery(joystick->GetPadIdentifier(), + joystick->GetBatteryLevel(battery_state, battery_percent)); joystick_map[guid].emplace_back(std::move(joystick)); return; } @@ -394,6 +535,8 @@ void SDLDriver::InitJoystick(int joystick_index) { if (joystick_it != joystick_guid_list.end()) { (*joystick_it)->SetSDLJoystick(sdl_joystick, sdl_gamecontroller); (*joystick_it)->EnableMotion(); + SetBattery((*joystick_it)->GetPadIdentifier(), + (*joystick_it)->GetBatteryLevel(battery_state, battery_percent)); return; } @@ -401,6 +544,8 @@ void SDLDriver::InitJoystick(int joystick_index) { auto joystick = std::make_shared(guid, port, sdl_joystick, sdl_gamecontroller); PreSetController(joystick->GetPadIdentifier()); joystick->EnableMotion(); + SetBattery(joystick->GetPadIdentifier(), + joystick->GetBatteryLevel(battery_state, battery_percent)); joystick_guid_list.emplace_back(std::move(joystick)); } @@ -428,55 +573,60 @@ void SDLDriver::PumpEvents() const { void SDLDriver::HandleGameControllerEvent(const SDL_Event& event) { switch (event.type) { - case SDL_JOYBUTTONUP: { + case SDL_EVENT_JOYSTICK_BUTTON_UP: { if (const auto joystick = GetSDLJoystickBySDLID(event.jbutton.which)) { const PadIdentifier identifier = joystick->GetPadIdentifier(); SetButton(identifier, event.jbutton.button, false); } break; } - case SDL_JOYBUTTONDOWN: { + case SDL_EVENT_JOYSTICK_BUTTON_DOWN: { if (const auto joystick = GetSDLJoystickBySDLID(event.jbutton.which)) { const PadIdentifier identifier = joystick->GetPadIdentifier(); SetButton(identifier, event.jbutton.button, true); } break; } - case SDL_JOYHATMOTION: { + case SDL_EVENT_JOYSTICK_HAT_MOTION: { if (const auto joystick = GetSDLJoystickBySDLID(event.jhat.which)) { const PadIdentifier identifier = joystick->GetPadIdentifier(); SetHatButton(identifier, event.jhat.hat, event.jhat.value); } break; } - case SDL_JOYAXISMOTION: { + case SDL_EVENT_JOYSTICK_AXIS_MOTION: { if (const auto joystick = GetSDLJoystickBySDLID(event.jaxis.which)) { const PadIdentifier identifier = joystick->GetPadIdentifier(); SetAxis(identifier, event.jaxis.axis, event.jaxis.value / 32767.0f); } break; } - case SDL_CONTROLLERSENSORUPDATE: { - if (auto joystick = GetSDLJoystickBySDLID(event.csensor.which)) { - if (joystick->UpdateMotion(event.csensor)) { + case SDL_EVENT_GAMEPAD_SENSOR_UPDATE: { + auto joystick = GetSDLJoystickByGamepadID(event.gsensor.which); + if (!joystick) { + joystick = GetSDLJoystickBySDLID(event.gsensor.which); + } + if (joystick) { + if (joystick->UpdateMotion(event.gsensor)) { const PadIdentifier identifier = joystick->GetPadIdentifier(); SetMotion(identifier, 0, joystick->GetMotion()); } } break; } - case SDL_JOYBATTERYUPDATED: { + case SDL_EVENT_JOYSTICK_BATTERY_UPDATED: { if (auto joystick = GetSDLJoystickBySDLID(event.jbattery.which)) { const PadIdentifier identifier = joystick->GetPadIdentifier(); - SetBattery(identifier, joystick->GetBatteryLevel(event.jbattery.level)); + SetBattery(identifier, + joystick->GetBatteryLevel(event.jbattery.state, event.jbattery.percent)); } break; } - case SDL_JOYDEVICEREMOVED: + case SDL_EVENT_JOYSTICK_REMOVED: LOG_DEBUG(Input, "Controller removed with Instance_ID {}", event.jdevice.which); - CloseJoystick(SDL_JoystickFromInstanceID(event.jdevice.which)); + CloseJoystick(SDL_GetJoystickFromID(event.jdevice.which)); break; - case SDL_JOYDEVICEADDED: + case SDL_EVENT_JOYSTICK_ADDED: LOG_DEBUG(Input, "Controller connected with device index {}", event.jdevice.which); InitJoystick(event.jdevice.which); break; @@ -496,12 +646,13 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en // Disable raw input. When enabled this setting causes SDL to die when a web applet opens SDL_SetHint(SDL_HINT_JOYSTICK_RAWINPUT, Settings::values.enable_raw_input ? "1" : "0"); - // Prevent SDL from adding undesired axis - SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0"); + // SDL3 defaults Steam Controller Bluetooth HIDAPI support to off, which can disable gyro. + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_STEAM, "1"); + SDL_SetHint(SDL_HINT_GAMECONTROLLER_SENSOR_FUSION, "1"); + SDL_SetHint(SDL_HINT_AUTO_UPDATE_SENSORS, "1"); // Enable HIDAPI rumble. This prevents SDL from disabling motion on PS4 and PS5 controllers - SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1"); - SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1"); + SDL_SetHint(SDL_HINT_JOYSTICK_ENHANCED_REPORTS, "1"); SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); // Disable hidapi drivers for joycon controllers when the custom joycon driver is enabled @@ -523,16 +674,13 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en } SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_PLAYER_LED, "1"); - // Share the same button mapping with non-Nintendo controllers - SDL_SetHint(SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS, "0"); - // Disable hidapi driver for xbox. Already default on Windows, this causes conflict with native // driver on Linux. SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_XBOX, "0"); // If the frontend is going to manage the event loop, then we don't start one here - start_thread = SDL_WasInit(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) == 0; - if (start_thread && SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) < 0) { + start_thread = SDL_WasInit(SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD) == 0; + if (start_thread && !SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD)) { LOG_CRITICAL(Input, "SDL_Init failed with: {}", SDL_GetError()); return; } @@ -552,19 +700,24 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en } // Because the events for joystick connection happens before we have our event watcher added, we // can just open all the joysticks right here - for (int i = 0; i < SDL_NumJoysticks(); ++i) { - InitJoystick(i); + int joystick_count = 0; + SDL_JoystickID* joysticks = SDL_GetJoysticks(&joystick_count); + if (joysticks != nullptr) { + for (int i = 0; i < joystick_count; ++i) { + InitJoystick(joysticks[i]); + } + SDL_free(joysticks); } } SDLDriver::~SDLDriver() { CloseJoysticks(); - SDL_DelEventWatch(&SDLEventWatcher, this); + SDL_RemoveEventWatch(&SDLEventWatcher, this); initialized = false; if (start_thread) { vibration_thread.join(); - SDL_QuitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER); + SDL_QuitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD); } } @@ -758,17 +911,17 @@ Common::ParamPackage SDLDriver::BuildMotionParam(int port, const Common::UUID& g } Common::ParamPackage SDLDriver::BuildParamPackageForBinding( - int port, const Common::UUID& guid, const SDL_GameControllerButtonBind& binding) const { - switch (binding.bindType) { - case SDL_CONTROLLER_BINDTYPE_NONE: + int port, const Common::UUID& guid, const SDL_GamepadBinding& binding) const { + switch (binding.input_type) { + case SDL_GAMEPAD_BINDTYPE_NONE: break; - case SDL_CONTROLLER_BINDTYPE_AXIS: - return BuildAnalogParamPackageForButton(port, guid, binding.value.axis); - case SDL_CONTROLLER_BINDTYPE_BUTTON: - return BuildButtonParamPackageForButton(port, guid, binding.value.button); - case SDL_CONTROLLER_BINDTYPE_HAT: - return BuildHatParamPackageForButton(port, guid, binding.value.hat.hat, - static_cast(binding.value.hat.hat_mask)); + case SDL_GAMEPAD_BINDTYPE_AXIS: + return BuildAnalogParamPackageForButton(port, guid, binding.input.axis.axis); + case SDL_GAMEPAD_BINDTYPE_BUTTON: + return BuildButtonParamPackageForButton(port, guid, binding.input.button); + case SDL_GAMEPAD_BINDTYPE_HAT: + return BuildHatParamPackageForButton(port, guid, binding.input.hat.hat, + static_cast(binding.input.hat.hat_mask)); } return {}; } @@ -808,8 +961,8 @@ ButtonMapping SDLDriver::GetButtonMappingForDevice(const Common::ParamPackage& p // Add the missing bindings for ZL/ZR static constexpr ZButtonBindings switch_to_sdl_axis{{ - {Settings::NativeButton::ZL, SDL_CONTROLLER_AXIS_TRIGGERLEFT}, - {Settings::NativeButton::ZR, SDL_CONTROLLER_AXIS_TRIGGERRIGHT}, + {Settings::NativeButton::ZL, SDL_GAMEPAD_AXIS_LEFT_TRIGGER}, + {Settings::NativeButton::ZR, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER}, }}; // Parameters contain two joysticks return dual @@ -828,41 +981,41 @@ ButtonMapping SDLDriver::GetButtonMappingForDevice(const Common::ParamPackage& p ButtonBindings SDLDriver::GetDefaultButtonBinding( const std::shared_ptr& joystick) const { // Default SL/SR mapping for other controllers - auto sll_button = SDL_CONTROLLER_BUTTON_LEFTSHOULDER; - auto srl_button = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER; - auto slr_button = SDL_CONTROLLER_BUTTON_LEFTSHOULDER; - auto srr_button = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER; + auto sll_button = SDL_GAMEPAD_BUTTON_LEFT_SHOULDER; + auto srl_button = SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER; + auto slr_button = SDL_GAMEPAD_BUTTON_LEFT_SHOULDER; + auto srr_button = SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER; if (joystick->IsJoyconLeft()) { - sll_button = SDL_CONTROLLER_BUTTON_PADDLE2; - srl_button = SDL_CONTROLLER_BUTTON_PADDLE4; + sll_button = SDL_GAMEPAD_BUTTON_LEFT_PADDLE1; + srl_button = SDL_GAMEPAD_BUTTON_LEFT_PADDLE2; } if (joystick->IsJoyconRight()) { - slr_button = SDL_CONTROLLER_BUTTON_PADDLE3; - srr_button = SDL_CONTROLLER_BUTTON_PADDLE1; + slr_button = SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2; + srr_button = SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1; } return { - std::pair{Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_B}, - {Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_A}, - {Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_Y}, - {Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_X}, - {Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK}, - {Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK}, - {Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER}, - {Settings::NativeButton::R, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER}, - {Settings::NativeButton::Plus, SDL_CONTROLLER_BUTTON_START}, - {Settings::NativeButton::Minus, SDL_CONTROLLER_BUTTON_BACK}, - {Settings::NativeButton::DLeft, SDL_CONTROLLER_BUTTON_DPAD_LEFT}, - {Settings::NativeButton::DUp, SDL_CONTROLLER_BUTTON_DPAD_UP}, - {Settings::NativeButton::DRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT}, - {Settings::NativeButton::DDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN}, + std::pair{Settings::NativeButton::A, SDL_GAMEPAD_BUTTON_EAST}, + {Settings::NativeButton::B, SDL_GAMEPAD_BUTTON_SOUTH}, + {Settings::NativeButton::X, SDL_GAMEPAD_BUTTON_NORTH}, + {Settings::NativeButton::Y, SDL_GAMEPAD_BUTTON_WEST}, + {Settings::NativeButton::LStick, SDL_GAMEPAD_BUTTON_LEFT_STICK}, + {Settings::NativeButton::RStick, SDL_GAMEPAD_BUTTON_RIGHT_STICK}, + {Settings::NativeButton::L, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER}, + {Settings::NativeButton::R, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER}, + {Settings::NativeButton::Plus, SDL_GAMEPAD_BUTTON_START}, + {Settings::NativeButton::Minus, SDL_GAMEPAD_BUTTON_BACK}, + {Settings::NativeButton::DLeft, SDL_GAMEPAD_BUTTON_DPAD_LEFT}, + {Settings::NativeButton::DUp, SDL_GAMEPAD_BUTTON_DPAD_UP}, + {Settings::NativeButton::DRight, SDL_GAMEPAD_BUTTON_DPAD_RIGHT}, + {Settings::NativeButton::DDown, SDL_GAMEPAD_BUTTON_DPAD_DOWN}, {Settings::NativeButton::SLLeft, sll_button}, {Settings::NativeButton::SRLeft, srl_button}, {Settings::NativeButton::SLRight, slr_button}, {Settings::NativeButton::SRRight, srr_button}, - {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE}, - {Settings::NativeButton::Screenshot, SDL_CONTROLLER_BUTTON_MISC1}, + {Settings::NativeButton::Home, SDL_GAMEPAD_BUTTON_GUIDE}, + {Settings::NativeButton::Screenshot, SDL_GAMEPAD_BUTTON_MISC1}, }; } @@ -872,15 +1025,16 @@ ButtonMapping SDLDriver::GetSingleControllerMapping( ButtonMapping mapping; mapping.reserve(switch_to_sdl_button.size() + switch_to_sdl_axis.size()); auto* controller = joystick->GetSDLGameController(); + const auto bindings = GetBindings(controller); for (const auto& [switch_button, sdl_button] : switch_to_sdl_button) { - const auto& binding = SDL_GameControllerGetBindForButton(controller, sdl_button); + const auto binding = GetBindingForButton(bindings, sdl_button); mapping.insert_or_assign( switch_button, BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding)); } for (const auto& [switch_button, sdl_axis] : switch_to_sdl_axis) { - const auto& binding = SDL_GameControllerGetBindForAxis(controller, sdl_axis); + const auto binding = GetBindingForAxis(bindings, sdl_axis); mapping.insert_or_assign( switch_button, BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding)); @@ -897,29 +1051,31 @@ ButtonMapping SDLDriver::GetDualControllerMapping(const std::shared_ptrGetSDLGameController(); auto* controller2 = joystick2->GetSDLGameController(); + const auto bindings = GetBindings(controller); + const auto bindings2 = GetBindings(controller2); for (const auto& [switch_button, sdl_button] : switch_to_sdl_button) { if (IsButtonOnLeftSide(switch_button)) { - const auto& binding = SDL_GameControllerGetBindForButton(controller2, sdl_button); + const auto binding = GetBindingForButton(bindings2, sdl_button); mapping.insert_or_assign( switch_button, BuildParamPackageForBinding(joystick2->GetPort(), joystick2->GetGUID(), binding)); continue; } - const auto& binding = SDL_GameControllerGetBindForButton(controller, sdl_button); + const auto binding = GetBindingForButton(bindings, sdl_button); mapping.insert_or_assign( switch_button, BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding)); } for (const auto& [switch_button, sdl_axis] : switch_to_sdl_axis) { if (IsButtonOnLeftSide(switch_button)) { - const auto& binding = SDL_GameControllerGetBindForAxis(controller2, sdl_axis); + const auto binding = GetBindingForAxis(bindings2, sdl_axis); mapping.insert_or_assign( switch_button, BuildParamPackageForBinding(joystick2->GetPort(), joystick2->GetGUID(), binding)); continue; } - const auto& binding = SDL_GameControllerGetBindForAxis(controller, sdl_axis); + const auto binding = GetBindingForAxis(bindings, sdl_axis); mapping.insert_or_assign( switch_button, BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding)); @@ -957,46 +1113,43 @@ AnalogMapping SDLDriver::GetAnalogMappingForDevice(const Common::ParamPackage& p } AnalogMapping mapping = {}; - const auto& binding_left_x = - SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX); - const auto& binding_left_y = - SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY); + const auto bindings = GetBindings(controller); + const auto binding_left_x = GetBindingForAxis(bindings, SDL_GAMEPAD_AXIS_LEFTX); + const auto binding_left_y = GetBindingForAxis(bindings, SDL_GAMEPAD_AXIS_LEFTY); if (params.Has("guid2")) { const auto identifier = joystick2->GetPadIdentifier(); PreSetController(identifier); - PreSetAxis(identifier, binding_left_x.value.axis); - PreSetAxis(identifier, binding_left_y.value.axis); - const auto left_offset_x = -GetAxis(identifier, binding_left_x.value.axis); - const auto left_offset_y = GetAxis(identifier, binding_left_y.value.axis); + PreSetAxis(identifier, binding_left_x.input.axis.axis); + PreSetAxis(identifier, binding_left_y.input.axis.axis); + const auto left_offset_x = -GetAxis(identifier, binding_left_x.input.axis.axis); + const auto left_offset_y = GetAxis(identifier, binding_left_y.input.axis.axis); mapping.insert_or_assign(Settings::NativeAnalog::LStick, - BuildParamPackageForAnalog(identifier, binding_left_x.value.axis, - binding_left_y.value.axis, + BuildParamPackageForAnalog(identifier, binding_left_x.input.axis.axis, + binding_left_y.input.axis.axis, left_offset_x, left_offset_y)); } else { const auto identifier = joystick->GetPadIdentifier(); PreSetController(identifier); - PreSetAxis(identifier, binding_left_x.value.axis); - PreSetAxis(identifier, binding_left_y.value.axis); - const auto left_offset_x = -GetAxis(identifier, binding_left_x.value.axis); - const auto left_offset_y = GetAxis(identifier, binding_left_y.value.axis); + PreSetAxis(identifier, binding_left_x.input.axis.axis); + PreSetAxis(identifier, binding_left_y.input.axis.axis); + const auto left_offset_x = -GetAxis(identifier, binding_left_x.input.axis.axis); + const auto left_offset_y = GetAxis(identifier, binding_left_y.input.axis.axis); mapping.insert_or_assign(Settings::NativeAnalog::LStick, - BuildParamPackageForAnalog(identifier, binding_left_x.value.axis, - binding_left_y.value.axis, + BuildParamPackageForAnalog(identifier, binding_left_x.input.axis.axis, + binding_left_y.input.axis.axis, left_offset_x, left_offset_y)); } - const auto& binding_right_x = - SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX); - const auto& binding_right_y = - SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY); + const auto binding_right_x = GetBindingForAxis(bindings, SDL_GAMEPAD_AXIS_RIGHTX); + const auto binding_right_y = GetBindingForAxis(bindings, SDL_GAMEPAD_AXIS_RIGHTY); const auto identifier = joystick->GetPadIdentifier(); PreSetController(identifier); - PreSetAxis(identifier, binding_right_x.value.axis); - PreSetAxis(identifier, binding_right_y.value.axis); - const auto right_offset_x = -GetAxis(identifier, binding_right_x.value.axis); - const auto right_offset_y = GetAxis(identifier, binding_right_y.value.axis); + PreSetAxis(identifier, binding_right_x.input.axis.axis); + PreSetAxis(identifier, binding_right_y.input.axis.axis); + const auto right_offset_x = -GetAxis(identifier, binding_right_x.input.axis.axis); + const auto right_offset_y = GetAxis(identifier, binding_right_y.input.axis.axis); mapping.insert_or_assign(Settings::NativeAnalog::RStick, - BuildParamPackageForAnalog(identifier, binding_right_x.value.axis, - binding_right_y.value.axis, right_offset_x, + BuildParamPackageForAnalog(identifier, binding_right_x.input.axis.axis, + binding_right_y.input.axis.axis, right_offset_x, right_offset_y)); return mapping; } @@ -1102,19 +1255,16 @@ bool SDLDriver::IsStickInverted(const Common::ParamPackage& params) { const auto& axis_x = params.Get("axis_x", 0); const auto& axis_y = params.Get("axis_y", 0); - const auto& binding_left_x = - SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX); - const auto& binding_right_x = - SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX); - const auto& binding_left_y = - SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY); - const auto& binding_right_y = - SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY); + const auto bindings = GetBindings(controller); + const auto binding_left_x = GetBindingForAxis(bindings, SDL_GAMEPAD_AXIS_LEFTX); + const auto binding_right_x = GetBindingForAxis(bindings, SDL_GAMEPAD_AXIS_RIGHTX); + const auto binding_left_y = GetBindingForAxis(bindings, SDL_GAMEPAD_AXIS_LEFTY); + const auto binding_right_y = GetBindingForAxis(bindings, SDL_GAMEPAD_AXIS_RIGHTY); - if (axis_x != binding_left_y.value.axis && axis_x != binding_right_y.value.axis) { + if (axis_x != binding_left_y.input.axis.axis && axis_x != binding_right_y.input.axis.axis) { return false; } - if (axis_y != binding_left_x.value.axis && axis_y != binding_right_x.value.axis) { + if (axis_y != binding_left_x.input.axis.axis && axis_y != binding_right_x.input.axis.axis) { return false; } return true; diff --git a/src/input_common/drivers/sdl_driver.h b/src/input_common/drivers/sdl_driver.h index 1677ab8289..bf4a45e83d 100644 --- a/src/input_common/drivers/sdl_driver.h +++ b/src/input_common/drivers/sdl_driver.h @@ -11,25 +11,22 @@ #include #include -#include +#include #include "common/common_types.h" #include "common/threadsafe_queue.h" #include "input_common/input_engine.h" union SDL_Event; -using SDL_GameController = struct _SDL_GameController; -using SDL_Joystick = struct _SDL_Joystick; -using SDL_JoystickID = s32; namespace InputCommon { class SDLJoystick; using ButtonBindings = - std::array, 20>; + std::array, 20>; using ZButtonBindings = - std::array, 2>; + std::array, 2>; class SDLDriver : public InputEngine { public: @@ -46,6 +43,7 @@ public: /// Get the nth joystick with the corresponding GUID std::shared_ptr GetSDLJoystickBySDLID(SDL_JoystickID sdl_id); + std::shared_ptr GetSDLJoystickByGamepadID(SDL_JoystickID sdl_id); /** * Check how many identical joysticks (by guid) were connected before the one with sdl_id and so @@ -72,7 +70,7 @@ public: bool IsVibrationEnabled(const PadIdentifier& identifier) override; private: - void InitJoystick(int joystick_index); + void InitJoystick(SDL_JoystickID joystick_id); void CloseJoystick(SDL_Joystick* sdl_joystick); /// Needs to be called before SDL_QuitSubSystem. @@ -92,7 +90,7 @@ private: Common::ParamPackage BuildMotionParam(int port, const Common::UUID& guid) const; Common::ParamPackage BuildParamPackageForBinding( - int port, const Common::UUID& guid, const SDL_GameControllerButtonBind& binding) const; + int port, const Common::UUID& guid, const SDL_GamepadBinding& binding) const; Common::ParamPackage BuildParamPackageForAnalog(PadIdentifier identifier, int axis_x, int axis_y, float offset_x, diff --git a/src/input_common/drivers/udp_client.cpp b/src/input_common/drivers/udp_client.cpp index fc216e3c9f..620e0cf630 100644 --- a/src/input_common/drivers/udp_client.cpp +++ b/src/input_common/drivers/udp_client.cpp @@ -13,6 +13,7 @@ #include "common/param_package.h" #include "common/random.h" #include "common/settings.h" +#include "common/thread.h" #include "input_common/drivers/udp_client.h" #include "input_common/helpers/udp_protocol.h" @@ -31,8 +32,10 @@ public: using clock = std::chrono::system_clock; explicit Socket(const std::string& host, u16 port, SocketCallback callback_) - : callback(std::move(callback_)), timer(io_context), - socket(io_context, udp::endpoint(udp::v4(), 0)), client_id(Common::Random::Random32(0)) { + : callback(std::move(callback_)), timer(io_context) + , socket(io_context, udp::endpoint(udp::v4(), 0)) + , client_id(Common::Random::Random32(0)) + { boost::system::error_code ec{}; auto ipv4 = boost::asio::ip::make_address_v4(host, ec); if (ec.value() != boost::system::errc::success) { @@ -131,6 +134,7 @@ private: }; static void SocketLoop(Socket* socket) { + Common::SetCurrentThreadName("cemuhookWorker"); socket->StartReceive(); socket->StartSend(Socket::clock::now()); socket->Loop(); @@ -326,9 +330,11 @@ void UDPClient::OnPadData(Response::PadData data, std::size_t client) { } void UDPClient::StartCommunication(std::size_t client, const std::string& host, u16 port) { - SocketCallback callback{[this](Response::Version version) { OnVersion(version); }, - [this](Response::PortInfo info) { OnPortInfo(info); }, - [this, client](Response::PadData data) { OnPadData(data, client); }}; + SocketCallback callback{ + [this](Response::Version version) { OnVersion(version); }, + [this](Response::PortInfo info) { OnPortInfo(info); }, + [this, client](Response::PadData data) { OnPadData(data, client); } + }; LOG_INFO(Input, "Starting communication with UDP input server on {}:{}", host, port); clients[client].uuid = GetHostUUID(host); clients[client].host = host; @@ -353,8 +359,13 @@ PadIdentifier UDPClient::GetPadIdentifier(std::size_t pad_index) const { } Common::UUID UDPClient::GetHostUUID(const std::string& host) const { - const auto ip = boost::asio::ip::make_address_v4(host); - const auto hex_host = fmt::format("00000000-0000-0000-0000-0000{:06x}", ip.to_uint()); + boost::system::error_code ec{}; + auto ip = boost::asio::ip::make_address_v4(host, ec); + if (ec.value() != boost::system::errc::success) { + LOG_ERROR(Input, "Invalid IPv4 address \"{}\" provided", host); + ip = boost::asio::ip::address_v4{}; + } + auto const hex_host = fmt::format("00000000-0000-0000-0000-0000{:06x}", ip.to_uint()); return Common::UUID{hex_host}; } @@ -566,9 +577,7 @@ bool UDPClient::IsStickInverted(const Common::ParamPackage& params) { return true; } -void TestCommunication(const std::string& host, u16 port, - const std::function& success_callback, - const std::function& failure_callback) { +void TestCommunication(const std::string& host, u16 port, const std::function& success_callback, const std::function& failure_callback) { std::thread([=] { Common::Event success_event; SocketCallback callback{ @@ -601,40 +610,38 @@ CalibrationConfigurationJob::CalibrationConfigurationJob( u16 max_y{}; Status current_status{Status::Initialized}; - SocketCallback callback{[](Response::Version) {}, [](Response::PortInfo) {}, - [&](Response::PadData data) { - constexpr u16 CALIBRATION_THRESHOLD = 100; + SocketCallback callback{[](Response::Version) {}, [](Response::PortInfo) {}, [&](Response::PadData data) { + constexpr u16 CALIBRATION_THRESHOLD = 100; - if (current_status == Status::Initialized) { - // Receiving data means the communication is ready now - current_status = Status::Ready; - status_callback(current_status); - } - if (data.touch[0].is_active == 0) { - return; - } - LOG_DEBUG(Input, "Current touch: {} {}", data.touch[0].x, - data.touch[0].y); - min_x = (std::min)(min_x, static_cast(data.touch[0].x)); - min_y = (std::min)(min_y, static_cast(data.touch[0].y)); - if (current_status == Status::Ready) { - // First touch - min data (min_x/min_y) - current_status = Status::Stage1Completed; - status_callback(current_status); - } - if (data.touch[0].x - min_x > CALIBRATION_THRESHOLD && - data.touch[0].y - min_y > CALIBRATION_THRESHOLD) { - // Set the current position as max value and finishes - // configuration - max_x = data.touch[0].x; - max_y = data.touch[0].y; - current_status = Status::Completed; - data_callback(min_x, min_y, max_x, max_y); - status_callback(current_status); + if (current_status == Status::Initialized) { + // Receiving data means the communication is ready now + current_status = Status::Ready; + status_callback(current_status); + } + if (data.touch[0].is_active == 0) { + return; + } + LOG_DEBUG(Input, "Current touch: {} {}", data.touch[0].x, data.touch[0].y); + min_x = (std::min)(min_x, u16(data.touch[0].x)); + min_y = (std::min)(min_y, u16(data.touch[0].y)); + if (current_status == Status::Ready) { + // First touch - min data (min_x/min_y) + current_status = Status::Stage1Completed; + status_callback(current_status); + } + if (data.touch[0].x - min_x > CALIBRATION_THRESHOLD && + data.touch[0].y - min_y > CALIBRATION_THRESHOLD) { + // Set the current position as max value and finishes + // configuration + max_x = data.touch[0].x; + max_y = data.touch[0].y; + current_status = Status::Completed; + data_callback(min_x, min_y, max_x, max_y); + status_callback(current_status); - complete_event.Set(); - } - }}; + complete_event.Set(); + } + }}; Socket socket{host, port, std::move(callback)}; std::thread worker_thread{SocketLoop, &socket}; complete_event.Wait(); diff --git a/src/input_common/helpers/joycon_protocol/joycon_types.h b/src/input_common/helpers/joycon_protocol/joycon_types.h index 792f124e14..dd3bd4398c 100644 --- a/src/input_common/helpers/joycon_protocol/joycon_types.h +++ b/src/input_common/helpers/joycon_protocol/joycon_types.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -10,7 +13,7 @@ #include #include -#include +#include #include "common/bit_field.h" #include "common/common_funcs.h" diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp index b04a70590a..6c8443ff62 100644 --- a/src/input_common/main.cpp +++ b/src/input_common/main.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: 2017 Citra Emulator Project @@ -25,12 +25,12 @@ #ifdef ENABLE_LIBUSB #include "input_common/drivers/gc_adapter.h" #endif -#ifdef HAVE_SDL2 +#ifdef HAVE_SDL3 #include "input_common/drivers/joycon.h" #include "input_common/drivers/sdl_driver.h" #endif -#ifdef ANDROID +#ifdef __ANDROID__ #include "input_common/drivers/android.h" #endif @@ -85,12 +85,12 @@ struct InputSubsystem::Impl { RegisterEngine("cemuhookudp", udp_client); RegisterEngine("tas", tas_input); RegisterEngine("camera", camera); -#ifdef ANDROID +#ifdef __ANDROID__ RegisterEngine("android", android); #endif RegisterEngine("virtual_amiibo", virtual_amiibo); RegisterEngine("virtual_gamepad", virtual_gamepad); -#ifdef HAVE_SDL2 +#ifdef HAVE_SDL3 RegisterEngine("sdl", sdl); RegisterEngine("joycon", joycon); #endif @@ -119,12 +119,12 @@ struct InputSubsystem::Impl { UnregisterEngine(udp_client); UnregisterEngine(tas_input); UnregisterEngine(camera); -#ifdef ANDROID +#ifdef __ANDROID__ UnregisterEngine(android); #endif UnregisterEngine(virtual_amiibo); UnregisterEngine(virtual_gamepad); -#ifdef HAVE_SDL2 +#ifdef HAVE_SDL3 UnregisterEngine(sdl); UnregisterEngine(joycon); #endif @@ -138,13 +138,13 @@ struct InputSubsystem::Impl { Common::ParamPackage{{"display", "Any"}, {"engine", "any"}}, }; -#ifndef ANDROID +#ifndef __ANDROID__ auto keyboard_devices = keyboard->GetInputDevices(); devices.insert(devices.end(), keyboard_devices.begin(), keyboard_devices.end()); auto mouse_devices = mouse->GetInputDevices(); devices.insert(devices.end(), mouse_devices.begin(), mouse_devices.end()); #endif -#ifdef ANDROID +#ifdef __ANDROID__ auto android_devices = android->GetInputDevices(); devices.insert(devices.end(), android_devices.begin(), android_devices.end()); #endif @@ -154,7 +154,7 @@ struct InputSubsystem::Impl { #endif auto udp_devices = udp_client->GetInputDevices(); devices.insert(devices.end(), udp_devices.begin(), udp_devices.end()); -#ifdef HAVE_SDL2 +#ifdef HAVE_SDL3 auto joycon_devices = joycon->GetInputDevices(); devices.insert(devices.end(), joycon_devices.begin(), joycon_devices.end()); auto sdl_devices = sdl->GetInputDevices(); @@ -176,7 +176,7 @@ struct InputSubsystem::Impl { if (engine == mouse->GetEngineName()) { return mouse; } -#ifdef ANDROID +#ifdef __ANDROID__ if (engine == android->GetEngineName()) { return android; } @@ -189,7 +189,7 @@ struct InputSubsystem::Impl { if (engine == udp_client->GetEngineName()) { return udp_client; } -#ifdef HAVE_SDL2 +#ifdef HAVE_SDL3 if (engine == sdl->GetEngineName()) { return sdl; } @@ -261,7 +261,7 @@ struct InputSubsystem::Impl { if (engine == mouse->GetEngineName()) { return true; } -#ifdef ANDROID +#ifdef __ANDROID__ if (engine == android->GetEngineName()) { return true; } @@ -280,7 +280,7 @@ struct InputSubsystem::Impl { if (engine == virtual_gamepad->GetEngineName()) { return true; } -#ifdef HAVE_SDL2 +#ifdef HAVE_SDL3 if (engine == sdl->GetEngineName()) { return true; } @@ -294,14 +294,14 @@ struct InputSubsystem::Impl { void BeginConfiguration() { keyboard->BeginConfiguration(); mouse->BeginConfiguration(); -#ifdef ANDROID +#ifdef __ANDROID__ android->BeginConfiguration(); #endif #ifdef ENABLE_LIBUSB gcadapter->BeginConfiguration(); #endif udp_client->BeginConfiguration(); -#ifdef HAVE_SDL2 +#ifdef HAVE_SDL3 sdl->BeginConfiguration(); joycon->BeginConfiguration(); #endif @@ -310,14 +310,14 @@ struct InputSubsystem::Impl { void EndConfiguration() { keyboard->EndConfiguration(); mouse->EndConfiguration(); -#ifdef ANDROID +#ifdef __ANDROID__ android->EndConfiguration(); #endif #ifdef ENABLE_LIBUSB gcadapter->EndConfiguration(); #endif udp_client->EndConfiguration(); -#ifdef HAVE_SDL2 +#ifdef HAVE_SDL3 sdl->EndConfiguration(); joycon->EndConfiguration(); #endif @@ -325,7 +325,7 @@ struct InputSubsystem::Impl { void PumpEvents() const { update_engine->PumpEvents(); -#ifdef HAVE_SDL2 +#ifdef HAVE_SDL3 sdl->PumpEvents(); #endif } @@ -350,12 +350,12 @@ struct InputSubsystem::Impl { std::shared_ptr gcadapter; #endif -#ifdef HAVE_SDL2 +#ifdef HAVE_SDL3 std::shared_ptr sdl; std::shared_ptr joycon; #endif -#ifdef ANDROID +#ifdef __ANDROID__ std::shared_ptr android; #endif }; @@ -412,7 +412,7 @@ const Camera* InputSubsystem::GetCamera() const { return impl->camera.get(); } -#ifdef ANDROID +#ifdef __ANDROID__ Android* InputSubsystem::GetAndroid() { return impl->android.get(); } diff --git a/src/qt_common/CMakeLists.txt b/src/qt_common/CMakeLists.txt index 399fbe67a0..8858b819d6 100644 --- a/src/qt_common/CMakeLists.txt +++ b/src/qt_common/CMakeLists.txt @@ -26,14 +26,21 @@ add_library(qt_common STATIC util/compress.h util/compress.cpp util/fs.h util/fs.cpp util/mod.h util/mod.cpp + util/vk.h util/vk.cpp abstract/frontend.h abstract/frontend.cpp abstract/progress.h abstract/progress.cpp + game_list/model.h game_list/model.cpp + game_list/worker.h game_list/worker.cpp + game_list/game_list_p.h + qt_string_lookup.h qt_compat.h - discord/discord.h) + discord/discord.h + render/context.h + render/emu_thread.h render/emu_thread.cpp) if (UNIX) target_sources(qt_common PRIVATE gui_settings.cpp gui_settings.h) @@ -56,9 +63,7 @@ if (USE_DISCORD_PRESENCE) endif() # TODO(crueter) -if (ENABLE_QT) - target_link_libraries(qt_common PRIVATE Qt6::Widgets) -endif() +target_link_libraries(qt_common PRIVATE Qt6::Widgets) target_compile_definitions(qt_common PUBLIC # Use QStringBuilder for string concatenation to reduce @@ -80,7 +85,7 @@ target_compile_definitions(qt_common PUBLIC ) target_link_libraries(qt_common PRIVATE core Qt6::Core Qt6::Concurrent SimpleIni::SimpleIni QuaZip::QuaZip) -target_link_libraries(qt_common PUBLIC frozen::frozen-headers) +target_link_libraries(qt_common PUBLIC frozen::frozen-headers Vulkan::Headers) target_link_libraries(qt_common PRIVATE gamemode::headers frontend_common) if (ENABLE_OPENGL) diff --git a/src/qt_common/abstract/frontend.h b/src/qt_common/abstract/frontend.h index 9b70f6849e..48c7c6edd0 100644 --- a/src/qt_common/abstract/frontend.h +++ b/src/qt_common/abstract/frontend.h @@ -1,8 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later -#ifndef FRONTEND_H -#define FRONTEND_H +#pragma once #include #include "qt_common/qt_common.h" @@ -114,4 +113,3 @@ const QString GetTextInput(const QString& title = QString(), const QString& capt const QString& defaultText = QString()); } // namespace QtCommon::Frontend -#endif // FRONTEND_H diff --git a/src/qt_common/config/shared_translation.cpp b/src/qt_common/config/shared_translation.cpp index 506d74084f..72241bab51 100644 --- a/src/qt_common/config/shared_translation.cpp +++ b/src/qt_common/config/shared_translation.cpp @@ -149,7 +149,7 @@ std::unique_ptr InitializeTranslations(QObject* parent) { "Options lower than 1X can cause artifacts.")); INSERT(Settings, scaling_filter, tr("Window Adapting Filter:"), QString()); INSERT(Settings, fsr_sharpening_slider, tr("FSR Sharpness:"), - tr("Determines how sharpened the image will look using FSR's dynamic contrast.")); + tr("Determines how sharpened the image will look using FSR's or SGSR's dynamic contrast.")); INSERT(Settings, anti_aliasing, tr("Anti-Aliasing Method:"), tr("The anti-aliasing method to use.\nSMAA offers the best quality.\nFXAA " "can produce a more stable picture in lower resolutions.")); @@ -165,8 +165,9 @@ std::unique_ptr InitializeTranslations(QObject* parent) { INSERT(Settings, use_disk_shader_cache, tr("Use persistent pipeline cache"), tr("Allows saving shaders to storage for faster loading on following game " "boots.\nDisabling it is only intended for debugging.")); - INSERT(Settings, use_asynchronous_gpu_emulation, tr("Use asynchronous GPU emulation"), - tr("Uses an extra CPU thread for rendering.\nThis option should always remain enabled.")); + INSERT( + Settings, use_asynchronous_gpu_emulation, tr("Use asynchronous GPU emulation"), + tr("Uses an extra CPU thread for rendering.\nThis option should always remain enabled.")); INSERT(Settings, nvdec_emulation, tr("NVDEC emulation:"), tr("Specifies how videos should be decoded.\nIt can either use the CPU or the GPU for " "decoding, or perform no decoding at all (black screen on videos).\n" @@ -192,6 +193,9 @@ std::unique_ptr InitializeTranslations(QObject* parent) { INSERT(Settings, skip_cpu_inner_invalidation, tr("Skip CPU Inner Invalidation"), tr("Skips certain cache invalidations during memory updates, reducing CPU usage and " "improving latency. This may cause soft-crashes.")); + INSERT(Settings, antiflicker, tr("Anti-Flicker"), + tr("Forces GPU fence callbacks to wait for submitted GPU work.\n" + "Use with Fast GPU mode, to avoid flicker with lower performance impact.")); INSERT(Settings, vsync_mode, tr("VSync Mode:"), tr("FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen " "refresh rate.\nFIFO Relaxed allows tearing as it recovers from a slow down.\n" @@ -225,6 +229,8 @@ std::unique_ptr InitializeTranslations(QObject* parent) { INSERT(Settings, dma_accuracy, tr("DMA Accuracy:"), tr("Controls the DMA precision accuracy. Safe precision fixes issues in some games but " "may degrade performance.")); + INSERT(Settings, enable_gpu_buffer_readback, tr("Enable GPU buffer readback"), + tr("Preserves GPU-modified buffer data by reading it back before uploads.\nSome games require this to render certain effects properly.\nMay cause issues if the hardware cannot handle the additional workload.")); INSERT(Settings, use_asynchronous_shaders, tr("Enable asynchronous shader compilation"), tr("May reduce shader stutter.")); INSERT(Settings, fast_gpu_time, tr("Fast GPU Time"), @@ -297,6 +303,8 @@ std::unique_ptr InitializeTranslations(QObject* parent) { tr("Controls the seed of the random number generator.\nMainly used for speedrunning.")); INSERT(Settings, rng_seed_enabled, QString(), QString()); INSERT(Settings, device_name, tr("Device Name"), tr("The name of the console.")); + INSERT(Settings, program_args, tr("Homebrew Args"), + tr("Command-line arguments passed to homebrew at launch (e.g. -noglsl).")); INSERT(Settings, custom_rtc, tr("Custom RTC Date:"), tr("This option allows to change the clock of the console.\n" "Can be used to manipulate time in games.")); @@ -488,6 +496,8 @@ std::unique_ptr ComboboxEnumeration(QObject* parent) { PAIR(ScalingFilter, BSpline, tr("B-Spline")), PAIR(ScalingFilter, Mitchell, tr("Mitchell")), PAIR(ScalingFilter, Spline1, tr("Spline-1")), + PAIR(ScalingFilter, Sgsr, tr("Snapdragon Game Super Resolution")), + PAIR(ScalingFilter, SgsrEdge, tr("Snapdragon Game Super Resolution EdgeDir")), }}); translations->insert({Settings::EnumMetadata::Index(), { diff --git a/src/qt_common/config/shared_translation.h b/src/qt_common/config/shared_translation.h index 6529c7bf40..c34b5162c4 100644 --- a/src/qt_common/config/shared_translation.h +++ b/src/qt_common/config/shared_translation.h @@ -53,6 +53,8 @@ static const std::map scaling_filter_texts_map {Settings::ScalingFilter::Fsr, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "FSR"))}, {Settings::ScalingFilter::Area, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Area"))}, {Settings::ScalingFilter::Mmpx, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "MMPX"))}, + {Settings::ScalingFilter::Sgsr, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "SGSR"))}, + {Settings::ScalingFilter::SgsrEdge, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "SGSR EdgeDir"))}, }; static const std::map use_docked_mode_texts_map = { diff --git a/src/qt_common/config/uisettings.h b/src/qt_common/config/uisettings.h index 4549a36345..fd79d2299f 100644 --- a/src/qt_common/config/uisettings.h +++ b/src/qt_common/config/uisettings.h @@ -207,12 +207,11 @@ struct Values { // Game List Setting show_add_ons{linkage, true, "show_add_ons", Category::UiGameList}; - Setting game_icon_size{linkage, 64, "game_icon_size", Category::UiGameList}; - Setting folder_icon_size{linkage, 48, "folder_icon_size", Category::UiGameList}; + Setting game_icon_size{linkage, 64, 0, 512, "game_icon_size", Category::UiGameList}; + Setting folder_icon_size{linkage, 48, 8, 512, "folder_icon_size", Category::UiGameList}; Setting row_1_text_id{linkage, 3, "row_1_text_id", Category::UiGameList}; Setting row_2_text_id{linkage, 2, "row_2_text_id", Category::UiGameList}; - Setting game_list_mode{linkage, Settings::GameListMode::TreeView, - "game_list_mode", Category::UiGameList}; + Setting game_list_mode{linkage, Settings::GameListMode::TreeView, "game_list_mode", Category::UiGameList}; Setting show_game_name{linkage, true, "show_game_name", Category::UiGameList}; std::atomic_bool is_game_list_reload_pending{false}; diff --git a/src/yuzu/game/game_list_p.h b/src/qt_common/game_list/game_list_p.h similarity index 80% rename from src/yuzu/game/game_list_p.h rename to src/qt_common/game_list/game_list_p.h index f9d714fb8b..d3d46771aa 100644 --- a/src/yuzu/game/game_list_p.h +++ b/src/qt_common/game_list/game_list_p.h @@ -17,14 +17,13 @@ #include #include #include -#include #include "common/common_types.h" #include "common/logging.h" #include "common/string_util.h" #include "frontend_common/play_time_manager.h" #include "qt_common/config/uisettings.h" -#include "yuzu/util/util.h" +#include "qt_common/qt_common.h" enum class GameListItemType { Game = QStandardItem::UserType + 1, @@ -49,6 +48,14 @@ static QPixmap GetDefaultIcon(u32 size) { return icon; } +static QPixmap ThemeIcon(const char* name) { + const int size = UISettings::values.folder_icon_size.GetValue(); + + return QIcon::fromTheme(QLatin1String(name)) + .pixmap(size, size) + .scaled(size, size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); +} + class GameListItem : public QStandardItem { public: @@ -158,6 +165,7 @@ public: return QStringLiteral("%1\n %2").arg(row1, row2); } case Settings::GameListMode::GridView: + case Settings::GameListMode::CarouselView: return row1; default: break; @@ -204,7 +212,7 @@ public: setData(compatibility, CompatNumberRole); setText(tr(status.text)); setToolTip(tr(status.tooltip)); - setData(CreateCirclePixmapFromColor(status.color), Qt::DecorationRole); + setData(QtCommon::CreateCirclePixmapFromColor(status.color), Qt::DecorationRole); } int type() const override { @@ -237,7 +245,7 @@ public: // representations of the data are always accurate and in the correct format. if (role == SizeRole) { qulonglong size_bytes = value.toULongLong(); - GameListItem::setData(ReadableByteSize(size_bytes), Qt::DisplayRole); + GameListItem::setData(QtCommon::ReadableByteSize(size_bytes), Qt::DisplayRole); GameListItem::setData(value, SizeRole); } else { GameListItem::setData(value, role); @@ -297,45 +305,33 @@ public: UISettings::GameDir* game_dir = &directory; setData(QVariant(UISettings::values.game_dirs.indexOf(directory)), GameDirRole); - const int icon_size = UISettings::values.folder_icon_size.GetValue(); + const char* icon_name = nullptr; + switch (dir_type) { case GameListItemType::SdmcDir: - setData( - QIcon::fromTheme(QStringLiteral("sd_card")) - .pixmap(icon_size) - .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), - Qt::DecorationRole); + icon_name = "sd_card"; setData(QObject::tr("Installed SD Titles"), Qt::DisplayRole); break; case GameListItemType::UserNandDir: - setData( - QIcon::fromTheme(QStringLiteral("chip")) - .pixmap(icon_size) - .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), - Qt::DecorationRole); + icon_name = "chip"; setData(QObject::tr("Installed NAND Titles"), Qt::DisplayRole); break; case GameListItemType::SysNandDir: - setData( - QIcon::fromTheme(QStringLiteral("chip")) - .pixmap(icon_size) - .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), - Qt::DecorationRole); + icon_name = "chip"; setData(QObject::tr("System Titles"), Qt::DisplayRole); break; case GameListItemType::CustomDir: { const QString path = QString::fromStdString(game_dir->path); - const QString icon_name = - QFileInfo::exists(path) ? QStringLiteral("folder") : QStringLiteral("bad_folder"); - setData(QIcon::fromTheme(icon_name).pixmap(icon_size).scaled( - icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), - Qt::DecorationRole); + icon_name = QFileInfo::exists(path) ? "folder" : "bad_folder"; setData(path, Qt::DisplayRole); break; } default: break; } + + if (icon_name != nullptr) + setData(ThemeIcon(icon_name), Qt::DecorationRole); } int type() const override { @@ -358,12 +354,7 @@ public: explicit GameListAddDir() { setData(type(), TypeRole); - const int icon_size = UISettings::values.folder_icon_size.GetValue(); - - setData(QIcon::fromTheme(QStringLiteral("list-add")) - .pixmap(icon_size) - .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), - Qt::DecorationRole); + setData(ThemeIcon("list-add"), Qt::DecorationRole); setData(QObject::tr("Add New Game Directory"), Qt::DisplayRole); } @@ -381,12 +372,7 @@ public: explicit GameListFavorites() { setData(type(), TypeRole); - const int icon_size = UISettings::values.folder_icon_size.GetValue(); - - setData(QIcon::fromTheme(QStringLiteral("star")) - .pixmap(icon_size) - .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), - Qt::DecorationRole); + setData(ThemeIcon("star"), Qt::DecorationRole); setData(QObject::tr("Favorites"), Qt::DisplayRole); } @@ -398,49 +384,3 @@ public: return false; } }; - -class GameList; -class QHBoxLayout; -class QTreeView; -class QLabel; -class QLineEdit; -class QToolButton; - -class GameListSearchField : public QWidget { - Q_OBJECT - -public: - explicit GameListSearchField(GameList* parent = nullptr); - - QString filterText() const; - void setFilterResult(int visible_, int total_); - - void clear(); - void setFocus(); - -private: - void changeEvent(QEvent*) override; - void RetranslateUI(); - - class KeyReleaseEater : public QObject { - public: - explicit KeyReleaseEater(GameList* gamelist_, QObject* parent = nullptr); - - private: - GameList* gamelist = nullptr; - QString edit_filter_text_old; - - protected: - // EventFilter in order to process systemkeys while editing the searchfield - bool eventFilter(QObject* obj, QEvent* event) override; - }; - int visible; - int total; - - QHBoxLayout* layout_filter = nullptr; - QTreeView* tree_view = nullptr; - QLabel* label_filter = nullptr; - QLineEdit* edit_filter = nullptr; - QLabel* label_filter_result = nullptr; - QToolButton* button_filter_close = nullptr; -}; diff --git a/src/qt_common/game_list/model.cpp b/src/qt_common/game_list/model.cpp new file mode 100644 index 0000000000..5c1042b002 --- /dev/null +++ b/src/qt_common/game_list/model.cpp @@ -0,0 +1,256 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include +#include +#include +#include +#include +#include + +#include "common/logging.h" +#include "common/settings.h" +#include "core/core.h" +#include "core/file_sys/patch_manager.h" +#include "core/file_sys/registered_cache.h" +#include "core/hle/service/filesystem/filesystem.h" +#include "qt_common/config/uisettings.h" +#include "qt_common/qt_common.h" +#include "qt_common/util/game.h" + +#include "qt_common/game_list/game_list_p.h" +#include "qt_common/game_list/model.h" +#include "qt_common/game_list/worker.h" + +GameListModel::GameListModel(std::shared_ptr vfs_, + FileSys::ManualContentProvider* provider_, + const PlayTime::PlayTimeManager& play_time_manager_, + Core::System& system_, QObject* parent) + : QStandardItemModel{parent}, vfs{std::move(vfs_)}, provider{provider_}, + play_time_manager{play_time_manager_}, system{system_} { + watcher = new QFileSystemWatcher(this); + external_watcher = new QFileSystemWatcher(this); + + connect(watcher, &QFileSystemWatcher::directoryChanged, this, + &GameListModel::RefreshGameDirectory); + connect(external_watcher, &QFileSystemWatcher::directoryChanged, this, + &GameListModel::RefreshExternalContent); + + ResetExternalWatcher(); + + insertColumns(0, COLUMN_COUNT); + RetranslateUI(); + + setSortRole(GameListItemPath::SortRole); +} + +GameListModel::~GameListModel() = default; + +void GameListModel::PopulateAsync(QVector& game_dirs) { + emit PopulatingStarted(); + + current_worker.reset(); + removeRows(0, rowCount()); + + current_worker = std::make_unique(vfs, provider, game_dirs, compatibility_list, + play_time_manager, system); + + connect(current_worker.get(), &GameListWorker::DataAvailable, this, &GameListModel::WorkerEvent, + Qt::QueuedConnection); + + QThreadPool::globalInstance()->start(current_worker.get()); +} + +void GameListModel::WorkerEvent() { + current_worker->ProcessEvents(this); +} + +void GameListModel::AddDirEntry(GameListDir* entry_items) { + if (m_flat) { + return; + } + invisibleRootItem()->appendRow(entry_items); +} + +void GameListModel::AddEntry(const QList& entry_items, GameListDir* parent) { + if (m_flat) { + invisibleRootItem()->appendRow(entry_items); + } else { + parent->appendRow(entry_items); + } +} + +void GameListModel::DonePopulating(const QStringList& watch_list) { + emit ShowList(!IsEmpty()); + + if (!m_flat) { + invisibleRootItem()->appendRow(new GameListAddDir()); + invisibleRootItem()->insertRow(0, new GameListFavorites()); + + for (const auto id : std::as_const(UISettings::values.favorited_ids)) { + AddFavorite(id); + } + } + + emit PopulatingCompleted(watch_list); +} + +bool GameListModel::IsEmpty() const { + for (int i = 0; i < rowCount(); i++) { + const QStandardItem* child = invisibleRootItem()->child(i); + const auto type = static_cast(child->type()); + + if (!child->hasChildren() && + (type == GameListItemType::SdmcDir || type == GameListItemType::UserNandDir || + type == GameListItemType::SysNandDir)) { + invisibleRootItem()->removeRow(child->row()); + i--; + } + } + + return !invisibleRootItem()->hasChildren(); +} + +void GameListModel::ToggleFavorite(u64 program_id) { + if (!UISettings::values.favorited_ids.contains(program_id)) { + UISettings::values.favorited_ids.append(program_id); + AddFavorite(program_id); + } else { + UISettings::values.favorited_ids.removeOne(program_id); + RemoveFavorite(program_id); + } + emit SaveConfig(); +} + +void GameListModel::AddFavorite(u64 program_id) { + auto* favorites_row = item(0); + + for (int i = 1; i < rowCount() - 1; i++) { + const auto* folder = item(i); + for (int j = 0; j < folder->rowCount(); j++) { + if (folder->child(j)->data(GameListItemPath::ProgramIdRole).toULongLong() == + program_id) { + QList list; + for (int k = 0; k < COLUMN_COUNT; k++) { + list.append(folder->child(j, k)->clone()); + } + list[0]->setData(folder->child(j)->data(GameListItem::SortRole), + GameListItem::SortRole); + list[0]->setText(folder->child(j)->data(Qt::DisplayRole).toString()); + + favorites_row->appendRow(list); + return; + } + } + } +} + +void GameListModel::RemoveFavorite(u64 program_id) { + auto* favorites_row = item(0); + + for (int i = 0; i < favorites_row->rowCount(); i++) { + const auto* game = favorites_row->child(i); + if (game->data(GameListItemPath::ProgramIdRole).toULongLong() == program_id) { + favorites_row->removeRow(i); + return; + } + } +} + +void GameListModel::LoadCompatibilityList() { + QFile compat_list{QStringLiteral(":compatibility_list/compatibility_list.json")}; + + if (!compat_list.open(QFile::ReadOnly | QFile::Text)) { + LOG_ERROR(Frontend, "Unable to open game compatibility list"); + return; + } + + if (compat_list.size() == 0) { + LOG_WARNING(Frontend, "Game compatibility list is empty"); + return; + } + + const QByteArray content = compat_list.readAll(); + if (content.isEmpty()) { + LOG_ERROR(Frontend, "Unable to completely read game compatibility list"); + return; + } + + const QJsonDocument json = QJsonDocument::fromJson(content); + const QJsonArray arr = json.array(); + + for (const QJsonValue& value : arr) { + const QJsonObject game = value.toObject(); + const QString compatibility_key = QStringLiteral("compatibility"); + + if (!game.contains(compatibility_key) || !game[compatibility_key].isDouble()) { + continue; + } + + const int compatibility = game[compatibility_key].toInt(); + const QString directory = game[QStringLiteral("directory")].toString(); + const QJsonArray ids = game[QStringLiteral("releases")].toArray(); + + for (const QJsonValue& id_ref : ids) { + const QJsonObject id_object = id_ref.toObject(); + const QString id = id_object[QStringLiteral("id")].toString(); + + compatibility_list.emplace(id.toUpper().toStdString(), + std::make_pair(QString::number(compatibility), directory)); + } + } +} + +void GameListModel::Repopulate() { + current_worker.reset(); + QtCommon::system->GetFileSystemController().CreateFactories(*QtCommon::vfs); + PopulateAsync(UISettings::values.game_dirs); +} + +void GameListModel::RefreshGameDirectory() { + ResetExternalWatcher(); + if (!UISettings::values.game_dirs.empty() && current_worker != nullptr) { + LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list."); + Repopulate(); + } +} + +void GameListModel::RefreshExternalContent() { + if (!UISettings::values.game_dirs.empty() && current_worker != nullptr) { + LOG_INFO(Frontend, "External content directory changed. Clearing metadata cache."); + QtCommon::Game::ResetMetadata(false); + Repopulate(); + } +} + +void GameListModel::ResetExternalWatcher() { + auto watch_dirs = external_watcher->directories(); + if (!watch_dirs.isEmpty()) { + external_watcher->removePaths(watch_dirs); + } + + for (const std::string& dir : Settings::values.external_content_dirs) { + external_watcher->addPath(QString::fromStdString(dir)); + } +} + +void GameListModel::RetranslateUI() { + setHeaderData(COLUMN_NAME, Qt::Horizontal, tr("Name")); + setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, tr("Compatibility")); + setHeaderData(COLUMN_ADD_ONS, Qt::Horizontal, tr("Add-ons")); + setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, tr("File type")); + setHeaderData(COLUMN_SIZE, Qt::Horizontal, tr("Size")); + setHeaderData(COLUMN_PLAY_TIME, Qt::Horizontal, tr("Play time")); +} + +QFileSystemWatcher* GameListModel::GetWatcher() const { + return watcher; +} + +const CompatibilityList& GameListModel::GetCompatibilityList() const { + return compatibility_list; +} + +void GameListModel::SetFlat(bool flat) { + m_flat = flat; +} diff --git a/src/qt_common/game_list/model.h b/src/qt_common/game_list/model.h new file mode 100644 index 0000000000..c8661e2fd1 --- /dev/null +++ b/src/qt_common/game_list/model.h @@ -0,0 +1,99 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include +#include +#include +#include + +#include "common/common_types.h" +#include "frontend_common/play_time_manager.h" +#include "qt_common/config/uisettings.h" +#include "yuzu/compatibility_list.h" + +namespace Core { +class System; +} + +class GameListDir; +class GameListWorker; +class QStandardItem; + +namespace FileSys { +class ManualContentProvider; +class VfsFilesystem; +} // namespace FileSys + +class GameListModel : public QStandardItemModel { + Q_OBJECT + +public: + enum Column { + COLUMN_NAME, + COLUMN_FILE_TYPE, + COLUMN_SIZE, + COLUMN_PLAY_TIME, + COLUMN_ADD_ONS, + COLUMN_COMPATIBILITY, + COLUMN_COUNT, + }; + + explicit GameListModel(std::shared_ptr vfs_, + FileSys::ManualContentProvider* provider_, + const PlayTime::PlayTimeManager& play_time_manager_, + Core::System& system_, QObject* parent = nullptr); + ~GameListModel() override; + + void AddDirEntry(GameListDir* entry_items); + void AddEntry(const QList& entry_items, GameListDir* parent); + void DonePopulating(const QStringList& watch_list); + + void PopulateAsync(QVector& game_dirs); + void WorkerEvent(); + + bool IsEmpty() const; + + void ToggleFavorite(u64 program_id); + + void RefreshGameDirectory(); + void RefreshExternalContent(); + void ResetExternalWatcher(); + + void LoadCompatibilityList(); + + void RetranslateUI(); + + QFileSystemWatcher* GetWatcher() const; + + const CompatibilityList& GetCompatibilityList() const; + + void SetFlat(bool flat); + +signals: + void ShowList(bool show); + void PopulatingCompleted(const QStringList& watch_list); + void PopulatingStarted(); + void SaveConfig(); + +private: + friend class GameListWorker; + + void AddFavorite(u64 program_id); + void RemoveFavorite(u64 program_id); + void Repopulate(); + + bool m_flat = false; + + std::shared_ptr vfs; + FileSys::ManualContentProvider* provider; + CompatibilityList compatibility_list; + const PlayTime::PlayTimeManager& play_time_manager; + Core::System& system; + + std::unique_ptr current_worker; + QFileSystemWatcher* watcher = nullptr; + QFileSystemWatcher* external_watcher = nullptr; +}; diff --git a/src/yuzu/game/game_list_worker.cpp b/src/qt_common/game_list/worker.cpp similarity index 86% rename from src/yuzu/game/game_list_worker.cpp rename to src/qt_common/game_list/worker.cpp index 777122e135..5cd68996c5 100644 --- a/src/yuzu/game/game_list_worker.cpp +++ b/src/qt_common/game_list/worker.cpp @@ -20,21 +20,23 @@ #include "common/settings.h" #include "core/core.h" #include "core/file_sys/card_image.h" -#include "core/file_sys/common_funcs.h" #include "core/file_sys/content_archive.h" #include "core/file_sys/control_metadata.h" #include "core/file_sys/fs_filesystem.h" #include "core/file_sys/nca_metadata.h" #include "core/file_sys/patch_manager.h" #include "core/file_sys/registered_cache.h" -#include "core/file_sys/romfs.h" #include "core/file_sys/submission_package.h" #include "core/loader/loader.h" + #include "qt_common/config/uisettings.h" +#include "qt_common/qt_common.h" + +#include "qt_common/game_list/game_list_p.h" #include "yuzu/compatibility_list.h" -#include "yuzu/game/game_list.h" -#include "yuzu/game/game_list_p.h" -#include "yuzu/game/game_list_worker.h" + +#include "qt_common/game_list/model.h" +#include "qt_common/game_list/worker.h" namespace { @@ -148,7 +150,7 @@ void GetMetadataFromControlNCA(const FileSys::PatchManager& patch_manager, const bool HasSupportedFileExtension(const std::string& file_name) { const QFileInfo file = QFileInfo(QString::fromStdString(file_name)); - return GameList::supported_file_extensions.contains(file.suffix(), Qt::CaseInsensitive); + return QtCommon::supported_file_extensions.contains(file.suffix(), Qt::CaseInsensitive); } bool IsExtractedNCAMain(const std::string& file_name) { @@ -249,9 +251,9 @@ GameListWorker::~GameListWorker() { processing_completed.Wait(); } -void GameListWorker::ProcessEvents(GameList* game_list) { +void GameListWorker::ProcessEvents(GameListModel* model) { while (true) { - std::function func; + std::function func; { // Lock queue to protect concurrent modification. std::scoped_lock lk(lock); @@ -267,7 +269,7 @@ void GameListWorker::ProcessEvents(GameList* game_list) { } // Run the function. - func(game_list); + func(model); } } @@ -334,7 +336,7 @@ void GameListWorker::AddTitlesToGameList(GameListDir* parent_dir) { auto entry = MakeGameListEntry(file->GetFullPath(), name, file->GetSize(), icon, *loader, program_id, compatibility_list, play_time_manager, patch); - RecordEvent([=](GameList* game_list) { game_list->AddEntry(entry, parent_dir); }); + RecordEvent([=](GameListModel* model) { model->AddEntry(entry, parent_dir); }); } } @@ -389,6 +391,25 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa std::vector program_ids; loader->ReadProgramIds(program_ids); + const auto addEntry = [this, physical_name, + parent_dir](std::unique_ptr& app_loader, + const u64 id) { + std::vector icon; + [[maybe_unused]] const auto res1 = app_loader->ReadIcon(icon); + + std::string name = " "; + [[maybe_unused]] const auto res3 = app_loader->ReadTitle(name); + + const FileSys::PatchManager patch{id, system.GetFileSystemController(), + system.GetContentProvider()}; + + auto entry = MakeGameListEntry( + physical_name, name, Common::FS::GetSize(physical_name), icon, *app_loader, + id, compatibility_list, play_time_manager, patch); + + RecordEvent([=](GameListModel* model) { model->AddEntry(entry, parent_dir); }); + }; + if (res2 == Loader::ResultStatus::Success && program_ids.size() > 1 && (file_type == Loader::FileType::XCI || file_type == Loader::FileType::NSP)) { for (const auto id : program_ids) { @@ -402,38 +423,10 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa continue; } - std::vector icon; - [[maybe_unused]] const auto res1 = loader->ReadIcon(icon); - - std::string name = " "; - [[maybe_unused]] const auto res3 = loader->ReadTitle(name); - - const FileSys::PatchManager patch{id, system.GetFileSystemController(), - system.GetContentProvider()}; - - auto entry = MakeGameListEntry( - physical_name, name, Common::FS::GetSize(physical_name), icon, *loader, - id, compatibility_list, play_time_manager, patch); - - RecordEvent( - [=](GameList* game_list) { game_list->AddEntry(entry, parent_dir); }); + addEntry(loader, id); } } else { - std::vector icon; - [[maybe_unused]] const auto res1 = loader->ReadIcon(icon); - - std::string name = " "; - [[maybe_unused]] const auto res3 = loader->ReadTitle(name); - - const FileSys::PatchManager patch{program_id, system.GetFileSystemController(), - system.GetContentProvider()}; - - auto entry = MakeGameListEntry( - physical_name, name, Common::FS::GetSize(physical_name), icon, *loader, - program_id, compatibility_list, play_time_manager, patch); - - RecordEvent( - [=](GameList* game_list) { game_list->AddEntry(entry, parent_dir); }); + addEntry(loader, program_id); } } } else if (is_dir) { @@ -456,7 +449,7 @@ void GameListWorker::run() { provider->ClearAllEntries(); const auto DirEntryReady = [&](GameListDir* game_list_dir) { - RecordEvent([=](GameList* game_list) { game_list->AddDirEntry(game_list_dir); }); + RecordEvent([=](GameListModel* model) { model->AddDirEntry(game_list_dir); }); }; for (UISettings::GameDir& game_dir : game_dirs) { @@ -464,32 +457,36 @@ void GameListWorker::run() { break; } + GameListDir* game_list_dir; + bool scan = false; + if (game_dir.path == std::string("SDMC")) { - auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SdmcDir); - DirEntryReady(game_list_dir); - AddTitlesToGameList(game_list_dir); + game_list_dir = new GameListDir(game_dir, GameListItemType::SdmcDir); } else if (game_dir.path == std::string("UserNAND")) { - auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::UserNandDir); - DirEntryReady(game_list_dir); - AddTitlesToGameList(game_list_dir); + game_list_dir = new GameListDir(game_dir, GameListItemType::UserNandDir); } else if (game_dir.path == std::string("SysNAND")) { - auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SysNandDir); - DirEntryReady(game_list_dir); - AddTitlesToGameList(game_list_dir); + game_list_dir = new GameListDir(game_dir, GameListItemType::SysNandDir); } else { const QString qpath = QString::fromStdString(game_dir.path); if (QDir(qpath).exists()) { watch_list.append(qpath); } - auto* const game_list_dir = new GameListDir(game_dir); - DirEntryReady(game_list_dir); + + game_list_dir = new GameListDir(game_dir); + scan = true; + } + + DirEntryReady(game_list_dir); + if (scan) { ScanFileSystem(ScanTarget::FillManualContentProvider, game_dir.path, game_dir.deep_scan, game_list_dir); ScanFileSystem(ScanTarget::PopulateGameList, game_dir.path, game_dir.deep_scan, game_list_dir); + } else { + AddTitlesToGameList(game_list_dir); } } - RecordEvent([this](GameList* game_list) { game_list->DonePopulating(watch_list); }); + RecordEvent([this](GameListModel* model) { model->DonePopulating(watch_list); }); processing_completed.Set(); } diff --git a/src/yuzu/game/game_list_worker.h b/src/qt_common/game_list/worker.h similarity index 87% rename from src/yuzu/game/game_list_worker.h rename to src/qt_common/game_list/worker.h index bf67585fe6..c11741c5f5 100644 --- a/src/yuzu/game/game_list_worker.h +++ b/src/qt_common/game_list/worker.h @@ -26,8 +26,8 @@ namespace Core { class System; } -class GameList; class GameListDir; +class GameListModel; class QStandardItem; namespace FileSys { @@ -58,11 +58,11 @@ public: /** * Synchronously processes any events queued by the worker. * - * AddDirEntry is called on the game list for every discovered directory. - * AddEntry is called on the game list for every discovered program. - * DonePopulating is called on the game list when processing completes. + * AddDirEntry is called on the model for every discovered directory. + * AddEntry is called on the model for every discovered program. + * DonePopulating is called on the model when processing completes. */ - void ProcessEvents(GameList* game_list); + void ProcessEvents(GameListModel* model); signals: void DataAvailable(); @@ -92,7 +92,7 @@ private: std::mutex lock; std::condition_variable cv; - std::deque> queued_events; + std::deque> queued_events; std::atomic_bool stop_requested = false; Common::Event processing_completed; diff --git a/src/qt_common/qt_common.cpp b/src/qt_common/qt_common.cpp index 3091171df8..b9b8caf6e8 100644 --- a/src/qt_common/qt_common.cpp +++ b/src/qt_common/qt_common.cpp @@ -1,25 +1,51 @@ // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later -#include "common/fs/fs.h" +#include "common/literals.h" + +#include "common/memory_detect.h" +#include "core/hle/service/filesystem/filesystem.h" +#include "frontend_common/data_manager.h" +#include "hid_core/hid_core.h" +#include "network/network.h" #include "qt_common.h" +#include "common/fs/fs.h" +#include "common/fs/path_util.h" +#include "common/logging.h" +#include "common/scm_rev.h" +#include "common/cpu_features.h" +#include "core/memory.h" + #include #include -#include "common/logging.h" #include "core/frontend/emu_window.h" +#include "qt_common/util/meta.h" #include +#include + +#include #include +#include #if !defined(WIN32) && !defined(__APPLE__) #include #elif defined(__APPLE__) #include -#include #endif +#ifdef _WIN32 +#include +#include +#include +#include "common/windows/timer_resolution.h" +#include "core/core_timing.h" +#endif + +using namespace Common::Literals; + namespace QtCommon { QWidget* rootObject = nullptr; @@ -27,6 +53,11 @@ QWidget* rootObject = nullptr; std::unique_ptr system = nullptr; std::shared_ptr vfs = nullptr; std::unique_ptr provider = nullptr; +std::unique_ptr emu_thread = nullptr; + +const QStringList supported_file_extensions = {QStringLiteral("nro"), QStringLiteral("nso"), + QStringLiteral("nca"), QStringLiteral("xci"), + QStringLiteral("nsp"), QStringLiteral("kip")}; Core::Frontend::WindowSystemType GetWindowSystemType() { // Determine WSI type based on Qt platform. @@ -103,18 +134,148 @@ Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow* window) } const QString tr(const char* str) { - return QGuiApplication::tr(str); + return rootObject->tr(str); } const QString tr(const std::string& str) { - return QGuiApplication::tr(str.c_str()); + return rootObject->tr(str.c_str()); +} + +static void LogRuntimes() { +#ifdef _MSC_VER + // It is possible that the name of the dll will change. + // vcruntime140.dll is for 2015 and onwards + static constexpr char runtime_dll_name[] = "vcruntime140.dll"; + UINT sz = GetFileVersionInfoSizeA(runtime_dll_name, nullptr); + bool runtime_version_inspection_worked = false; + if (sz > 0) { + std::vector buf(sz); + if (GetFileVersionInfoA(runtime_dll_name, 0, sz, buf.data())) { + VS_FIXEDFILEINFO* pvi; + sz = sizeof(VS_FIXEDFILEINFO); + if (VerQueryValueA(buf.data(), "\\", reinterpret_cast(&pvi), &sz)) { + if (pvi->dwSignature == VS_FFI_SIGNATURE) { + runtime_version_inspection_worked = true; + LOG_INFO(Frontend, "MSVC Compiler: {} Runtime: {}.{}.{}.{}", _MSC_VER, + pvi->dwProductVersionMS >> 16, pvi->dwProductVersionMS & 0xFFFF, + pvi->dwProductVersionLS >> 16, pvi->dwProductVersionLS & 0xFFFF); + } + } + } + } + if (!runtime_version_inspection_worked) { + LOG_INFO(Frontend, "Unable to inspect {}", runtime_dll_name); + } +#endif + LOG_INFO(Frontend, "Qt Compile: {} Runtime: {}", QT_VERSION_STR, qVersion()); +} + +static QString PrettyProductName() { +#ifdef _WIN32 + // After Windows 10 Version 2004, Microsoft decided to switch to a different notation: 20H2 + // With that notation change they changed the registry key used to denote the current version + QSettings windows_registry( + QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"), + QSettings::NativeFormat); + const QString release_id = windows_registry.value(QStringLiteral("ReleaseId")).toString(); + if (release_id == QStringLiteral("2009")) { + const u32 current_build = windows_registry.value(QStringLiteral("CurrentBuild")).toUInt(); + const QString display_version = + windows_registry.value(QStringLiteral("DisplayVersion")).toString(); + const u32 ubr = windows_registry.value(QStringLiteral("UBR")).toUInt(); + u32 version = 10; + if (current_build >= 22000) { + version = 11; + } + return QStringLiteral("Windows %1 Version %2 (Build %3.%4)") + .arg(QString::number(version), display_version, QString::number(current_build), + QString::number(ubr)); + } +#endif + return QSysInfo::prettyProductName(); +} + +static void RemoveCachedContents() { + const auto cache_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::CacheDir); + const auto offline_fonts = cache_dir / "fonts"; + const auto offline_manual = cache_dir / "offline_web_applet_manual"; + const auto offline_legal_information = cache_dir / "offline_web_applet_legal_information"; + const auto offline_system_data = cache_dir / "offline_web_applet_system_data"; + + Common::FS::RemoveDirRecursively(offline_fonts); + Common::FS::RemoveDirRecursively(offline_manual); + Common::FS::RemoveDirRecursively(offline_legal_information); + Common::FS::RemoveDirRecursively(offline_system_data); } void Init(QWidget* root) { - system = std::make_unique(); rootObject = root; + + system = std::make_unique(); vfs = std::make_unique(); provider = std::make_unique(); + + // initialization stuff + Common::FS::CreateEdenPaths(); + + system->Initialize(); + + Common::Log::Initialize(); + Common::Log::Start(); + + Network::Init(); + + QtCommon::Meta::RegisterMetaTypes(); + + // build version + const auto branch_name = std::string(Common::g_scm_branch); + const auto description = std::string(Common::g_scm_desc); + const auto build_id = std::string(Common::g_build_id); + + const auto yuzu_build = fmt::format("Eden Development Build | {}-{}", branch_name, description); + const auto override_build = + fmt::format(fmt::runtime(std::string(Common::g_title_bar_format_idle)), build_id); + const auto yuzu_build_version = override_build.empty() ? yuzu_build : override_build; + const auto processor_count = std::thread::hardware_concurrency(); + + // info logging + LOG_INFO(Frontend, "Eden Version: {}", yuzu_build_version); + LogRuntimes(); +#ifdef ARCHITECTURE_x86_64 + const auto& caps = Common::g_cpu_caps; + std::string cpu_string = caps.cpu_string; + if (caps.avx || caps.avx2 || caps.avx512f) { + cpu_string += " | AVX"; + if (caps.avx512f) { + cpu_string += "512"; + } else if (caps.avx2) { + cpu_string += '2'; + } + if (caps.fma) { + cpu_string += " | FMA"; + } + } + LOG_INFO(Frontend, "Host CPU: {}", cpu_string); + if (std::optional processor_core = Common::GetProcessorCount()) { + LOG_INFO(Frontend, "Host CPU Cores: {}", *processor_core); + } +#endif + LOG_INFO(Frontend, "Host CPU Threads: {}", processor_count); + LOG_INFO(Frontend, "Host OS: {}", PrettyProductName().toStdString()); + LOG_INFO(Frontend, "Host RAM: {:.2f} GiB", + Common::GetMemInfo().TotalPhysicalMemory / f64{1_GiB}); + LOG_INFO(Frontend, "Host Swap: {:.2f} GiB", Common::GetMemInfo().TotalSwapMemory / f64{1_GiB}); +#ifdef _WIN32 + LOG_INFO(Frontend, "Host Timer Resolution: {:.4f} ms", + std::chrono::duration_cast>( + Common::Windows::SetCurrentTimerResolutionToMaximum()) + .count()); + QtCommon::system->CoreTiming().SetTimerResolutionNs( + Common::Windows::GetCurrentTimerResolution()); +#endif + + // Remove cached contents generated during the previous session + RemoveCachedContents(); } std::filesystem::path GetEdenCommand() { @@ -137,4 +298,30 @@ std::filesystem::path GetEdenCommand() { return command; } +void SetupContentProviders() { + system->SetContentProvider(std::make_unique()); + system->RegisterContentProvider(FileSys::ContentProviderUnionSlot::FrontendManual, + provider.get()); + system->GetFileSystemController().CreateFactories(*vfs); +} + +void SetupHID() { + system->HIDCore().ReloadInputDevices(); +} + +QString ReadableByteSize(qulonglong size) { + return QString::fromStdString(FrontendCommon::DataManager::ReadableBytesSize(size)); +} + +QPixmap CreateCirclePixmapFromColor(const QColor& color) { + QPixmap circle_pixmap(16, 16); + circle_pixmap.fill(Qt::transparent); + QPainter painter(&circle_pixmap); + painter.setRenderHint(QPainter::Antialiasing); + painter.setPen(color); + painter.setBrush(color); + painter.drawEllipse({circle_pixmap.width() / 2.0, circle_pixmap.height() / 2.0}, 7.0, 7.0); + return circle_pixmap; +} + } // namespace QtCommon diff --git a/src/qt_common/qt_common.h b/src/qt_common/qt_common.h index 19c09ef5d0..ea8e3f73a2 100644 --- a/src/qt_common/qt_common.h +++ b/src/qt_common/qt_common.h @@ -1,24 +1,35 @@ // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later -#ifndef QT_COMMON_H -#define QT_COMMON_H +#pragma once #include #include -#include + #include "core/core.h" #include "core/file_sys/registered_cache.h" +#include "core/frontend/emu_window.h" -#include +#include "qt_common/render/emu_thread.h" + +#include "core/file_sys/vfs/vfs_real.h" + +enum class StartGameType { + Normal, // Can use custom configuration + Global, // Only uses global configuration +}; namespace QtCommon { +// TODO: Remove QWidget dependency extern QWidget* rootObject; extern std::unique_ptr system; extern std::shared_ptr vfs; extern std::unique_ptr provider; +extern std::unique_ptr emu_thread; + +extern const QStringList supported_file_extensions; typedef std::function QtProgressCallback; @@ -28,9 +39,25 @@ Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow* window) void Init(QWidget* root); +void SetupContentProviders(); +void SetupHID(); + const QString tr(const char* str); const QString tr(const std::string& str); +// TODO: Find a better place for these + +/// Convert a size in bytes into a readable format (KiB, MiB, etc.) +[[nodiscard]] QString ReadableByteSize(qulonglong size); + +/** + * Creates a circle pixmap from a specified color + * @param color The color the pixmap shall have + * @return QPixmap circle pixmap + */ +[[nodiscard]] QPixmap CreateCirclePixmapFromColor(const QColor& color); + + + std::filesystem::path GetEdenCommand(); } // namespace QtCommon -#endif diff --git a/src/qt_common/render/context.h b/src/qt_common/render/context.h new file mode 100644 index 0000000000..a0ecade9c8 --- /dev/null +++ b/src/qt_common/render/context.h @@ -0,0 +1,107 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include "common/logging.h" +#include "common/settings.h" +#include "core/frontend/graphics_context.h" + +#ifdef HAS_OPENGL +#include +#include +#endif + +#ifdef HAS_OPENGL +class OpenGLSharedContext : public Core::Frontend::GraphicsContext { +public: + /// Create the original context that should be shared from + explicit OpenGLSharedContext(QSurface* surface_) : surface{surface_} { + QSurfaceFormat format; + format.setVersion(4, 6); + format.setProfile(QSurfaceFormat::CompatibilityProfile); + format.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions); + if (Settings::values.renderer_debug) { + format.setOption(QSurfaceFormat::FormatOption::DebugContext); + } + // TODO: expose a setting for buffer value (ie default/single/double/triple) + format.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior); + format.setSwapInterval(0); + + context = std::make_unique(); + context->setFormat(format); + if (!context->create()) { + LOG_ERROR(Frontend, "Unable to create main openGL context"); + } + } + + /// Create the shared contexts for rendering and presentation + explicit OpenGLSharedContext(QOpenGLContext* share_context, QSurface* main_surface = nullptr) { + + // disable vsync for any shared contexts + auto format = share_context->format(); + const int swap_interval = + Settings::values.vsync_mode.GetValue() == Settings::VSyncMode::Immediate ? 0 : 1; + + format.setSwapInterval(main_surface ? swap_interval : 0); + + context = std::make_unique(); + context->setShareContext(share_context); + context->setFormat(format); + if (!context->create()) { + LOG_ERROR(Frontend, "Unable to create shared openGL context"); + } + + if (!main_surface) { + offscreen_surface = std::make_unique(nullptr); + offscreen_surface->setFormat(format); + offscreen_surface->create(); + surface = offscreen_surface.get(); + } else { + surface = main_surface; + } + } + + ~OpenGLSharedContext() { + DoneCurrent(); + } + + void SwapBuffers() override { + context->swapBuffers(surface); + } + + void MakeCurrent() override { + // We can't track the current state of the underlying context in this wrapper class because + // Qt may make the underlying context not current for one reason or another. In particular, + // the WebBrowser uses GL, so it seems to conflict if we aren't careful. + // Instead of always just making the context current (which does not have any caching to + // check if the underlying context is already current) we can check for the current context + // in the thread local data by calling `currentContext()` and checking if its ours. + if (QOpenGLContext::currentContext() != context.get()) { + context->makeCurrent(surface); + } + } + + void DoneCurrent() override { + context->doneCurrent(); + } + + QOpenGLContext* GetShareContext() { + return context.get(); + } + + const QOpenGLContext* GetShareContext() const { + return context.get(); + } + +private: + // Avoid using Qt parent system here since we might move the QObjects to new threads + // As a note, this means we should avoid using slots/signals with the objects too + std::unique_ptr context; + std::unique_ptr offscreen_surface{}; + QSurface* surface; +}; +#endif + +class DummyContext : public Core::Frontend::GraphicsContext {}; diff --git a/src/qt_common/render/emu_thread.cpp b/src/qt_common/render/emu_thread.cpp new file mode 100644 index 0000000000..1b65867509 --- /dev/null +++ b/src/qt_common/render/emu_thread.cpp @@ -0,0 +1,83 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include +#include "core/core.h" +#include "core/cpu_manager.h" +#include "emu_thread.h" +#include "qt_common/qt_common.h" +#include "video_core/gpu.h" +#include "video_core/rasterizer_interface.h" +#include "video_core/renderer_base.h" + +EmuThread::EmuThread() {} + +EmuThread::~EmuThread() = default; + +void EmuThread::run() { + Common::SetCurrentThreadName("EmuControlThread"); + + auto& gpu = QtCommon::system->GPU(); + auto stop_token = m_stop_source.get_token(); + + QtCommon::system->RegisterHostThread(); + + // Main process has been loaded. Make the context current to this thread and begin GPU and CPU + // execution. + gpu.ObtainContext(); + + emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); + if (Settings::values.use_disk_shader_cache.GetValue()) { + QtCommon::system->Renderer().ReadRasterizer()->LoadDiskResources( + QtCommon::system->GetApplicationProcessProgramID(), stop_token, + [this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) { + emit LoadProgress(stage, value, total); + }); + } + emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); + + gpu.ReleaseContext(); + gpu.Start(); + + QtCommon::system->GetCpuManager().OnGpuReady(); + + if (QtCommon::system->DebuggerEnabled()) { + QtCommon::system->InitializeDebugger(); + } + + while (!stop_token.stop_requested()) { + std::unique_lock lk{m_should_run_mutex}; + if (m_should_run) { + QtCommon::system->Run(); + m_stopped.Reset(); + + m_should_run_cv.wait(lk, stop_token, [&] { return !m_should_run; }); + } else { + QtCommon::system->Pause(); + m_stopped.Set(); + + EmulationPaused(lk); + m_should_run_cv.wait(lk, stop_token, [&] { return m_should_run; }); + EmulationResumed(lk); + } + } + + // Shutdown the main emulated process + QtCommon::system->DetachDebugger(); + QtCommon::system->ShutdownMainProcess(); +} + +// Unlock while emitting signals so that the main thread can +// continue pumping events. + +void EmuThread::EmulationPaused(std::unique_lock& lk) { + lk.unlock(); + emit DebugModeEntered(); + lk.lock(); +} + +void EmuThread::EmulationResumed(std::unique_lock& lk) { + lk.unlock(); + emit DebugModeLeft(); + lk.lock(); +} diff --git a/src/qt_common/render/emu_thread.h b/src/qt_common/render/emu_thread.h new file mode 100644 index 0000000000..92bae50979 --- /dev/null +++ b/src/qt_common/render/emu_thread.h @@ -0,0 +1,97 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include "common/logging.h" +#include "common/thread.h" + +namespace Core { +class System; +} // namespace Core + +namespace VideoCore { +enum class LoadCallbackStage; +} // namespace VideoCore + +class EmuThread final : public QThread { + Q_OBJECT + +public: + explicit EmuThread(); + ~EmuThread() override; + + /** + * Start emulation (on new thread) + * @warning Only call when not running! + */ + void run() override; + + /** + * Sets whether the emulation thread should run or not + * @param should_run Boolean value, set the emulation thread to running if true + */ + void SetRunning(bool should_run) { + // TODO: Prevent other threads from modifying the state until we finish. + { + // Notify the running thread to change state. + std::unique_lock run_lk{m_should_run_mutex}; + m_should_run = should_run; + m_should_run_cv.notify_one(); + } + + // Wait until paused, if pausing. + if (!should_run) { + m_stopped.Wait(); + } + } + + /** + * Check if the emulation thread is running or not + * @return True if the emulation thread is running, otherwise false + */ + bool IsRunning() const { + return m_should_run; + } + + /** + * Requests for the emulation thread to immediately stop running + */ + void ForceStop() { + LOG_WARNING(Frontend, "Force stopping EmuThread"); + m_stop_source.request_stop(); + } + +private: + void EmulationPaused(std::unique_lock& lk); + void EmulationResumed(std::unique_lock& lk); + +private: + std::stop_source m_stop_source; + std::mutex m_should_run_mutex; + std::condition_variable_any m_should_run_cv; + Common::Event m_stopped; + bool m_should_run{true}; + +signals: + /** + * Emitted when the CPU has halted execution + * + * @warning When connecting to this signal from other threads, make sure to specify either + * Qt::QueuedConnection (invoke slot within the destination object's message thread) or even + * Qt::BlockingQueuedConnection (additionally block source thread until slot returns) + */ + void DebugModeEntered(); + + /** + * Emitted right before the CPU continues execution + * + * @warning When connecting to this signal from other threads, make sure to specify either + * Qt::QueuedConnection (invoke slot within the destination object's message thread) or even + * Qt::BlockingQueuedConnection (additionally block source thread until slot returns) + */ + void DebugModeLeft(); + + void LoadProgress(VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total); +}; diff --git a/src/qt_common/util/applet.h b/src/qt_common/util/applet.h index f88aa066b2..41294466da 100644 --- a/src/qt_common/util/applet.h +++ b/src/qt_common/util/applet.h @@ -1,9 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later -#ifndef QT_APPLET_UTIL_H -#define QT_APPLET_UTIL_H +#pragma once // TODO namespace QtCommon::Applets {} -#endif // QT_APPLET_UTIL_H diff --git a/src/qt_common/util/content.cpp b/src/qt_common/util/content.cpp index f705b3e00f..f22242e7f7 100644 --- a/src/qt_common/util/content.cpp +++ b/src/qt_common/util/content.cpp @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later +#include "core/file_sys/card_image.h" #include "qt_common/util/content.h" #include "qt_common/util/game.h" @@ -494,5 +495,94 @@ void ImportDataDir(FrontendCommon::DataManager::DataDir data_dir, const std::str }); } -// TODO(crueter): Port InstallFirmware et al. from QML Branch +bool CheckKeys() { + if (!ContentManager::AreKeysPresent()) { + QtCommon::Frontend::Information( + tr("Keys not installed"), + tr("Install decryption keys and restart Eden before attempting to install firmware.")); + return false; + } + + return true; +} + +void InstallFirmware() { + if (!CheckKeys()) + return; + + const QString firmware_source_location = + QtCommon::Frontend::GetExistingDirectory(tr("Select Dumped Firmware Source Location"), {}); + + if (!firmware_source_location.isEmpty()) + QtCommon::Content::InstallFirmware(firmware_source_location, false); +} + +void InstallFirmwareZip() { + if (!CheckKeys()) + return; + + const QString firmware_zip_location = QtCommon::Frontend::GetOpenFileName( + tr("Select Dumped Firmware ZIP"), {}, tr("Zipped Archives (*.zip)")); + + if (firmware_zip_location.isEmpty()) + return; + + const QString qCacheDir = QtCommon::Content::UnzipFirmwareToTmp(firmware_zip_location); + + // In this case, it has to be done recursively, since sometimes people + // will pack it into a subdirectory after dumping + if (!qCacheDir.isEmpty()) { + QtCommon::Content::InstallFirmware(qCacheDir, true); + std::error_code ec; + std::filesystem::remove_all(std::filesystem::temp_directory_path() / "eden" / "firmware", + ec); + + if (ec) { + QtCommon::Frontend::Warning( + tr("Firmware cleanup failed"), + tr("Failed to clean up extracted firmware cache.\n" + "Check write permissions in the system temp directory and try " + "again.\nOS reported error: %1") + .arg(QString::fromStdString(ec.message()))); + } + } +} + +void configureFilesystemProvider(const std::string& filepath) { + // Ensure all NCAs are registered before launching the game + const auto file = QtCommon::vfs->OpenFile(filepath, FileSys::OpenMode::Read); + if (!file) { + return; + } + + auto loader = Loader::GetLoader(*QtCommon::system, file); + if (!loader) { + return; + } + + const auto file_type = loader->GetFileType(); + if (file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error) { + return; + } + + u64 program_id = 0; + const auto res2 = loader->ReadProgramId(program_id); + if (res2 == Loader::ResultStatus::Success && file_type == Loader::FileType::NCA) { + QtCommon::provider->AddEntry(FileSys::TitleType::Application, + FileSys::GetCRTypeFromNCAType(FileSys::NCA{file}.GetType()), + program_id, file); + } else if (res2 == Loader::ResultStatus::Success && + (file_type == Loader::FileType::XCI || file_type == Loader::FileType::NSP)) { + const auto nsp = file_type == Loader::FileType::NSP + ? std::make_shared(file) + : FileSys::XCI{file}.GetSecurePartitionNSP(); + for (const auto& title : nsp->GetNCAs()) { + for (const auto& entry : title.second) { + QtCommon::provider->AddEntry(entry.first.first, entry.first.second, title.first, + entry.second->GetBaseFile()); + } + } + } +} + } // namespace QtCommon::Content diff --git a/src/qt_common/util/content.h b/src/qt_common/util/content.h index 20dacf540a..6b0cbb5965 100644 --- a/src/qt_common/util/content.h +++ b/src/qt_common/util/content.h @@ -1,8 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later -#ifndef QT_CONTENT_UTIL_H -#define QT_CONTENT_UTIL_H +#pragma once #include #include "common/common_types.h" @@ -37,9 +36,12 @@ inline const QString GetKeyInstallResultString(FirmwareManager::KeyInstallResult } void InstallFirmware(const QString& location, bool recursive); - QString UnzipFirmwareToTmp(const QString& location); +bool CheckKeys(); +void InstallFirmware(); +void InstallFirmwareZip(); + // Keys // void InstallKeys(); @@ -54,7 +56,9 @@ void ExportDataDir(FrontendCommon::DataManager::DataDir dir, const std::string& void ImportDataDir(FrontendCommon::DataManager::DataDir dir, const std::string& user_id = "", std::function callback = {}); +// Loader // +void configureFilesystemProvider(const std::string& filepath); + // Profiles // void FixProfiles(); } // namespace QtCommon::Content -#endif // QT_CONTENT_UTIL_H diff --git a/src/qt_common/util/fs.h b/src/qt_common/util/fs.h index 5ab59e2a45..8f42beaaa3 100644 --- a/src/qt_common/util/fs.h +++ b/src/qt_common/util/fs.h @@ -1,12 +1,12 @@ // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + #include #include #include "common/common_types.h" -#pragma once - namespace QtCommon::FS { void LinkRyujinx(std::filesystem::path& from, std::filesystem::path& to); diff --git a/src/qt_common/util/game.cpp b/src/qt_common/util/game.cpp index e037fdae6c..ddbe979765 100644 --- a/src/qt_common/util/game.cpp +++ b/src/qt_common/util/game.cpp @@ -11,7 +11,6 @@ #include "qt_common/abstract/frontend.h" #include "qt_common/config/uisettings.h" #include "qt_common/qt_common.h" -#include "yuzu/util/util.h" #include #include @@ -22,6 +21,7 @@ #include #include "common/scope_exit.h" #include "common/string_util.h" +#include "common/fs/file.h" #else #include #include "fmt/ostream.h" @@ -367,6 +367,7 @@ void ResetMetadata(bool show_message) { // Uhhh // +// TODO(crueter): Make QtCommon::Shortcut // Messages in pre-defined message boxes for less code spaghetti inline constexpr bool CreateShortcutMessagesGUI(ShortcutMessages imsg, const QString& game_title) { int result = 0; @@ -533,4 +534,110 @@ void CreateHomeMenuShortcut(ShortcutTarget target) { CreateShortcut(game_path, QLaunchId, "Switch Home Menu", target, "-qlaunch", false); } +bool SaveIconToFile(const std::filesystem::path& icon_path, const QImage& image) { +#if defined(WIN32) +#pragma pack(push, 2) + struct IconDir { + WORD id_reserved; + WORD id_type; + WORD id_count; + }; + + struct IconDirEntry { + BYTE width; + BYTE height; + BYTE color_count; + BYTE reserved; + WORD planes; + WORD bit_count; + DWORD bytes_in_res; + DWORD image_offset; + }; +#pragma pack(pop) + + const QImage source_image = image.convertToFormat(QImage::Format_RGB32); + constexpr std::array scale_sizes{256, 128, 64, 48, 32, 24, 16}; + constexpr int bytes_per_pixel = 4; + + const IconDir icon_dir{ + .id_reserved = 0, + .id_type = 1, + .id_count = static_cast(scale_sizes.size()), + }; + + Common::FS::IOFile icon_file(icon_path.string(), Common::FS::FileAccessMode::Write, + Common::FS::FileType::BinaryFile); + if (!icon_file.IsOpen()) { + return false; + } + + if (!icon_file.Write(icon_dir)) { + return false; + } + + std::size_t image_offset = sizeof(IconDir) + (sizeof(IconDirEntry) * scale_sizes.size()); + for (std::size_t i = 0; i < scale_sizes.size(); i++) { + const int image_size = scale_sizes[i] * scale_sizes[i] * bytes_per_pixel; + const IconDirEntry icon_entry{ + .width = static_cast(scale_sizes[i]), + .height = static_cast(scale_sizes[i]), + .color_count = 0, + .reserved = 0, + .planes = 1, + .bit_count = bytes_per_pixel * 8, + .bytes_in_res = static_cast(sizeof(BITMAPINFOHEADER) + image_size), + .image_offset = static_cast(image_offset), + }; + image_offset += icon_entry.bytes_in_res; + if (!icon_file.Write(icon_entry)) { + return false; + } + } + + for (std::size_t i = 0; i < scale_sizes.size(); i++) { + const QImage scaled_image = source_image.scaled( + scale_sizes[i], scale_sizes[i], Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + const BITMAPINFOHEADER info_header{ + .biSize = sizeof(BITMAPINFOHEADER), + .biWidth = scaled_image.width(), + .biHeight = scaled_image.height() * 2, + .biPlanes = 1, + .biBitCount = bytes_per_pixel * 8, + .biCompression = BI_RGB, + .biSizeImage{}, + .biXPelsPerMeter{}, + .biYPelsPerMeter{}, + .biClrUsed{}, + .biClrImportant{}, + }; + + if (!icon_file.Write(info_header)) { + return false; + } + + for (int y = 0; y < scaled_image.height(); y++) { + const auto* line = scaled_image.scanLine(scaled_image.height() - 1 - y); + std::vector line_data(scaled_image.width() * bytes_per_pixel); + std::memcpy(line_data.data(), line, line_data.size()); + if (!icon_file.Write(line_data)) { + return false; + } + } + } + icon_file.Close(); + + return true; +#elif defined(__unix__) && !defined(__APPLE__) && !defined(__ANDROID__) + // Convert and write the icon as a PNG + if (!image.save(QString::fromStdString(icon_path.string()))) { + LOG_ERROR(Frontend, "Could not write icon as PNG to file"); + } else { + LOG_INFO(Frontend, "Wrote an icon to {}", icon_path.string()); + } + return true; +#else + return false; +#endif +} + } // namespace QtCommon::Game diff --git a/src/qt_common/util/game.h b/src/qt_common/util/game.h index 0d7f03fa86..8722574ed7 100644 --- a/src/qt_common/util/game.h +++ b/src/qt_common/util/game.h @@ -1,8 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later -#ifndef QT_GAME_UTIL_H -#define QT_GAME_UTIL_H +#pragma once #include #include @@ -69,6 +68,12 @@ void CreateShortcut(const std::string& game_path, const u64 program_id, std::string GetShortcutPath(ShortcutTarget target); void CreateHomeMenuShortcut(ShortcutTarget target); -} // namespace QtCommon::Game +/** + * Saves a windows icon to a file + * @param path The icons path + * @param image The image to save + * @return bool If the operation succeeded + */ +[[nodiscard]] bool SaveIconToFile(const std::filesystem::path& icon_path, const QImage& image); -#endif // QT_GAME_UTIL_H +} // namespace QtCommon::Game diff --git a/src/qt_common/util/meta.h b/src/qt_common/util/meta.h index ae6dbc49d8..d408b5759c 100644 --- a/src/qt_common/util/meta.h +++ b/src/qt_common/util/meta.h @@ -1,8 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later -#ifndef QT_META_H -#define QT_META_H +#pragma once #include @@ -12,4 +11,3 @@ namespace QtCommon::Meta { void RegisterMetaTypes(); } // namespace QtCommon::Meta -#endif // QT_META_H diff --git a/src/qt_common/util/mod.cpp b/src/qt_common/util/mod.cpp index 3e75cb21fa..c09b39cf82 100644 --- a/src/qt_common/util/mod.cpp +++ b/src/qt_common/util/mod.cpp @@ -33,10 +33,13 @@ QStringList GetModFolders(const QString& root, const QString& fallbackName) { std_path = paths[0]; QString default_name; - if (!fallbackName.isEmpty()) - default_name = fallbackName; - else if (!paths.empty()) + + // If this is an atmosphere-packed mod, the default name will end up as the game's title ID. + // So in this case ignore it and use the zip name instead + if (!paths.empty() && std_path.string().find("atmosphere") == std::string::npos) default_name = QString::fromStdString(std_path.filename().string()); + else if (!fallbackName.isEmpty()) + default_name = fallbackName; else default_name = root.split(QLatin1Char('/')).last(); diff --git a/src/qt_common/util/path.h b/src/qt_common/util/path.h index aff0009f34..ba77625226 100644 --- a/src/qt_common/util/path.h +++ b/src/qt_common/util/path.h @@ -1,8 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later -#ifndef QT_PATH_UTIL_H -#define QT_PATH_UTIL_H +#pragma once #include #include "common/common_types.h" @@ -10,5 +9,3 @@ namespace QtCommon::Path { bool OpenShaderCache(u64 program_id, QObject* parent); } - -#endif // QT_PATH_UTIL_H diff --git a/src/qt_common/util/rom.h b/src/qt_common/util/rom.h index 5ceadaf46b..6ea5082082 100644 --- a/src/qt_common/util/rom.h +++ b/src/qt_common/util/rom.h @@ -1,16 +1,11 @@ // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later -#ifndef QT_ROM_UTIL_H -#define QT_ROM_UTIL_H +#pragma once #include #include "qt_common/qt_common.h" namespace QtCommon::ROM { - -bool RomFSRawCopy(size_t total_size, size_t& read_size, QtProgressCallback callback, - const FileSys::VirtualDir& src, const FileSys::VirtualDir& dest, bool full); - +bool RomFSRawCopy(size_t total_size, size_t& read_size, QtProgressCallback callback, const FileSys::VirtualDir& src, const FileSys::VirtualDir& dest, bool full); } -#endif // QT_ROM_UTIL_H diff --git a/src/yuzu/vk_device_info.cpp b/src/qt_common/util/vk.cpp similarity index 98% rename from src/yuzu/vk_device_info.cpp rename to src/qt_common/util/vk.cpp index 1e8e140d77..8ca7f4a665 100644 --- a/src/yuzu/vk_device_info.cpp +++ b/src/qt_common/util/vk.cpp @@ -11,13 +11,13 @@ #include "common/dynamic_library.h" #include "common/logging.h" +#include "qt_common/util/vk.h" #include "video_core/vulkan_common/vulkan_device.h" #include "video_core/vulkan_common/vulkan_instance.h" #include "video_core/vulkan_common/vulkan_library.h" #include "video_core/vulkan_common/vulkan_surface.h" #include "video_core/vulkan_common/vulkan_wrapper.h" #include "vulkan/vulkan_core.h" -#include "yuzu/vk_device_info.h" class QWindow; diff --git a/src/qt_common/util/vk.h b/src/qt_common/util/vk.h new file mode 100644 index 0000000000..3e6730f643 --- /dev/null +++ b/src/qt_common/util/vk.h @@ -0,0 +1,71 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include "common/common_types.h" +#include "common/settings_enums.h" +#include "vulkan/vulkan_core.h" + +static const std::vector default_present_modes{VK_PRESENT_MODE_IMMEDIATE_KHR, + VK_PRESENT_MODE_FIFO_KHR}; + +// Converts a setting to a present mode (or vice versa) +static inline constexpr VkPresentModeKHR VSyncSettingToMode(Settings::VSyncMode mode) { + switch (mode) { + case Settings::VSyncMode::Immediate: + return VK_PRESENT_MODE_IMMEDIATE_KHR; + case Settings::VSyncMode::Mailbox: + return VK_PRESENT_MODE_MAILBOX_KHR; + case Settings::VSyncMode::Fifo: + return VK_PRESENT_MODE_FIFO_KHR; + case Settings::VSyncMode::FifoRelaxed: + return VK_PRESENT_MODE_FIFO_RELAXED_KHR; + default: + return VK_PRESENT_MODE_FIFO_KHR; + } +} + +static inline constexpr Settings::VSyncMode PresentModeToSetting(VkPresentModeKHR mode) { + switch (mode) { + case VK_PRESENT_MODE_IMMEDIATE_KHR: + return Settings::VSyncMode::Immediate; + case VK_PRESENT_MODE_MAILBOX_KHR: + return Settings::VSyncMode::Mailbox; + case VK_PRESENT_MODE_FIFO_KHR: + return Settings::VSyncMode::Fifo; + case VK_PRESENT_MODE_FIFO_RELAXED_KHR: + return Settings::VSyncMode::FifoRelaxed; + default: + return Settings::VSyncMode::Fifo; + } +} + +class QWindow; + +namespace Settings { +enum class VSyncMode : u32; +} + +namespace VkDeviceInfo { +// Short class to record Vulkan driver information for configuration purposes +class Record { +public: + explicit Record(std::string_view name, const std::vector& vsync_modes, + bool has_broken_compute); + ~Record(); + + const std::string name; + const std::vector vsync_support; + const bool has_broken_compute; +}; + +// TODO(crueter): Port some configure_graphics.cpp stuff +void PopulateRecords(std::vector& records, QWindow* window); +} // namespace VkDeviceInfo diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp index fc102cadac..0447dfe90d 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp @@ -462,6 +462,13 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct ctx.AddCapability(spv::Capability::ImageGatherExtended); ctx.AddCapability(spv::Capability::ImageQuery); ctx.AddCapability(spv::Capability::SampledBuffer); + // TODO: this usage needs to be tracked properly + if (ctx.profile.support_sampled_image_array_nonuniform_indexing) { + if (ctx.profile.supported_spirv < 0x00010400) + ctx.AddExtension("SPV_EXT_descriptor_indexing"); + ctx.AddCapability(spv::Capability::ShaderNonUniform); + ctx.AddCapability(spv::Capability::SampledImageArrayNonUniformIndexing); + } } void PatchPhiNodes(IR::Program& program, EmitContext& ctx) { diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_convert.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_convert.cpp index 115e9ed4ed..9fa84cceb7 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_convert.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_convert.cpp @@ -151,7 +151,7 @@ Id EmitConvertU32U64(EmitContext& ctx, Id value) { } Id EmitConvertF16F32(EmitContext& ctx, Id value) { -#ifdef ANDROID +#ifdef __ANDROID__ return ctx.OpFConvert(ctx.F16[1], value); #else const auto result = ctx.OpFConvert(ctx.F16[1], value); diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp index 62a82dce9d..df4da81356 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp @@ -14,37 +14,10 @@ namespace Shader::Backend::SPIRV { namespace { -class DescriptorIndex { -public: - explicit DescriptorIndex(EmitContext& ctx, const IR::Value& index) - : id{index.IsImmediate() ? ctx.Const(index.U32()) : ctx.Def(index)}, - is_non_uniform{ctx.profile.support_sampled_image_array_nonuniform_indexing && - !index.IsImmediate()} { - if (!is_non_uniform) { - return; - } - if (ctx.profile.supported_spirv < 0x00010400) { - ctx.AddExtension("SPV_EXT_descriptor_indexing"); - } - ctx.AddCapability(spv::Capability::ShaderNonUniform); - ctx.AddCapability(spv::Capability::SampledImageArrayNonUniformIndexing); - Decorate(ctx, id); - } - Id Value() const { - return id; - } - - void Decorate(EmitContext& ctx, Id object) const { - if (is_non_uniform) { - ctx.Decorate(object, spv::Decoration::NonUniform); - } - } - -private: - Id id; - bool is_non_uniform; -}; +[[nodiscard]] bool IsNonUniformDescriptor(EmitContext& ctx, const IR::Value& index) noexcept { + return ctx.profile.support_sampled_image_array_nonuniform_indexing && !index.IsImmediate(); +} class ImageOperands { public: @@ -221,11 +194,13 @@ private: Id Texture(EmitContext& ctx, IR::TextureInstInfo info, [[maybe_unused]] const IR::Value& index) { const TextureDefinition& def{ctx.textures.at(info.descriptor_index)}; if (def.count > 1) { - const DescriptorIndex idx{ctx, index}; - const Id pointer{ctx.OpAccessChain(def.pointer_type, def.id, idx.Value())}; - idx.Decorate(ctx, pointer); + auto const idx = index.IsImmediate() ? ctx.Const(index.U32()) : ctx.Def(index); + if (!ctx.non_uniform_ids.contains(idx.value) && IsNonUniformDescriptor(ctx, index)) { + ctx.Decorate(idx, spv::Decoration::NonUniform); + ctx.non_uniform_ids.insert(idx.value); + } + const Id pointer{ctx.OpAccessChain(def.pointer_type, def.id, idx)}; const Id object{ctx.OpLoad(def.sampled_type, pointer)}; - idx.Decorate(ctx, object); return object; } else { return ctx.OpLoad(def.sampled_type, def.id); @@ -244,13 +219,14 @@ Id TextureImage(EmitContext& ctx, IR::TextureInstInfo info, const IR::Value& ind } else { const TextureDefinition& def{ctx.textures.at(info.descriptor_index)}; if (def.count > 1) { - const DescriptorIndex idx{ctx, index}; - const Id ptr{ctx.OpAccessChain(def.pointer_type, def.id, idx.Value())}; - idx.Decorate(ctx, ptr); - const Id object{ctx.OpLoad(def.sampled_type, ptr)}; - idx.Decorate(ctx, object); - const Id image{ctx.OpImage(def.image_type, object)}; - idx.Decorate(ctx, image); + auto const idx = index.IsImmediate() ? ctx.Const(index.U32()) : ctx.Def(index); + if (!ctx.non_uniform_ids.contains(idx.value) && IsNonUniformDescriptor(ctx, index)) { + ctx.Decorate(idx, spv::Decoration::NonUniform); + ctx.non_uniform_ids.insert(idx.value); + } + const Id ptr = ctx.OpAccessChain(def.pointer_type, def.id, idx); + const Id object = ctx.OpLoad(def.sampled_type, ptr); + const Id image = ctx.OpImage(def.image_type, object); return image; } return ctx.OpImage(def.image_type, ctx.OpLoad(def.sampled_type, def.id)); @@ -530,7 +506,7 @@ Id EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& Id result = Emit(&EmitContext::OpImageSparseSampleExplicitLod, &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4], Texture(ctx, info, index), coords, operands.Mask(), operands.Span()); -#ifdef ANDROID +#ifdef __ANDROID__ if (Settings::values.fix_bloom_effects.GetValue()) { result = ctx.OpVectorTimesScalar(ctx.F32[4], result, ctx.Const(0.98f)); } diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h index de56809a98..55385de0b0 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h @@ -9,6 +9,7 @@ #include #include +#include #include "shader_recompiler/backend/bindings.h" #include "shader_recompiler/frontend/ir/program.h" @@ -366,6 +367,9 @@ public: Id load_const_func_u32x2{}; Id load_const_func_u32x4{}; + // Sirit::Id doesn't play nice with *::set<> + ankerl::unordered_dense::set non_uniform_ids; + private: void DefineCommonTypes(const Info& info); void DefineCommonConstants(); diff --git a/src/shader_recompiler/frontend/ir/attribute.h b/src/shader_recompiler/frontend/ir/attribute.h index 943c8d673f..4adf980949 100644 --- a/src/shader_recompiler/frontend/ir/attribute.h +++ b/src/shader_recompiler/frontend/ir/attribute.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project @@ -240,6 +240,14 @@ constexpr size_t NUM_FIXEDFNCTEXTURE = 10; return attribute >= Attribute::Generic0X && attribute <= Attribute::Generic31X; } +[[nodiscard]] inline bool IsLegacyAttribute(Attribute attribute) noexcept { + return (attribute >= Attribute::ColorFrontDiffuseR && + attribute <= Attribute::ColorBackSpecularA) || + attribute == Attribute::FogCoordinate || + (attribute >= Attribute::FixedFncTexture0S && + attribute <= Attribute::FixedFncTexture9Q); +} + [[nodiscard]] inline u32 GenericAttributeIndex(Attribute attribute) { if (!IsGeneric(attribute)) throw InvalidArgument("Attribute is not generic {}", attribute); diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/load_store_attribute.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/load_store_attribute.cpp index e3745ce084..d4990af7a6 100644 --- a/src/shader_recompiler/frontend/maxwell/translate/impl/load_store_attribute.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate/impl/load_store_attribute.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -163,12 +166,24 @@ void TranslatorVisitor::IPA(u64 insn) { const IR::Attribute attribute{ipa.attribute}; IR::F32 value{is_indexed ? ir.GetAttributeIndexed(X(ipa.index_reg)) : ir.GetAttribute(attribute)}; - if (IR::IsGeneric(attribute)) { - const ProgramHeader& sph{env.SPH()}; - const u32 attr_index{IR::GenericAttributeIndex(attribute)}; - const u32 element{static_cast(attribute) % 4}; - const std::array input_map{sph.ps.GenericInputMap(attr_index)}; - const bool is_perspective{input_map[element] == Shader::PixelImap::Perspective}; + const bool is_legacy{IR::IsLegacyAttribute(attribute)}; + if (IR::IsGeneric(attribute) || is_legacy) { + bool is_perspective{is_legacy && + ipa.interpolation_mode != InterpolationMode::Sc}; + if (!is_legacy) { + const ProgramHeader& sph{env.SPH()}; + const u32 attr_index{IR::GenericAttributeIndex(attribute)}; + const std::array input_map{sph.ps.GenericInputMap(attr_index)}; + Shader::PixelImap effective_imap{Shader::PixelImap::Unused}; + for (const Shader::PixelImap component : input_map) { + if (component != Shader::PixelImap::Unused) { + effective_imap = component; + break; + } + } + is_perspective = effective_imap == Shader::PixelImap::Perspective || + effective_imap == Shader::PixelImap::Unused; + } if (is_perspective) { const IR::F32 position_w{ir.GetAttribute(IR::Attribute::PositionW)}; value = ir.FPMul(value, position_w); diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp index f156192c13..705f20850a 100644 --- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp @@ -132,13 +132,7 @@ void AddNVNStorageBuffers(IR::Program& program) { } } -bool IsLegacyAttribute(IR::Attribute attribute) { - return (attribute >= IR::Attribute::ColorFrontDiffuseR && - attribute <= IR::Attribute::ColorBackSpecularA) || - attribute == IR::Attribute::FogCoordinate || - (attribute >= IR::Attribute::FixedFncTexture0S && - attribute <= IR::Attribute::FixedFncTexture9Q); -} +using IR::IsLegacyAttribute; //rescoped to attribute.h to make it visible in load_store_attribute.cpp IPA std::map GenerateLegacyToGenericMappings( const VaryingState& state, std::queue unused_generics, diff --git a/src/shader_recompiler/ir_opt/texture_pass.cpp b/src/shader_recompiler/ir_opt/texture_pass.cpp index 55b6f7213c..c500b53ff7 100644 --- a/src/shader_recompiler/ir_opt/texture_pass.cpp +++ b/src/shader_recompiler/ir_opt/texture_pass.cpp @@ -12,6 +12,7 @@ #include #include +#include "common/settings.h" #include "shader_recompiler/environment.h" #include "shader_recompiler/frontend/ir/basic_block.h" #include "shader_recompiler/frontend/ir/breadth_first_search.h" @@ -733,9 +734,8 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo break; } u32 index; - const u32 size_shift{cbuf.count > 1 ? DynamicDescriptorSizeShift(cbuf.dynamic_offset) - : DESCRIPTOR_SIZE_SHIFT}; - u32 count{cbuf.count}; + u32 size_shift = cbuf.count > 1 ? DynamicDescriptorSizeShift(cbuf.dynamic_offset) : DESCRIPTOR_SIZE_SHIFT; + u32 count = cbuf.count; switch (inst->GetOpcode()) { case IR::Opcode::ImageRead: case IR::Opcode::ImageAtomicIAdd32: @@ -821,8 +821,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo const auto insert_point{IR::Block::InstructionList::s_iterator_to(*inst)}; IR::IREmitter ir{*texture_inst.block, insert_point}; const IR::U32 shift{ir.Imm32(size_shift)}; - inst->SetArg(0, ir.UMin(ir.ShiftRightLogical(cbuf.dynamic_offset, shift), - ir.Imm32(count - 1))); + inst->SetArg(0, ir.UMin(ir.ShiftRightLogical(cbuf.dynamic_offset, shift), ir.Imm32(count - 1))); } else { inst->SetArg(0, IR::Value{}); } diff --git a/src/tests/common/host_memory.cpp b/src/tests/common/host_memory.cpp index cb040c942d..71354a6c59 100644 --- a/src/tests/common/host_memory.cpp +++ b/src/tests/common/host_memory.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -15,12 +18,19 @@ static constexpr auto PERMS = Common::MemoryPermission::ReadWrite; static constexpr auto HEAP = false; TEST_CASE("HostMemory: Initialize and deinitialize", "[common]") { - { HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); } - { HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); } + { + HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); + REQUIRE(mem.BackingBasePointer() != nullptr); + } + { + HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); + REQUIRE(mem.BackingBasePointer() != nullptr); + } } TEST_CASE("HostMemory: Simple map", "[common]") { HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); + REQUIRE(mem.BackingBasePointer() != nullptr); mem.Map(0x5000, 0x8000, 0x1000, PERMS, HEAP); volatile u8* const data = mem.VirtualBasePointer() + 0x5000; @@ -30,6 +40,7 @@ TEST_CASE("HostMemory: Simple map", "[common]") { TEST_CASE("HostMemory: Simple mirror map", "[common]") { HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); + REQUIRE(mem.BackingBasePointer() != nullptr); mem.Map(0x5000, 0x3000, 0x2000, PERMS, HEAP); mem.Map(0x8000, 0x4000, 0x1000, PERMS, HEAP); @@ -41,6 +52,7 @@ TEST_CASE("HostMemory: Simple mirror map", "[common]") { TEST_CASE("HostMemory: Simple unmap", "[common]") { HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); + REQUIRE(mem.BackingBasePointer() != nullptr); mem.Map(0x5000, 0x3000, 0x2000, PERMS, HEAP); volatile u8* const data = mem.VirtualBasePointer() + 0x5000; @@ -52,6 +64,7 @@ TEST_CASE("HostMemory: Simple unmap", "[common]") { TEST_CASE("HostMemory: Simple unmap and remap", "[common]") { HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); + REQUIRE(mem.BackingBasePointer() != nullptr); mem.Map(0x5000, 0x3000, 0x2000, PERMS, HEAP); volatile u8* const data = mem.VirtualBasePointer() + 0x5000; @@ -69,6 +82,7 @@ TEST_CASE("HostMemory: Simple unmap and remap", "[common]") { TEST_CASE("HostMemory: Nieche allocation", "[common]") { HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); + REQUIRE(mem.BackingBasePointer() != nullptr); mem.Map(0x0000, 0, 0x20000, PERMS, HEAP); mem.Unmap(0x0000, 0x4000, HEAP); mem.Map(0x1000, 0, 0x2000, PERMS, HEAP); @@ -78,6 +92,7 @@ TEST_CASE("HostMemory: Nieche allocation", "[common]") { TEST_CASE("HostMemory: Full unmap", "[common]") { HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); + REQUIRE(mem.BackingBasePointer() != nullptr); mem.Map(0x8000, 0, 0x4000, PERMS, HEAP); mem.Unmap(0x8000, 0x4000, HEAP); mem.Map(0x6000, 0, 0x16000, PERMS, HEAP); @@ -85,6 +100,7 @@ TEST_CASE("HostMemory: Full unmap", "[common]") { TEST_CASE("HostMemory: Right out of bounds unmap", "[common]") { HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); + REQUIRE(mem.BackingBasePointer() != nullptr); mem.Map(0x0000, 0, 0x4000, PERMS, HEAP); mem.Unmap(0x2000, 0x4000, HEAP); mem.Map(0x2000, 0x80000, 0x4000, PERMS, HEAP); @@ -92,6 +108,7 @@ TEST_CASE("HostMemory: Right out of bounds unmap", "[common]") { TEST_CASE("HostMemory: Left out of bounds unmap", "[common]") { HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); + mem.Map(0x8000, 0, 0x4000, PERMS, HEAP); mem.Unmap(0x6000, 0x4000, HEAP); mem.Map(0x8000, 0, 0x2000, PERMS, HEAP); @@ -99,6 +116,7 @@ TEST_CASE("HostMemory: Left out of bounds unmap", "[common]") { TEST_CASE("HostMemory: Multiple placeholder unmap", "[common]") { HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); + REQUIRE(mem.BackingBasePointer() != nullptr); mem.Map(0x0000, 0, 0x4000, PERMS, HEAP); mem.Map(0x4000, 0, 0x1b000, PERMS, HEAP); mem.Unmap(0x3000, 0x1c000, HEAP); @@ -107,6 +125,7 @@ TEST_CASE("HostMemory: Multiple placeholder unmap", "[common]") { TEST_CASE("HostMemory: Unmap between placeholders", "[common]") { HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); + REQUIRE(mem.BackingBasePointer() != nullptr); mem.Map(0x0000, 0, 0x4000, PERMS, HEAP); mem.Map(0x4000, 0, 0x4000, PERMS, HEAP); mem.Unmap(0x2000, 0x4000, HEAP); @@ -115,6 +134,7 @@ TEST_CASE("HostMemory: Unmap between placeholders", "[common]") { TEST_CASE("HostMemory: Unmap to origin", "[common]") { HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); + REQUIRE(mem.BackingBasePointer() != nullptr); mem.Map(0x4000, 0, 0x4000, PERMS, HEAP); mem.Map(0x8000, 0, 0x4000, PERMS, HEAP); mem.Unmap(0x4000, 0x4000, HEAP); @@ -124,6 +144,7 @@ TEST_CASE("HostMemory: Unmap to origin", "[common]") { TEST_CASE("HostMemory: Unmap to right", "[common]") { HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); + REQUIRE(mem.BackingBasePointer() != nullptr); mem.Map(0x4000, 0, 0x4000, PERMS, HEAP); mem.Map(0x8000, 0, 0x4000, PERMS, HEAP); mem.Unmap(0x8000, 0x4000, HEAP); @@ -132,6 +153,7 @@ TEST_CASE("HostMemory: Unmap to right", "[common]") { TEST_CASE("HostMemory: Partial right unmap check bindings", "[common]") { HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); + REQUIRE(mem.BackingBasePointer() != nullptr); mem.Map(0x4000, 0x10000, 0x4000, PERMS, HEAP); volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000; @@ -144,6 +166,7 @@ TEST_CASE("HostMemory: Partial right unmap check bindings", "[common]") { TEST_CASE("HostMemory: Partial left unmap check bindings", "[common]") { HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); + REQUIRE(mem.BackingBasePointer() != nullptr); mem.Map(0x4000, 0x10000, 0x4000, PERMS, HEAP); volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000; @@ -158,6 +181,7 @@ TEST_CASE("HostMemory: Partial left unmap check bindings", "[common]") { TEST_CASE("HostMemory: Partial middle unmap check bindings", "[common]") { HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); + REQUIRE(mem.BackingBasePointer() != nullptr); mem.Map(0x4000, 0x10000, 0x4000, PERMS, HEAP); volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000; @@ -172,6 +196,7 @@ TEST_CASE("HostMemory: Partial middle unmap check bindings", "[common]") { TEST_CASE("HostMemory: Partial sparse middle unmap and check bindings", "[common]") { HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); + REQUIRE(mem.BackingBasePointer() != nullptr); mem.Map(0x4000, 0x10000, 0x2000, PERMS, HEAP); mem.Map(0x6000, 0x20000, 0x2000, PERMS, HEAP); diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index be53b71393..5c02b5ed90 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -130,6 +130,8 @@ add_library(video_core STATIC renderer_vulkan/present/present_push_constants.h renderer_vulkan/present/smaa.cpp renderer_vulkan/present/smaa.h + renderer_vulkan/present/sgsr.cpp + renderer_vulkan/present/sgsr.h renderer_vulkan/present/util.cpp renderer_vulkan/present/util.h renderer_vulkan/present/window_adapt_pass.cpp @@ -332,7 +334,7 @@ if (YUZU_USE_EXTERNAL_FFMPEG) add_dependencies(video_core ffmpeg-build) endif() -target_include_directories(video_core PRIVATE ${FFmpeg_INCLUDE_DIR}) +target_include_directories(video_core PUBLIC ${FFmpeg_INCLUDE_DIR}) target_link_libraries(video_core PRIVATE ${FFmpeg_LIBRARIES}) target_link_options(video_core PRIVATE ${FFmpeg_LDFLAGS}) diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 60e0e8449b..740d99558e 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -754,7 +754,7 @@ void BufferCache

::BindHostIndexBuffer() { } } if constexpr (HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT) { - const u32 new_offset = offset + draw_state.index_buffer.first * draw_state.index_buffer.FormatSizeInBytes(); + const u32 new_offset = offset + draw_state.index_buffer.first * u32(draw_state.index_buffer.FormatSizeInBytes()); runtime.BindIndexBuffer(buffer, new_offset, size); } else { buffer.MarkUsage(offset, size); @@ -801,7 +801,7 @@ void BufferCache

::UpdateVertexBufferSlot(u32 index, const Binding& binding) { template void BufferCache

::BindHostVertexBuffers() { -#ifdef ANDROID +#ifdef __ANDROID__ const bool use_optimized_vertex_buffers = Settings::values.use_optimized_vertex_buffers.GetValue(); #else constexpr bool use_optimized_vertex_buffers = true; @@ -1252,8 +1252,7 @@ void BufferCache

::UpdateIndexBuffer() { const GPUVAddr gpu_addr_end = index_buffer_ref.EndAddress(); const std::optional device_addr = gpu_memory->GpuToCpuAddress(gpu_addr_begin); const u32 address_size = static_cast(gpu_addr_end - gpu_addr_begin); - const u32 draw_size = - (index_buffer_ref.count + index_buffer_ref.first) * index_buffer_ref.FormatSizeInBytes(); + const u32 draw_size = (index_buffer_ref.count + index_buffer_ref.first) * u32(index_buffer_ref.FormatSizeInBytes()); const u32 size = (std::min)(address_size, draw_size); if (size == 0 || !device_addr) { channel_state->index_buffer = NULL_BINDING; @@ -1635,6 +1634,17 @@ bool BufferCache

::SynchronizeBuffer(Buffer& buffer, DAddr device_addr, u32 si if (total_size_bytes == 0) { return true; } + if (Settings::values.enable_gpu_buffer_readback.GetValue()) { + u64 min_offset = (std::numeric_limits::max)(); + u64 max_offset = 0; + for (const auto& copy : upload_copies) { + min_offset = (std::min)(min_offset, copy.dst_offset); + max_offset = (std::max)(max_offset, copy.dst_offset + copy.size); + } + const DAddr sync_addr = buffer.CpuAddr() + min_offset; + const u64 sync_size = max_offset - min_offset; + DownloadBufferMemory(buffer, sync_addr, sync_size); + } const std::span copies_span(upload_copies.data(), upload_copies.size()); UploadMemory(buffer, total_size_bytes, largest_copy, copies_span); any_buffer_uploaded = true; @@ -1688,7 +1698,6 @@ void BufferCache

::MappedUploadMemory([[maybe_unused]] Buffer& buffer, u8* const src_pointer = staging_pointer.data() + copy.src_offset; const DAddr device_addr = buffer.CpuAddr() + copy.dst_offset; device_memory.ReadBlockUnsafe(device_addr, src_pointer, copy.size); - // Apply the staging offset copy.src_offset += upload_staging.offset; } @@ -1880,23 +1889,17 @@ Binding BufferCache

::StorageBufferBinding(GPUVAddr ssbo_addr, u32 cbuf_index, return NULL_BINDING; } - // xbzk: New size logic. Fixes MCI. - // If ever the * comment below prove wrong, the 'if' block may be removed. const auto size = [&]() { - const bool is_nvn_cbuf = cbuf_index == 0; - if (is_nvn_cbuf) { - // * The NVN driver buffer (index 0) is known to pack the SSBO address followed by its size. - const u64 next_qword = gpu_memory->Read(ssbo_addr + 8); - const u32 upper_32 = static_cast(next_qword >> 32); - // Hardware-based detection: GPU addresses have non-zero upper bits - if (upper_32 == 0) { - // This is a size field, not a GPU address - return static_cast(next_qword); // Return lower_32 - } + const u32 memory_layout_size = + static_cast(gpu_memory->GetMemoryLayoutSize(gpu_addr)); + const u64 next_qword = gpu_memory->Read(ssbo_addr + 8); + const u32 packed_size = static_cast(next_qword); + const bool next_qword_is_size = static_cast(next_qword >> 32) == 0 && + packed_size != 0 && + packed_size <= memory_layout_size; + if (next_qword_is_size) { + return packed_size; } - // Fall through: either not NVN cbuf (Doom Eternal & +), or NVN but ssbo_addr+8 is a GPU address (MCI) - const u32 memory_layout_size = static_cast(gpu_memory->GetMemoryLayoutSize(gpu_addr)); - // Cap at 8MB to prevent allocator overflow from misinterpreted addresses return (std::min)(memory_layout_size, static_cast(8_MiB)); }(); diff --git a/src/video_core/cdma_pusher.cpp b/src/video_core/cdma_pusher.cpp index 1f0f8b5a38..635ca769d8 100644 --- a/src/video_core/cdma_pusher.cpp +++ b/src/video_core/cdma_pusher.cpp @@ -20,8 +20,7 @@ namespace Tegra { CDmaPusher::CDmaPusher(Host1x::Host1x& host1x_, s32 id) - : host_processor(host1x_) - , host1x{host1x_} + : host1x{host1x_} , current_class{ChClassId(id)} { thread = std::jthread([this](std::stop_token stop_token) { @@ -99,7 +98,7 @@ void CDmaPusher::ExecuteCommand(u32 method, u32 arg) { switch (current_class) { case ChClassId::Control: LOG_TRACE(Service_NVDRV, "Class {} method {:#X} arg 0x{:X}", u32(current_class), method, arg); - host_processor.ProcessMethod(Host1x::Control::Method(method), arg); + host_processor.ProcessMethod(host1x, Host1x::Control::Method(method), arg); break; default: thi_regs.reg_array[method] = arg; diff --git a/src/video_core/control/channel_state.cpp b/src/video_core/control/channel_state.cpp index d07c7e2a83..c6f2c3c2ca 100644 --- a/src/video_core/control/channel_state.cpp +++ b/src/video_core/control/channel_state.cpp @@ -17,28 +17,36 @@ namespace Tegra::Control { -ChannelState::ChannelState(s32 bind_id_) : bind_id{bind_id_}, initialized{} {} +ChannelState::Payload::Payload(Core::System& system, MemoryManager& memory_manager, ChannelState& channel_state) + : maxwell_3d(memory_manager) + , fermi_2d(memory_manager) + , kepler_compute(memory_manager) + , maxwell_dma(memory_manager) + , kepler_memory(memory_manager) + , nv01_timer(memory_manager) + , dma_pusher(system, memory_manager, channel_state) +{} -void ChannelState::Init(Core::System& system, GPU& gpu, u64 program_id_) { +ChannelState::ChannelState(s32 bind_id_) + : bind_id{bind_id_} +{} + +void ChannelState::Init(Core::System& system, u64 program_id_) { ASSERT(memory_manager); program_id = program_id_; - dma_pusher.emplace(system, gpu, *memory_manager, *this); - maxwell_3d.emplace(system, *memory_manager); - fermi_2d.emplace(*memory_manager); - kepler_compute.emplace(system, *memory_manager); - maxwell_dma.emplace(system, *memory_manager); - kepler_memory.emplace(system, *memory_manager); + payload.emplace(system, *memory_manager, *this); initialized = true; } void ChannelState::BindRasterizer(VideoCore::RasterizerInterface* rasterizer) { - dma_pusher->BindRasterizer(rasterizer); + payload->dma_pusher.BindRasterizer(rasterizer); memory_manager->BindRasterizer(rasterizer); - maxwell_3d->BindRasterizer(rasterizer); - fermi_2d->BindRasterizer(rasterizer); - kepler_memory->BindRasterizer(rasterizer); - kepler_compute->BindRasterizer(rasterizer); - maxwell_dma->BindRasterizer(rasterizer); + payload->maxwell_3d.BindRasterizer(rasterizer); + payload->fermi_2d.BindRasterizer(rasterizer); + payload->kepler_memory.BindRasterizer(rasterizer); + payload->kepler_compute.BindRasterizer(rasterizer); + payload->maxwell_dma.BindRasterizer(rasterizer); + //payload->nv01_timer.BindRasterizer(rasterizer); } } // namespace Tegra::Control diff --git a/src/video_core/control/channel_state.h b/src/video_core/control/channel_state.h index 2984d2e09e..80811c1458 100644 --- a/src/video_core/control/channel_state.h +++ b/src/video_core/control/channel_state.h @@ -14,6 +14,7 @@ #include "video_core/engines/kepler_compute.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/engines/maxwell_dma.h" +#include "video_core/engines/nv01_timer.h" #include "video_core/dma_pusher.h" namespace Core { @@ -34,28 +35,33 @@ namespace Control { struct ChannelState { explicit ChannelState(s32 bind_id); - void Init(Core::System& system, GPU& gpu, u64 program_id); + void Init(Core::System& system, u64 program_id); void BindRasterizer(VideoCore::RasterizerInterface* rasterizer); - /// 3D engine - std::optional maxwell_3d; - /// 2D engine - std::optional fermi_2d; - /// Compute engine - std::optional kepler_compute; - /// DMA engine - std::optional maxwell_dma; - /// Inline memory engine - std::optional kepler_memory; - /// NV01 Timer - std::optional nv01_timer; - std::optional dma_pusher; - std::shared_ptr memory_manager; + struct Payload { + explicit Payload(Core::System& system, MemoryManager& memory_manager, ChannelState& channel_state); + + /// 3D engine + Engines::Maxwell3D maxwell_3d; + /// 2D engine + Engines::Fermi2D fermi_2d; + /// Compute engine + Engines::KeplerCompute kepler_compute; + /// DMA engine + Engines::MaxwellDMA maxwell_dma; + /// Inline memory engine + Engines::KeplerMemory kepler_memory; + /// NV01 Timer + Engines::Nv01Timer nv01_timer; + DmaPusher dma_pusher; + }; + std::optional payload; + MemoryManager* memory_manager = nullptr; s32 bind_id = -1; u64 program_id = 0; - bool initialized{}; + bool initialized = false; }; } // namespace Control diff --git a/src/video_core/control/channel_state_cache.cpp b/src/video_core/control/channel_state_cache.cpp index f8c6a762d2..bd794d939a 100644 --- a/src/video_core/control/channel_state_cache.cpp +++ b/src/video_core/control/channel_state_cache.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later @@ -6,8 +9,11 @@ namespace VideoCommon { ChannelInfo::ChannelInfo(Tegra::Control::ChannelState& channel_state) - : maxwell3d{*channel_state.maxwell_3d}, kepler_compute{*channel_state.kepler_compute}, - gpu_memory{*channel_state.memory_manager}, program_id{channel_state.program_id} {} + : maxwell3d{channel_state.payload->maxwell_3d} + , kepler_compute{channel_state.payload->kepler_compute} + , gpu_memory{*channel_state.memory_manager} + , program_id{channel_state.program_id} + {} template class VideoCommon::ChannelSetupCaches; diff --git a/src/video_core/control/channel_state_cache.inc b/src/video_core/control/channel_state_cache.inc index d882d8222f..5bb15f8907 100644 --- a/src/video_core/control/channel_state_cache.inc +++ b/src/video_core/control/channel_state_cache.inc @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later @@ -41,7 +44,7 @@ void ChannelSetupCaches

::CreateChannel(struct Tegra::Control::ChannelState& c AddressSpaceRef new_gpu_mem_ref{ .ref_count = 1, .storage_id = address_spaces.size(), - .gpu_memory = channel.memory_manager.get(), + .gpu_memory = channel.memory_manager, }; address_spaces.emplace(channel.memory_manager->GetID(), new_gpu_mem_ref); OnGPUASRegister(channel.memory_manager->GetID()); diff --git a/src/video_core/control/scheduler.cpp b/src/video_core/control/scheduler.cpp index bd3b8b860e..8af13e415c 100644 --- a/src/video_core/control/scheduler.cpp +++ b/src/video_core/control/scheduler.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: 2021 yuzu Emulator Project @@ -12,11 +12,8 @@ #include "video_core/gpu.h" namespace Tegra::Control { -Scheduler::Scheduler(GPU& gpu_) : gpu{gpu_} {} -Scheduler::~Scheduler() = default; - -void Scheduler::Push(s32 channel, CommandList&& entries) { +void Scheduler::Push(GPU& gpu, s32 channel, CommandList&& entries) { std::shared_ptr channel_state; { std::unique_lock lk(scheduling_guard); @@ -27,8 +24,8 @@ void Scheduler::Push(s32 channel, CommandList&& entries) { } // Process commands outside the lock to reduce contention. // Multiple channels can prepare their commands in parallel. - channel_state->dma_pusher->Push(std::move(entries)); - channel_state->dma_pusher->DispatchCalls(); + channel_state->payload->dma_pusher.Push(std::move(entries)); + channel_state->payload->dma_pusher.DispatchCalls(); } void Scheduler::DeclareChannel(std::shared_ptr new_channel) { diff --git a/src/video_core/control/scheduler.h b/src/video_core/control/scheduler.h index 9c2b9de32b..fb9c583386 100644 --- a/src/video_core/control/scheduler.h +++ b/src/video_core/control/scheduler.h @@ -22,17 +22,13 @@ struct ChannelState; class Scheduler { public: - explicit Scheduler(GPU& gpu_); - ~Scheduler(); - - void Push(s32 channel, CommandList&& entries); + void Push(GPU& gpu, s32 channel, CommandList&& entries); void DeclareChannel(std::shared_ptr new_channel); private: ankerl::unordered_dense::map> channels; std::mutex scheduling_guard; - GPU& gpu; }; } // namespace Control diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp index 3844a8e2f9..41bb43f0c1 100644 --- a/src/video_core/dma_pusher.cpp +++ b/src/video_core/dma_pusher.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project @@ -12,37 +12,32 @@ #include "video_core/guest_memory.h" #include "video_core/memory_manager.h" #include "video_core/rasterizer_interface.h" -#include "video_core/texture_cache/util.h" - -#ifdef _MSC_VER -#include -#endif namespace Tegra { constexpr u32 MacroRegistersStart = 0xE00; [[maybe_unused]] constexpr u32 ComputeInline = 0x6D; -DmaPusher::DmaPusher(Core::System& system_, GPU& gpu_, MemoryManager& memory_manager_, - Control::ChannelState& channel_state_) - : gpu{gpu_}, system{system_}, memory_manager{memory_manager_}, puller{gpu_, memory_manager_, - *this, channel_state_}, signal_sync{false}, synced{false} {} +DmaPusher::DmaPusher(Core::System& system_, MemoryManager& memory_manager_, Control::ChannelState& channel_state_) + : system{system_} + , memory_manager{memory_manager_} + , channel_state{channel_state_} + , signal_sync{false} + , synced{false} +{} DmaPusher::~DmaPusher() = default; void DmaPusher::DispatchCalls() { - dma_pushbuffer_subindex = 0; - dma_state.is_last_call = true; - while (system.IsPoweredOn()) { if (!Step()) { break; } } - gpu.FlushCommands(); - gpu.OnCommandListEnd(); + system.GPU().FlushCommands(); + system.GPU().OnCommandListEnd(); } bool DmaPusher::Step() { @@ -171,9 +166,9 @@ void DmaPusher::SetState(const CommandHeader& command_header) { dma_state.method_count = command_header.method_count; } -void DmaPusher::CallMethod(u32 argument) const { +void DmaPusher::CallMethod(u32 argument) { if (dma_state.method < non_puller_methods) { - puller.CallPullerMethod(Engines::Puller::MethodCall{ + puller.CallPullerMethod(*this, Engines::Puller::MethodCall{ dma_state.method, argument, dma_state.subchannel, @@ -184,27 +179,26 @@ void DmaPusher::CallMethod(u32 argument) const { if (!subchannel->execution_mask[dma_state.method]) { subchannel->method_sink.emplace_back(dma_state.method, argument); } else { - subchannel->ConsumeSink(); + subchannel->ConsumeSink(system); subchannel->current_dma_segment = dma_state.dma_get + dma_state.dma_word_offset; - subchannel->CallMethod(dma_state.method, argument, dma_state.is_last_call); + subchannel->CallMethod(system, dma_state.method, argument, dma_state.is_last_call); } } } -void DmaPusher::CallMultiMethod(const u32* base_start, u32 num_methods) const { +void DmaPusher::CallMultiMethod(const u32* base_start, u32 num_methods) { if (dma_state.method < non_puller_methods) { - puller.CallMultiMethod(dma_state.method, dma_state.subchannel, base_start, num_methods, dma_state.method_count); + puller.CallMultiMethod(*this, dma_state.method, dma_state.subchannel, base_start, num_methods, dma_state.method_count); } else { auto subchannel = subchannels[dma_state.subchannel]; - subchannel->ConsumeSink(); + subchannel->ConsumeSink(system); subchannel->current_dma_segment = dma_state.dma_get + dma_state.dma_word_offset; - subchannel->CallMultiMethod(dma_state.method, base_start, num_methods, dma_state.method_count); + subchannel->CallMultiMethod(system, dma_state.method, base_start, num_methods, dma_state.method_count); } } void DmaPusher::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) { rasterizer = rasterizer_; - puller.BindRasterizer(rasterizer); } } // namespace Tegra diff --git a/src/video_core/dma_pusher.h b/src/video_core/dma_pusher.h index f850513603..62af628511 100644 --- a/src/video_core/dma_pusher.h +++ b/src/video_core/dma_pusher.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project @@ -109,25 +109,21 @@ inline CommandHeader BuildCommandHeader(BufferMethods method, u32 arg_count, Sub struct CommandList final { CommandList() = default; explicit CommandList(std::size_t size) : command_lists(size) {} - explicit CommandList( - boost::container::small_vector&& prefetch_command_list_) + explicit CommandList(boost::container::small_vector&& prefetch_command_list_) : prefetch_command_list{std::move(prefetch_command_list_)} {} boost::container::small_vector command_lists; boost::container::small_vector prefetch_command_list; }; -/** - * The DmaPusher class implements DMA submission to FIFOs, providing an area of memory that the - * emulated app fills with commands and tells PFIFO to process. The pushbuffers are then assembled - * into a "command stream" consisting of 32-bit words that make up "commands". - * See https://envytools.readthedocs.io/en/latest/hw/fifo/dma-pusher.html#fifo-dma-pusher for - * details on this implementation. - */ +/// @brief The DmaPusher class implements DMA submission to FIFOs, providing an area of memory that the +/// emulated app fills with commands and tells PFIFO to process. The pushbuffers are then assembled +/// into a "command stream" consisting of 32-bit words that make up "commands". +/// See https://envytools.readthedocs.io/en/latest/hw/fifo/dma-pusher.html#fifo-dma-pusher for +/// details on this implementation. class DmaPusher final { public: - explicit DmaPusher(Core::System& system_, GPU& gpu_, MemoryManager& memory_manager_, - Control::ChannelState& channel_state_); + explicit DmaPusher(Core::System& system_, MemoryManager& memory_manager_, Control::ChannelState& channel_state_); ~DmaPusher(); void Push(CommandList&& entries) { @@ -136,8 +132,7 @@ public: void DispatchCalls(); - void BindSubchannel(Engines::EngineInterface* engine, u32 subchannel_id, - Engines::EngineTypes engine_type) { + void BindSubchannel(Engines::EngineInterface* engine, u32 subchannel_id, Engines::EngineTypes engine_type) { subchannels[subchannel_id] = engine; subchannel_type[subchannel_id] = engine_type; } @@ -152,11 +147,11 @@ private: void SetState(const CommandHeader& command_header); - void CallMethod(u32 argument) const; - void CallMultiMethod(const u32* base_start, u32 num_methods) const; + void CallMethod(u32 argument); + void CallMultiMethod(const u32* base_start, u32 num_methods); - Common::ScratchBuffer - command_headers; ///< Buffer for list of commands fetched at once +public: + Common::ScratchBuffer command_headers; ///< Buffer for list of commands fetched at once std::queue dma_pushbuffer; ///< Queue of command lists to be processed std::size_t dma_pushbuffer_subindex{}; ///< Index within a command list within the pushbuffer @@ -172,24 +167,24 @@ private: bool is_last_call; }; + Core::System& system; + MemoryManager& memory_manager; + Control::ChannelState& channel_state; + DmaState dma_state{}; - bool dma_increment_once{}; - - const bool ib_enable{true}; ///< IB mode enabled - std::array subchannels{}; std::array subchannel_type; - GPU& gpu; - Core::System& system; - MemoryManager& memory_manager; - mutable Engines::Puller puller; - - VideoCore::RasterizerInterface* rasterizer; - bool signal_sync; - bool synced; + Engines::Puller puller; std::mutex sync_mutex; std::condition_variable sync_cv; + + VideoCore::RasterizerInterface* rasterizer = nullptr; + + const bool ib_enable : 1 = true; ///< IB mode enabled + bool dma_increment_once : 1 = false; + bool signal_sync : 1 = false; + bool synced : 1 = false; }; } // namespace Tegra diff --git a/src/video_core/engines/draw_manager.cpp b/src/video_core/engines/draw_manager.cpp index 079c7bdc09..5891c25622 100644 --- a/src/video_core/engines/draw_manager.cpp +++ b/src/video_core/engines/draw_manager.cpp @@ -4,6 +4,8 @@ // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include + #include "common/settings.h" #include "video_core/dirty_flags.h" #include "video_core/engines/maxwell_3d.h" @@ -130,6 +132,44 @@ void Maxwell3D::DrawManager::SetInlineIndexBuffer(Maxwell3D& maxwell3d, u32 inde draw_state.draw_mode = DrawMode::InlineIndex; } +void Maxwell3D::DrawManager::SetInlineIndexBuffer(Maxwell3D& maxwell3d, u32 method, + const u32* base_start, u32 amount) { + auto& index_buffer = draw_state.inline_index_draw_indexes; + switch (method) { + case MAXWELL3D_REG_INDEX(draw_inline_index): { + const auto* const bytes = reinterpret_cast(base_start); + index_buffer.insert(index_buffer.end(), bytes, bytes + size_t(amount) * sizeof(u32)); + break; + } + case MAXWELL3D_REG_INDEX(inline_index_2x16.even): { + const size_t offset = index_buffer.size(); + index_buffer.resize(offset + size_t(amount) * 2 * sizeof(u32)); + u8* dst = index_buffer.data() + offset; + for (u32 i = 0; i < amount; ++i) { + const u32 word = base_start[i]; + const u32 indexes[2]{word & 0xFFFF, word >> 16}; + std::memcpy(dst, indexes, sizeof(indexes)); + dst += sizeof(indexes); + } + break; + } + case MAXWELL3D_REG_INDEX(inline_index_4x8.index0): { + const size_t offset = index_buffer.size(); + index_buffer.resize(offset + size_t(amount) * 4 * sizeof(u32)); + u8* dst = index_buffer.data() + offset; + for (u32 i = 0; i < amount; ++i) { + const u32 word = base_start[i]; + const u32 indexes[4]{word & 0xFF, (word >> 8) & 0xFF, (word >> 16) & 0xFF, + word >> 24}; + std::memcpy(dst, indexes, sizeof(indexes)); + dst += sizeof(indexes); + } + break; + } + } + draw_state.draw_mode = DrawMode::InlineIndex; +} + void Maxwell3D::DrawManager::DrawBegin(Maxwell3D& maxwell3d) { auto reset_instance_count = maxwell3d.regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::First; auto increment_instance_count = maxwell3d.regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Subsequent; diff --git a/src/video_core/engines/engine_interface.h b/src/video_core/engines/engine_interface.h index bf3bd66aca..83dbf13059 100644 --- a/src/video_core/engines/engine_interface.h +++ b/src/video_core/engines/engine_interface.h @@ -12,6 +12,10 @@ #include "common/common_types.h" +namespace Core { +class System; +} + namespace Tegra::Engines { enum class EngineTypes : u32 { @@ -28,28 +32,25 @@ public: virtual ~EngineInterface() = default; /// Write the value to the register identified by method. - virtual void CallMethod(u32 method, u32 method_argument, bool is_last_call) = 0; + virtual void CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) = 0; /// Write multiple values to the register identified by method. - virtual void CallMultiMethod(u32 method, const u32* base_start, u32 amount, - u32 methods_pending) = 0; + virtual void CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) = 0; - void ConsumeSink() { - if (method_sink.empty()) { - return; + void ConsumeSink(Core::System& system) { + if (!method_sink.empty()) { + ConsumeSinkImpl(system); } - ConsumeSinkImpl(); } std::bitset<(std::numeric_limits::max)()> execution_mask{}; std::vector> method_sink{}; - bool current_dirty{}; GPUVAddr current_dma_segment; - + bool current_dirty{}; protected: - virtual void ConsumeSinkImpl() { + virtual void ConsumeSinkImpl(Core::System& system) { for (auto [method, value] : method_sink) { - CallMethod(method, value, true); + CallMethod(system, method, value, true); } method_sink.clear(); } diff --git a/src/video_core/engines/fermi_2d.cpp b/src/video_core/engines/fermi_2d.cpp index b442c5cc76..8b02c91e03 100644 --- a/src/video_core/engines/fermi_2d.cpp +++ b/src/video_core/engines/fermi_2d.cpp @@ -36,9 +36,8 @@ void Fermi2D::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) { rasterizer = rasterizer_; } -void Fermi2D::CallMethod(u32 method, u32 method_argument, bool is_last_call) { - ASSERT_MSG(method < Regs::NUM_REGS, - "Invalid Fermi2D register, increase the size of the Regs structure"); +void Fermi2D::CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) { + ASSERT_MSG(method < Regs::NUM_REGS, "Invalid Fermi2D register, increase the size of the Regs structure"); regs.reg_array[method] = method_argument; if (method == FERMI2D_REG_INDEX(pixels_from_memory.src_y0) + 1) { @@ -46,13 +45,13 @@ void Fermi2D::CallMethod(u32 method, u32 method_argument, bool is_last_call) { } } -void Fermi2D::CallMultiMethod(u32 method, const u32* base_start, u32 amount, u32 methods_pending) { +void Fermi2D::CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) { for (u32 i = 0; i < amount; ++i) { - CallMethod(method, base_start[i], methods_pending - i <= 1); + CallMethod(system, method, base_start[i], methods_pending - i <= 1); } } -void Fermi2D::ConsumeSinkImpl() { +void Fermi2D::ConsumeSinkImpl(Core::System& system) { for (auto [method, value] : method_sink) { regs.reg_array[method] = value; } @@ -60,8 +59,7 @@ void Fermi2D::ConsumeSinkImpl() { } void Fermi2D::Blit() { - LOG_DEBUG(HW_GPU, "called. source address=0x{:x}, destination address=0x{:x}", - regs.src.Address(), regs.dst.Address()); + LOG_DEBUG(HW_GPU, "called. source address=0x{:x}, destination address=0x{:x}", regs.src.Address(), regs.dst.Address()); UNIMPLEMENTED_IF_MSG(regs.operation != Operation::SrcCopy, "Operation is not copy"); UNIMPLEMENTED_IF_MSG(regs.src.layer != 0, "Source layer is not zero"); diff --git a/src/video_core/engines/fermi_2d.h b/src/video_core/engines/fermi_2d.h index 705b323e15..7fddf9fdc1 100644 --- a/src/video_core/engines/fermi_2d.h +++ b/src/video_core/engines/fermi_2d.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -44,11 +47,10 @@ public: void BindRasterizer(VideoCore::RasterizerInterface* rasterizer); /// Write the value to the register identified by method. - void CallMethod(u32 method, u32 method_argument, bool is_last_call) override; + void CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) override; /// Write multiple values to the register identified by method. - void CallMultiMethod(u32 method, const u32* base_start, u32 amount, - u32 methods_pending) override; + void CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) override; enum class Origin : u32 { Center = 0, @@ -311,7 +313,7 @@ private: /// registers. void Blit(); - void ConsumeSinkImpl() override; + void ConsumeSinkImpl(Core::System& system) override; }; #define ASSERT_REG_POSITION(field_name, position) \ diff --git a/src/video_core/engines/kepler_compute.cpp b/src/video_core/engines/kepler_compute.cpp index 7b4efeb1e0..1ef6e0fc0d 100644 --- a/src/video_core/engines/kepler_compute.cpp +++ b/src/video_core/engines/kepler_compute.cpp @@ -16,8 +16,10 @@ namespace Tegra::Engines { -KeplerCompute::KeplerCompute(Core::System& system_, MemoryManager& memory_manager_) - : system{system_}, memory_manager{memory_manager_}, upload_state{memory_manager, regs.upload} { +KeplerCompute::KeplerCompute(MemoryManager& memory_manager_) + : memory_manager{memory_manager_} + , upload_state{memory_manager, regs.upload} +{ execution_mask.reset(); execution_mask[KEPLER_COMPUTE_REG_INDEX(exec_upload)] = true; execution_mask[KEPLER_COMPUTE_REG_INDEX(data_upload)] = true; @@ -31,16 +33,15 @@ void KeplerCompute::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) upload_state.BindRasterizer(rasterizer); } -void KeplerCompute::ConsumeSinkImpl() { +void KeplerCompute::ConsumeSinkImpl(Core::System& system) { for (auto [method, value] : method_sink) { regs.reg_array[method] = value; } method_sink.clear(); } -void KeplerCompute::CallMethod(u32 method, u32 method_argument, bool is_last_call) { - ASSERT_MSG(method < Regs::NUM_REGS, - "Invalid KeplerCompute register, increase the size of the Regs structure"); +void KeplerCompute::CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) { + ASSERT_MSG(method < Regs::NUM_REGS, "Invalid KeplerCompute register, increase the size of the Regs structure"); regs.reg_array[method] = method_argument; @@ -78,8 +79,7 @@ void KeplerCompute::CallMethod(u32 method, u32 method_argument, bool is_last_cal } } -void KeplerCompute::CallMultiMethod(u32 method, const u32* base_start, u32 amount, - u32 methods_pending) { +void KeplerCompute::CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) { switch (method) { case KEPLER_COMPUTE_REG_INDEX(data_upload): upload_address = current_dma_segment; @@ -87,7 +87,7 @@ void KeplerCompute::CallMultiMethod(u32 method, const u32* base_start, u32 amoun return; default: for (u32 i = 0; i < amount; i++) { - CallMethod(method, base_start[i], methods_pending - i <= 1); + CallMethod(system, method, base_start[i], methods_pending - i <= 1); } break; } diff --git a/src/video_core/engines/kepler_compute.h b/src/video_core/engines/kepler_compute.h index 735e05fb43..4f7242f804 100644 --- a/src/video_core/engines/kepler_compute.h +++ b/src/video_core/engines/kepler_compute.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -42,7 +45,7 @@ namespace Tegra::Engines { class KeplerCompute final : public EngineInterface { public: - explicit KeplerCompute(Core::System& system, MemoryManager& memory_manager); + explicit KeplerCompute(MemoryManager& memory_manager); ~KeplerCompute(); /// Binds a rasterizer to this engine. @@ -199,11 +202,10 @@ public: "KeplerCompute LaunchParams has wrong size"); /// Write the value to the register identified by method. - void CallMethod(u32 method, u32 method_argument, bool is_last_call) override; + void CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) override; /// Write multiple values to the register identified by method. - void CallMultiMethod(u32 method, const u32* base_start, u32 amount, - u32 methods_pending) override; + void CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) override; std::optional GetIndirectComputeAddress() const { return indirect_compute; @@ -212,7 +214,7 @@ public: private: void ProcessLaunch(); - void ConsumeSinkImpl() override; + void ConsumeSinkImpl(Core::System& system) override; /// Retrieves information about a specific TIC entry from the TIC buffer. Texture::TICEntry GetTICEntry(u32 tic_index) const; @@ -220,7 +222,6 @@ private: /// Retrieves information about a specific TSC entry from the TSC buffer. Texture::TSCEntry GetTSCEntry(u32 tsc_index) const; - Core::System& system; MemoryManager& memory_manager; VideoCore::RasterizerInterface* rasterizer = nullptr; Upload::State upload_state; diff --git a/src/video_core/engines/kepler_memory.cpp b/src/video_core/engines/kepler_memory.cpp index 5d4c4720d3..3af5f91271 100644 --- a/src/video_core/engines/kepler_memory.cpp +++ b/src/video_core/engines/kepler_memory.cpp @@ -14,8 +14,9 @@ namespace Tegra::Engines { -KeplerMemory::KeplerMemory(Core::System& system_, MemoryManager& memory_manager) - : system{system_}, upload_state{memory_manager, regs.upload} {} +KeplerMemory::KeplerMemory(MemoryManager& memory_manager) + : upload_state{memory_manager, regs.upload} +{} KeplerMemory::~KeplerMemory() = default; @@ -27,16 +28,15 @@ void KeplerMemory::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) { execution_mask[KEPLERMEMORY_REG_INDEX(data)] = true; } -void KeplerMemory::ConsumeSinkImpl() { +void KeplerMemory::ConsumeSinkImpl(Core::System& system) { for (auto [method, value] : method_sink) { regs.reg_array[method] = value; } method_sink.clear(); } -void KeplerMemory::CallMethod(u32 method, u32 method_argument, bool is_last_call) { - ASSERT_MSG(method < Regs::NUM_REGS, - "Invalid KeplerMemory register, increase the size of the Regs structure"); +void KeplerMemory::CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) { + ASSERT_MSG(method < Regs::NUM_REGS, "Invalid KeplerMemory register, increase the size of the Regs structure"); regs.reg_array[method] = method_argument; @@ -52,15 +52,14 @@ void KeplerMemory::CallMethod(u32 method, u32 method_argument, bool is_last_call } } -void KeplerMemory::CallMultiMethod(u32 method, const u32* base_start, u32 amount, - u32 methods_pending) { +void KeplerMemory::CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) { switch (method) { case KEPLERMEMORY_REG_INDEX(data): upload_state.ProcessData(base_start, amount); return; default: for (u32 i = 0; i < amount; i++) { - CallMethod(method, base_start[i], methods_pending - i <= 1); + CallMethod(system, method, base_start[i], methods_pending - i <= 1); } break; } diff --git a/src/video_core/engines/kepler_memory.h b/src/video_core/engines/kepler_memory.h index fb1eecbba9..aa0952d17a 100644 --- a/src/video_core/engines/kepler_memory.h +++ b/src/video_core/engines/kepler_memory.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -36,18 +39,17 @@ namespace Tegra::Engines { class KeplerMemory final : public EngineInterface { public: - explicit KeplerMemory(Core::System& system_, MemoryManager& memory_manager); + explicit KeplerMemory(MemoryManager& memory_manager); ~KeplerMemory() override; /// Binds a rasterizer to this engine. void BindRasterizer(VideoCore::RasterizerInterface* rasterizer); /// Write the value to the register identified by method. - void CallMethod(u32 method, u32 method_argument, bool is_last_call) override; + void CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) override; /// Write multiple values to the register identified by method. - void CallMultiMethod(u32 method, const u32* base_start, u32 amount, - u32 methods_pending) override; + void CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) override; struct Regs { static constexpr size_t NUM_REGS = 0x7F; @@ -73,9 +75,7 @@ public: } regs{}; private: - void ConsumeSinkImpl() override; - - Core::System& system; + void ConsumeSinkImpl(Core::System& system) override; Upload::State upload_state; }; diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 9aaa99f7ff..e7da9d9138 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -25,9 +25,8 @@ namespace Tegra::Engines { /// First register id that is actually a Macro call. constexpr u32 MacroRegistersStart = 0xE00; -Maxwell3D::Maxwell3D(Core::System& system_, MemoryManager& memory_manager_) +Maxwell3D::Maxwell3D(MemoryManager& memory_manager_) : draw_manager() - , system{system_} , memory_manager{memory_manager_} #ifdef ARCHITECTURE_x86_64 , macro_engine(bool(Settings::values.disable_macro_jit)) @@ -201,28 +200,23 @@ bool Maxwell3D::IsMethodExecutable(u32 method) { } } -void Maxwell3D::ProcessMacro(u32 method, const u32* base_start, u32 amount, bool is_last_call) { +void Maxwell3D::ProcessMacro(Core::System& system, u32 method, const u32* base_start, u32 amount, bool is_last_call) { if (executing_macro == 0) { // A macro call must begin by writing the macro method's register, not its argument. - ASSERT_MSG((method % 2) == 0, - "Can't start macro execution by writing to the ARGS register"); + ASSERT((method % 2) == 0 && "Can't start macro execution by writing to the ARGS register"); executing_macro = method; } macro_params.insert(macro_params.end(), base_start, base_start + amount); - for (size_t i = 0; i < amount; i++) { - macro_addresses.push_back(current_dma_segment + i * sizeof(u32)); - } macro_segments.emplace_back(current_dma_segment, amount); current_macro_dirty |= current_dirty; current_dirty = false; // Call the macro when there are no more parameters in the command buffer if (is_last_call) { - ConsumeSink(); - CallMacroMethod(executing_macro, macro_params); + ConsumeSink(system); + CallMacroMethod(system, executing_macro, macro_params); macro_params.clear(); - macro_addresses.clear(); macro_segments.clear(); current_macro_dirty = false; } @@ -270,35 +264,24 @@ u32 Maxwell3D::GetMaxCurrentVertices() { size_t Maxwell3D::EstimateIndexBufferSize() { GPUVAddr start_address = regs.index_buffer.StartAddress(); GPUVAddr end_address = regs.index_buffer.EndAddress(); - static constexpr std::array max_sizes = {(std::numeric_limits::max)(), - (std::numeric_limits::max)(), - (std::numeric_limits::max)()}; - const size_t byte_size = regs.index_buffer.FormatSizeInBytes(); - const size_t log2_byte_size = Common::Log2Ceil64(byte_size); - const size_t cap{GetMaxCurrentVertices() * 4 * byte_size}; - const size_t lower_cap = - std::min(static_cast(end_address - start_address), cap); - return std::min( - memory_manager.GetMemoryLayoutSize(start_address, byte_size * max_sizes[log2_byte_size]) / - byte_size, - lower_cap); + auto const byte_size = regs.index_buffer.FormatSizeInBytes(); + auto const max_size = 1ull << (byte_size * CHAR_BIT); + auto const upper_cap = GetMaxCurrentVertices() * 4 * byte_size; + auto const lower_cap = std::min(size_t(end_address - start_address), upper_cap); + return std::min(memory_manager.GetMemoryLayoutSize(start_address, byte_size * max_size) / byte_size, lower_cap); } u32 Maxwell3D::ProcessShadowRam(u32 method, u32 argument) { // Keep track of the register value in shadow_state when requested. - const auto control = shadow_state.shadow_ram_control; - if (control == Regs::ShadowRamControl::Track || - control == Regs::ShadowRamControl::TrackWithFilter) { - shadow_state.reg_array[method] = argument; - return argument; - } - if (control == Regs::ShadowRamControl::Replay) { + auto const c = shadow_state.shadow_ram_control; + if (c == Regs::ShadowRamControl::Track || c == Regs::ShadowRamControl::TrackWithFilter) + return shadow_state.reg_array[method] = argument; + else if (c == Regs::ShadowRamControl::Replay) return shadow_state.reg_array[method]; - } return argument; } -void Maxwell3D::ConsumeSinkImpl() { +void Maxwell3D::ConsumeSinkImpl(Core::System& system) { const auto control = shadow_state.shadow_ram_control; if (control == Regs::ShadowRamControl::Track || control == Regs::ShadowRamControl::TrackWithFilter) { for (auto [method, value] : method_sink) { @@ -317,10 +300,8 @@ void Maxwell3D::ConsumeSinkImpl() { void Maxwell3D::ProcessDirtyRegisters(u32 method, u32 argument) { regs.reg_array[method] = argument; - - for (const auto& table : dirty.tables) { + for (auto const& table : dirty.tables) dirty.flags[table[method]] = true; - } } void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argument, bool is_last_call) { @@ -391,7 +372,7 @@ void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argume } } -void Maxwell3D::CallMacroMethod(u32 method, const std::vector& parameters) { +void Maxwell3D::CallMacroMethod(Core::System& system, u32 method, const std::vector& parameters) { // Reset the current macro. executing_macro = 0; @@ -400,11 +381,11 @@ void Maxwell3D::CallMacroMethod(u32 method, const std::vector& parameters) ((method - MacroRegistersStart) >> 1) % static_cast(macro_positions.size()); // Execute the current macro. - macro_engine.Execute(*this, macro_positions[entry], parameters); + macro_engine.Execute(system, *this, macro_positions[entry], parameters); draw_manager.DrawDeferred(*this); } -void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) { +void Maxwell3D::CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) { // It is an error to write to a register other than the current macro's ARG register before // it has finished execution. if (executing_macro != 0) { @@ -414,7 +395,7 @@ void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) { // Methods after 0xE00 are special, they're actually triggers for some microcode that was // uploaded to the GPU during initialization. if (method >= MacroRegistersStart) { - ProcessMacro(method, &method_argument, 1, is_last_call); + ProcessMacro(system, method, &method_argument, 1, is_last_call); return; } @@ -424,12 +405,11 @@ void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) { ProcessMethodCall(method, argument, method_argument, is_last_call); } -void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount, - u32 methods_pending) { +void Maxwell3D::CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) { // Methods after 0xE00 are special, they're actually triggers for some microcode that was // uploaded to the GPU during initialization. if (method >= MacroRegistersStart) { - ProcessMacro(method, base_start, amount, amount == methods_pending); + ProcessMacro(system, method, base_start, amount, amount == methods_pending); return; } switch (method) { @@ -456,9 +436,17 @@ void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount, upload_state.ProcessData(base_start, amount); return; } + case MAXWELL3D_REG_INDEX(draw_inline_index): + case MAXWELL3D_REG_INDEX(inline_index_2x16.even): + case MAXWELL3D_REG_INDEX(inline_index_4x8.index0): + if (shadow_state.shadow_ram_control != Regs::ShadowRamControl::Replay) { + ProcessInlineIndexMultiData(method, base_start, amount); + break; + } + [[fallthrough]]; default: for (u32 i = 0; i < amount; i++) { - CallMethod(method, base_start[i], methods_pending - i <= 1); + CallMethod(system, method, base_start[i], methods_pending - i <= 1); } break; } @@ -480,7 +468,7 @@ void Maxwell3D::ProcessFirmwareCall4() { regs.shadow_scratch[0] = 1; } -void Maxwell3D::StampQueryResult(u64 payload, bool long_query) { +void Maxwell3D::StampQueryResult(Core::System& system, u64 payload, bool long_query) { const GPUVAddr sequence_address{regs.report_semaphore.Address()}; if (long_query) { memory_manager.Write(sequence_address + sizeof(u64), system.GPU().GetTicks()); @@ -642,6 +630,15 @@ void Maxwell3D::ProcessCBData(u32 value) { ProcessCBMultiData(&value, 1); } +void Maxwell3D::ProcessInlineIndexMultiData(u32 method, const u32* start_base, u32 amount) { + if (amount == 0) { + return; + } + const u32 argument = ProcessShadowRam(method, start_base[amount - 1]); + ProcessDirtyRegisters(method, argument); + draw_manager.SetInlineIndexBuffer(*this, method, start_base, amount); +} + Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const { const GPUVAddr tic_address_gpu{regs.tex_header.Address() + tic_index * sizeof(Texture::TICEntry)}; diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index 3ac79e0eb8..dd16438d55 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h @@ -55,7 +55,7 @@ namespace Tegra::Engines { class Maxwell3D final : public EngineInterface { public: - explicit Maxwell3D(Core::System& system, MemoryManager& memory_manager); + explicit Maxwell3D(MemoryManager& memory_manager); ~Maxwell3D(); /// Binds a rasterizer to this engine. @@ -2215,7 +2215,7 @@ public: u32 first; u32 count; - unsigned FormatSizeInBytes() const { + size_t FormatSizeInBytes() const { switch (format) { case IndexFormat::UnsignedByte: return 1; @@ -2224,7 +2224,7 @@ public: case IndexFormat::UnsignedInt: return 4; } - ASSERT(false); + UNREACHABLE(); return 1; } @@ -3077,6 +3077,7 @@ public: void DrawArrayIndirect(Maxwell3D& maxwell3d, Maxwell3D::Regs::PrimitiveTopology topology); void DrawIndexedIndirect(Maxwell3D& maxwell3d, Maxwell3D::Regs::PrimitiveTopology topology, u32 index_first, u32 index_count); void SetInlineIndexBuffer(Maxwell3D& maxwell3d, u32 index); + void SetInlineIndexBuffer(Maxwell3D& maxwell3d, u32 method, const u32* base_start, u32 amount); void DrawBegin(Maxwell3D& maxwell3d); void DrawEnd(Maxwell3D& maxwell3d, u32 instance_count = 1, bool force_draw = false); void DrawIndexSmall(Maxwell3D& maxwell3d, u32 argument); @@ -3129,11 +3130,10 @@ public: u32 GetRegisterValue(u32 method) const; /// Write the value to the register identified by method. - void CallMethod(u32 method, u32 method_argument, bool is_last_call) override; + void CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) override; /// Write multiple values to the register identified by method. - void CallMultiMethod(u32 method, const u32* base_start, u32 amount, - u32 methods_pending) override; + void CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) override; bool ShouldExecute() const { return execute_on; @@ -3148,9 +3148,9 @@ public: } struct DirtyState { - using Flags = std::bitset<(std::numeric_limits::max)()>; + using Flags = std::bitset<(std::numeric_limits::max)() + 1>; using Table = std::array; - using Tables = std::array; + using Tables = std::array, 2>; Flags flags; Tables tables{}; @@ -3159,7 +3159,14 @@ public: DrawManager draw_manager; GPUVAddr GetMacroAddress(size_t index) const { - return macro_addresses[index]; + size_t base = 0; + for (const auto& [addr, count] : macro_segments) { + if (index < base + count) { + return addr + (index - base) * sizeof(u32); + } + base += count; + } + return 0; } void RefreshParameters() { @@ -3187,16 +3194,18 @@ public: void ProcessCBData(u32 value); void ProcessCBMultiData(const u32* start_base, u32 amount); + void ProcessInlineIndexMultiData(u32 method, const u32* start_base, u32 amount); + private: void InitializeRegisterDefaults(); - void ProcessMacro(u32 method, const u32* base_start, u32 amount, bool is_last_call); + void ProcessMacro(Core::System& system, u32 method, const u32* base_start, u32 amount, bool is_last_call); u32 ProcessShadowRam(u32 method, u32 argument); void ProcessDirtyRegisters(u32 method, u32 argument); - void ConsumeSinkImpl() override; + void ConsumeSinkImpl(Core::System& system) override; void ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argument, bool is_last_call); @@ -3212,7 +3221,7 @@ private: * @param method Method to call * @param parameters Arguments to the method call */ - void CallMacroMethod(u32 method, const std::vector& parameters); + void CallMacroMethod(Core::System& system, u32 method, const std::vector& parameters); /// Handles writes to the macro uploading register. void ProcessMacroUpload(u32 data); @@ -3227,7 +3236,7 @@ private: void ProcessQueryGet(); /// Writes the query result accordingly. - void StampQueryResult(u64 payload, bool long_query); + void StampQueryResult(Core::System& system, u64 payload, bool long_query); /// Handles conditional rendering. void ProcessQueryCondition(); @@ -3242,7 +3251,6 @@ private: bool IsMethodExecutable(u32 method); - Core::System& system; MemoryManager& memory_manager; VideoCore::RasterizerInterface* rasterizer = nullptr; @@ -3263,7 +3271,6 @@ private: bool execute_on{true}; std::vector> macro_segments; - std::vector macro_addresses; bool current_macro_dirty{}; }; diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp index 089d118a09..a4090bc2ce 100644 --- a/src/video_core/engines/maxwell_dma.cpp +++ b/src/video_core/engines/maxwell_dma.cpp @@ -21,8 +21,9 @@ namespace Tegra::Engines { using namespace Texture; -MaxwellDMA::MaxwellDMA(Core::System& system_, MemoryManager& memory_manager_) - : system{system_}, memory_manager{memory_manager_} { +MaxwellDMA::MaxwellDMA(MemoryManager& memory_manager_) + : memory_manager{memory_manager_} +{ execution_mask.reset(); execution_mask[offsetof(Regs, launch_dma) / sizeof(u32)] = true; } @@ -33,14 +34,14 @@ void MaxwellDMA::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) { rasterizer = rasterizer_; } -void MaxwellDMA::ConsumeSinkImpl() { +void MaxwellDMA::ConsumeSinkImpl(Core::System& system) { for (auto [method, value] : method_sink) { regs.reg_array[method] = value; } method_sink.clear(); } -void MaxwellDMA::CallMethod(u32 method, u32 method_argument, bool is_last_call) { +void MaxwellDMA::CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) { ASSERT_MSG(method < NUM_REGS, "Invalid MaxwellDMA register"); regs.reg_array[method] = method_argument; @@ -50,16 +51,14 @@ void MaxwellDMA::CallMethod(u32 method, u32 method_argument, bool is_last_call) } } -void MaxwellDMA::CallMultiMethod(u32 method, const u32* base_start, u32 amount, - u32 methods_pending) { +void MaxwellDMA::CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) { for (u32 i = 0; i < amount; ++i) { - CallMethod(method, base_start[i], methods_pending - i <= 1); + CallMethod(system, method, base_start[i], methods_pending - i <= 1); } } void MaxwellDMA::Launch() { - LOG_TRACE(Render_OpenGL, "DMA copy 0x{:x} -> 0x{:x}", static_cast(regs.offset_in), - static_cast(regs.offset_out)); + LOG_TRACE(Render_OpenGL, "DMA copy 0x{:x} -> 0x{:x}", static_cast(regs.offset_in), GPUVAddr(regs.offset_out)); // TODO(Subv): Perform more research and implement all features of this engine. const LaunchDMA& launch = regs.launch_dma; diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h index 99341e431c..9cf2813d31 100644 --- a/src/video_core/engines/maxwell_dma.h +++ b/src/video_core/engines/maxwell_dma.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -238,15 +241,14 @@ public: void BindRasterizer(VideoCore::RasterizerInterface* rasterizer); - explicit MaxwellDMA(Core::System& system_, MemoryManager& memory_manager_); + explicit MaxwellDMA(MemoryManager& memory_manager_); ~MaxwellDMA() override; /// Write the value to the register identified by method. - void CallMethod(u32 method, u32 method_argument, bool is_last_call) override; + void CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) override; /// Write multiple values to the register identified by method. - void CallMultiMethod(u32 method, const u32* base_start, u32 amount, - u32 methods_pending) override; + void CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) override; private: /// Performs the copy from the source buffer to the destination buffer as configured in the @@ -261,9 +263,7 @@ private: void ReleaseSemaphore(); - void ConsumeSinkImpl() override; - - Core::System& system; + void ConsumeSinkImpl(Core::System& system) override; MemoryManager& memory_manager; VideoCore::RasterizerInterface* rasterizer = nullptr; diff --git a/src/video_core/engines/nv01_timer.h b/src/video_core/engines/nv01_timer.h index add9886f41..37c6668144 100644 --- a/src/video_core/engines/nv01_timer.h +++ b/src/video_core/engines/nv01_timer.h @@ -26,18 +26,16 @@ class MemoryManager; namespace Tegra::Engines { class Nv01Timer final : public EngineInterface { public: - explicit Nv01Timer(Core::System& system_, MemoryManager& memory_manager) - : system{system_} - {} - ~Nv01Timer() override; + explicit Nv01Timer(MemoryManager& memory_manager) noexcept {} + ~Nv01Timer() noexcept override {} /// Write the value to the register identified by method. - void CallMethod(u32 method, u32 method_argument, bool is_last_call) override { + void CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) override { LOG_DEBUG(HW_GPU, "method={}, argument={}, is_last_call={}", method, method_argument, is_last_call); } /// Write multiple values to the register identified by method. - void CallMultiMethod(u32 method, const u32* base_start, u32 amount, u32 methods_pending) override { + void CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) override { LOG_DEBUG(HW_GPU, "method={}, base_start={}, amount={}, pending={}", method, fmt::ptr(base_start), amount, methods_pending); } @@ -46,7 +44,6 @@ public: INSERT_PADDING_BYTES_NOINIT(0x48); } regs{}; private: - void ConsumeSinkImpl() override {} - Core::System& system; + void ConsumeSinkImpl(Core::System& system) override {} }; } diff --git a/src/video_core/engines/puller.cpp b/src/video_core/engines/puller.cpp index 4030f93d49..5816b5901a 100644 --- a/src/video_core/engines/puller.cpp +++ b/src/video_core/engines/puller.cpp @@ -22,37 +22,29 @@ namespace Tegra::Engines { -Puller::Puller(GPU& gpu_, MemoryManager& memory_manager_, DmaPusher& dma_pusher_, - Control::ChannelState& channel_state_) - : gpu{gpu_}, memory_manager{memory_manager_}, dma_pusher{dma_pusher_}, channel_state{ - channel_state_} {} - -Puller::~Puller() = default; - -void Puller::ProcessBindMethod(const MethodCall& method_call) { +void Puller::ProcessBindMethod(DmaPusher& dma_pusher, const MethodCall& method_call) { // Bind the current subchannel to the desired engine id. - LOG_DEBUG(HW_GPU, "Binding subchannel {} to engine {}", method_call.subchannel, - method_call.argument); + LOG_DEBUG(HW_GPU, "Binding subchannel {} to engine {}", method_call.subchannel, method_call.argument); const auto engine_id = static_cast(method_call.argument); bound_engines[method_call.subchannel] = engine_id; switch (engine_id) { case EngineID::FERMI_TWOD_A: - dma_pusher.BindSubchannel(&*channel_state.fermi_2d, method_call.subchannel, EngineTypes::Fermi2D); + dma_pusher.BindSubchannel(&dma_pusher.channel_state.payload->fermi_2d, method_call.subchannel, EngineTypes::Fermi2D); break; case EngineID::MAXWELL_B: - dma_pusher.BindSubchannel(&*channel_state.maxwell_3d, method_call.subchannel, EngineTypes::Maxwell3D); + dma_pusher.BindSubchannel(&dma_pusher.channel_state.payload->maxwell_3d, method_call.subchannel, EngineTypes::Maxwell3D); break; case EngineID::KEPLER_COMPUTE_B: - dma_pusher.BindSubchannel(&*channel_state.kepler_compute, method_call.subchannel, EngineTypes::KeplerCompute); + dma_pusher.BindSubchannel(&dma_pusher.channel_state.payload->kepler_compute, method_call.subchannel, EngineTypes::KeplerCompute); break; case EngineID::MAXWELL_DMA_COPY_A: - dma_pusher.BindSubchannel(&*channel_state.maxwell_dma, method_call.subchannel, EngineTypes::MaxwellDMA); + dma_pusher.BindSubchannel(&dma_pusher.channel_state.payload->maxwell_dma, method_call.subchannel, EngineTypes::MaxwellDMA); break; case EngineID::KEPLER_INLINE_TO_MEMORY_B: - dma_pusher.BindSubchannel(&*channel_state.kepler_memory, method_call.subchannel, EngineTypes::KeplerMemory); + dma_pusher.BindSubchannel(&dma_pusher.channel_state.payload->kepler_memory, method_call.subchannel, EngineTypes::KeplerMemory); break; case EngineID::NV01_TIMER: - dma_pusher.BindSubchannel(&*channel_state.nv01_timer, method_call.subchannel, EngineTypes::Nv01Timer); + dma_pusher.BindSubchannel(&dma_pusher.channel_state.payload->nv01_timer, method_call.subchannel, EngineTypes::Nv01Timer); break; default: UNIMPLEMENTED_MSG("Unimplemented engine {:04X}", engine_id); @@ -60,15 +52,15 @@ void Puller::ProcessBindMethod(const MethodCall& method_call) { } } -void Puller::ProcessFenceActionMethod() { +void Puller::ProcessFenceActionMethod(DmaPusher& dma_pusher) { switch (regs.fence_action.op) { case Puller::FenceOperation::Acquire: // UNIMPLEMENTED_MSG("Channel Scheduling pending."); // WaitFence(regs.fence_action.syncpoint_id, regs.fence_value); - rasterizer->ReleaseFences(); + dma_pusher.rasterizer->ReleaseFences(); break; case Puller::FenceOperation::Increment: - rasterizer->SignalSyncPoint(regs.fence_action.syncpoint_id); + dma_pusher.rasterizer->SignalSyncPoint(regs.fence_action.syncpoint_id); break; default: UNIMPLEMENTED_MSG("Unimplemented operation {}", regs.fence_action.op.Value()); @@ -76,37 +68,35 @@ void Puller::ProcessFenceActionMethod() { } } -void Puller::ProcessSemaphoreTriggerMethod() { +void Puller::ProcessSemaphoreTriggerMethod(DmaPusher& dma_pusher) { const auto semaphoreOperationMask = 0xF; - const auto op = - static_cast(regs.semaphore_trigger & semaphoreOperationMask); + const auto op = GpuSemaphoreOperation(regs.semaphore_trigger & semaphoreOperationMask); if (op == GpuSemaphoreOperation::WriteLong) { const GPUVAddr sequence_address{regs.semaphore_address.SemaphoreAddress()}; const u32 payload = regs.semaphore_sequence; - rasterizer->Query(sequence_address, VideoCommon::QueryType::Payload, - VideoCommon::QueryPropertiesFlags::HasTimeout, payload, 0); + dma_pusher.rasterizer->Query(sequence_address, VideoCommon::QueryType::Payload, VideoCommon::QueryPropertiesFlags::HasTimeout, payload, 0); } else { do { - const u32 word{memory_manager.Read(regs.semaphore_address.SemaphoreAddress())}; + const u32 word = dma_pusher.memory_manager.Read(regs.semaphore_address.SemaphoreAddress()); regs.acquire_source = true; regs.acquire_value = regs.semaphore_sequence; if (op == GpuSemaphoreOperation::AcquireEqual) { regs.acquire_active = true; regs.acquire_mode = false; if (word != regs.acquire_value) { - rasterizer->ReleaseFences(); + dma_pusher.rasterizer->ReleaseFences(); continue; } } else if (op == GpuSemaphoreOperation::AcquireGequal) { regs.acquire_active = true; regs.acquire_mode = true; if (word < regs.acquire_value) { - rasterizer->ReleaseFences(); + dma_pusher.rasterizer->ReleaseFences(); continue; } } else if (op == GpuSemaphoreOperation::AcquireMask) { if (word && regs.semaphore_sequence == 0) { - rasterizer->ReleaseFences(); + dma_pusher.rasterizer->ReleaseFences(); continue; } } else { @@ -116,21 +106,20 @@ void Puller::ProcessSemaphoreTriggerMethod() { } } -void Puller::ProcessSemaphoreRelease() { +void Puller::ProcessSemaphoreRelease(DmaPusher& dma_pusher) { const GPUVAddr sequence_address{regs.semaphore_address.SemaphoreAddress()}; const u32 payload = regs.semaphore_release; - rasterizer->Query(sequence_address, VideoCommon::QueryType::Payload, - VideoCommon::QueryPropertiesFlags::IsAFence, payload, 0); + dma_pusher.rasterizer->Query(sequence_address, VideoCommon::QueryType::Payload, VideoCommon::QueryPropertiesFlags::IsAFence, payload, 0); } -void Puller::ProcessSemaphoreAcquire() { - u32 word = memory_manager.Read(regs.semaphore_address.SemaphoreAddress()); +void Puller::ProcessSemaphoreAcquire(DmaPusher& dma_pusher) { + u32 word = dma_pusher.memory_manager.Read(regs.semaphore_address.SemaphoreAddress()); const auto value = regs.semaphore_acquire; while (word != value) { regs.acquire_active = true; regs.acquire_value = value; - rasterizer->ReleaseFences(); - word = memory_manager.Read(regs.semaphore_address.SemaphoreAddress()); + dma_pusher.rasterizer->ReleaseFences(); + word = dma_pusher.memory_manager.Read(regs.semaphore_address.SemaphoreAddress()); // TODO(kemathe73) figure out how to do the acquire_timeout regs.acquire_mode = false; regs.acquire_source = false; @@ -138,13 +127,13 @@ void Puller::ProcessSemaphoreAcquire() { } /// Calls a GPU puller method. -void Puller::CallPullerMethod(const MethodCall& method_call) { +void Puller::CallPullerMethod(DmaPusher& dma_pusher, const MethodCall& method_call) { regs.reg_array[method_call.method] = method_call.argument; const auto method = static_cast(method_call.method); switch (method) { case BufferMethods::BindObject: { - ProcessBindMethod(method_call); + ProcessBindMethod(dma_pusher, method_call); break; } case BufferMethods::Nop: @@ -155,16 +144,16 @@ void Puller::CallPullerMethod(const MethodCall& method_call) { case BufferMethods::WrcacheFlush: break; case BufferMethods::RefCnt: - rasterizer->SignalReference(); + dma_pusher.rasterizer->SignalReference(); break; case BufferMethods::SyncpointOperation: - ProcessFenceActionMethod(); + ProcessFenceActionMethod(dma_pusher); break; case BufferMethods::WaitForIdle: - rasterizer->WaitForIdle(); + dma_pusher.rasterizer->WaitForIdle(); break; case BufferMethods::SemaphoreOperation: { - ProcessSemaphoreTriggerMethod(); + ProcessSemaphoreTriggerMethod(dma_pusher); break; } case BufferMethods::NonStallInterrupt: { @@ -177,7 +166,7 @@ void Puller::CallPullerMethod(const MethodCall& method_call) { } case BufferMethods::MemOpB: { // Implement this better. - rasterizer->InvalidateGPUCache(); + dma_pusher.rasterizer->InvalidateGPUCache(); break; } case BufferMethods::MemOpC: @@ -186,11 +175,11 @@ void Puller::CallPullerMethod(const MethodCall& method_call) { break; } case BufferMethods::SemaphoreAcquire: { - ProcessSemaphoreAcquire(); + ProcessSemaphoreAcquire(dma_pusher); break; } case BufferMethods::SemaphoreRelease: { - ProcessSemaphoreRelease(); + ProcessSemaphoreRelease(dma_pusher); break; } case BufferMethods::Yield: { @@ -205,27 +194,26 @@ void Puller::CallPullerMethod(const MethodCall& method_call) { } /// Calls a GPU engine method. -void Puller::CallEngineMethod(const MethodCall& method_call) { +void Puller::CallEngineMethod(DmaPusher& dma_pusher, const MethodCall& method_call) { const EngineID engine = bound_engines[method_call.subchannel]; - switch (engine) { case EngineID::FERMI_TWOD_A: - channel_state.fermi_2d->CallMethod(method_call.method, method_call.argument, method_call.IsLastCall()); + dma_pusher.channel_state.payload->fermi_2d.CallMethod(dma_pusher.system, method_call.method, method_call.argument, method_call.IsLastCall()); break; case EngineID::MAXWELL_B: - channel_state.maxwell_3d->CallMethod(method_call.method, method_call.argument, method_call.IsLastCall()); + dma_pusher.channel_state.payload->maxwell_3d.CallMethod(dma_pusher.system, method_call.method, method_call.argument, method_call.IsLastCall()); break; case EngineID::KEPLER_COMPUTE_B: - channel_state.kepler_compute->CallMethod(method_call.method, method_call.argument, method_call.IsLastCall()); + dma_pusher.channel_state.payload->kepler_compute.CallMethod(dma_pusher.system, method_call.method, method_call.argument, method_call.IsLastCall()); break; case EngineID::MAXWELL_DMA_COPY_A: - channel_state.maxwell_dma->CallMethod(method_call.method, method_call.argument, method_call.IsLastCall()); + dma_pusher.channel_state.payload->maxwell_dma.CallMethod(dma_pusher.system, method_call.method, method_call.argument, method_call.IsLastCall()); break; case EngineID::KEPLER_INLINE_TO_MEMORY_B: - channel_state.kepler_memory->CallMethod(method_call.method, method_call.argument, method_call.IsLastCall()); + dma_pusher.channel_state.payload->kepler_memory.CallMethod(dma_pusher.system, method_call.method, method_call.argument, method_call.IsLastCall()); break; case EngineID::NV01_TIMER: - channel_state.nv01_timer->CallMethod(method_call.method, method_call.argument, method_call.IsLastCall()); + dma_pusher.channel_state.payload->nv01_timer.CallMethod(dma_pusher.system, method_call.method, method_call.argument, method_call.IsLastCall()); break; default: UNIMPLEMENTED_MSG("Unimplemented engine"); @@ -234,28 +222,26 @@ void Puller::CallEngineMethod(const MethodCall& method_call) { } /// Calls a GPU engine multivalue method. -void Puller::CallEngineMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount, - u32 methods_pending) { +void Puller::CallEngineMultiMethod(DmaPusher& dma_pusher, u32 method, u32 subchannel, const u32* base_start, u32 amount, u32 methods_pending) { const EngineID engine = bound_engines[subchannel]; - switch (engine) { case EngineID::FERMI_TWOD_A: - channel_state.fermi_2d->CallMultiMethod(method, base_start, amount, methods_pending); + dma_pusher.channel_state.payload->fermi_2d.CallMultiMethod(dma_pusher.system, method, base_start, amount, methods_pending); break; case EngineID::MAXWELL_B: - channel_state.maxwell_3d->CallMultiMethod(method, base_start, amount, methods_pending); + dma_pusher.channel_state.payload->maxwell_3d.CallMultiMethod(dma_pusher.system, method, base_start, amount, methods_pending); break; case EngineID::KEPLER_COMPUTE_B: - channel_state.kepler_compute->CallMultiMethod(method, base_start, amount, methods_pending); + dma_pusher.channel_state.payload->kepler_compute.CallMultiMethod(dma_pusher.system, method, base_start, amount, methods_pending); break; case EngineID::MAXWELL_DMA_COPY_A: - channel_state.maxwell_dma->CallMultiMethod(method, base_start, amount, methods_pending); + dma_pusher.channel_state.payload->maxwell_dma.CallMultiMethod(dma_pusher.system, method, base_start, amount, methods_pending); break; case EngineID::KEPLER_INLINE_TO_MEMORY_B: - channel_state.kepler_memory->CallMultiMethod(method, base_start, amount, methods_pending); + dma_pusher.channel_state.payload->kepler_memory.CallMultiMethod(dma_pusher.system, method, base_start, amount, methods_pending); break; case EngineID::NV01_TIMER: - channel_state.nv01_timer->CallMultiMethod(method, base_start, amount, methods_pending); + dma_pusher.channel_state.payload->nv01_timer.CallMultiMethod(dma_pusher.system, method, base_start, amount, methods_pending); break; default: UNIMPLEMENTED_MSG("Unimplemented engine"); @@ -264,31 +250,26 @@ void Puller::CallEngineMultiMethod(u32 method, u32 subchannel, const u32* base_s } /// Calls a GPU method. -void Puller::CallMethod(const MethodCall& method_call) { - LOG_TRACE(HW_GPU, "Processing method {:08X} on subchannel {}", method_call.method, - method_call.subchannel); - +void Puller::CallMethod(DmaPusher& dma_pusher, const MethodCall& method_call) { + LOG_TRACE(HW_GPU, "Processing method {:08X} on subchannel {}", method_call.method, method_call.subchannel); ASSERT(method_call.subchannel < bound_engines.size()); - if (ExecuteMethodOnEngine(method_call.method)) { - CallEngineMethod(method_call); + if (ExecuteMethodOnEngine(dma_pusher, method_call.method)) { + CallEngineMethod(dma_pusher, method_call); } else { - CallPullerMethod(method_call); + CallPullerMethod(dma_pusher, method_call); } } /// Calls a GPU multivalue method. -void Puller::CallMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount, - u32 methods_pending) { +void Puller::CallMultiMethod(DmaPusher& dma_pusher, u32 method, u32 subchannel, const u32* base_start, u32 amount, u32 methods_pending) { LOG_TRACE(HW_GPU, "Processing method {:08X} on subchannel {}", method, subchannel); - ASSERT(subchannel < bound_engines.size()); - - if (ExecuteMethodOnEngine(method)) { - CallEngineMultiMethod(method, subchannel, base_start, amount, methods_pending); + if (ExecuteMethodOnEngine(dma_pusher, method)) { + CallEngineMultiMethod(dma_pusher, method, subchannel, base_start, amount, methods_pending); } else { for (u32 i = 0; i < amount; i++) { - CallPullerMethod(MethodCall{ + CallPullerMethod(dma_pusher, MethodCall{ method, base_start[i], subchannel, @@ -298,13 +279,9 @@ void Puller::CallMultiMethod(u32 method, u32 subchannel, const u32* base_start, } } -void Puller::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) { - rasterizer = rasterizer_; -} - /// Determines where the method should be executed. -[[nodiscard]] bool Puller::ExecuteMethodOnEngine(u32 method) { - const auto buffer_method = static_cast(method); +[[nodiscard]] bool Puller::ExecuteMethodOnEngine(DmaPusher& dma_pusher, u32 method) { + const auto buffer_method = BufferMethods(method); return buffer_method >= BufferMethods::NonPullerMethods; } diff --git a/src/video_core/engines/puller.h b/src/video_core/engines/puller.h index fe5102e3ed..e8994f5640 100644 --- a/src/video_core/engines/puller.h +++ b/src/video_core/engines/puller.h @@ -70,32 +70,13 @@ public: BitField<8, 24, u32> syncpoint_id; }; - explicit Puller(GPU& gpu_, MemoryManager& memory_manager_, DmaPusher& dma_pusher, - Control::ChannelState& channel_state); - ~Puller(); - - void CallMethod(const MethodCall& method_call); - - void CallMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount, - u32 methods_pending); - - void BindRasterizer(VideoCore::RasterizerInterface* rasterizer); - - void CallPullerMethod(const MethodCall& method_call); - - void CallEngineMethod(const MethodCall& method_call); - - void CallEngineMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount, - u32 methods_pending); - + void CallMethod(DmaPusher& dma_pusher, const MethodCall& method_call); + void CallMultiMethod(DmaPusher& dma_pusher, u32 method, u32 subchannel, const u32* base_start, u32 amount, u32 methods_pending); + void BindRasterizer(DmaPusher& dma_pusher, VideoCore::RasterizerInterface* rasterizer); + void CallPullerMethod(DmaPusher& dma_pusher, const MethodCall& method_call); + void CallEngineMethod(DmaPusher& dma_pusher, const MethodCall& method_call); + void CallEngineMultiMethod(DmaPusher& dma_pusher, u32 method, u32 subchannel, const u32* base_start, u32 amount, u32 methods_pending); private: - Tegra::GPU& gpu; - - MemoryManager& memory_manager; - DmaPusher& dma_pusher; - Control::ChannelState& channel_state; - VideoCore::RasterizerInterface* rasterizer = nullptr; - static constexpr std::size_t NUM_REGS = 0x800; struct Regs { static constexpr size_t NUM_REGS = 0x40; @@ -139,12 +120,12 @@ private: }; } regs{}; - void ProcessBindMethod(const MethodCall& method_call); - void ProcessFenceActionMethod(); - void ProcessSemaphoreAcquire(); - void ProcessSemaphoreRelease(); - void ProcessSemaphoreTriggerMethod(); - [[nodiscard]] bool ExecuteMethodOnEngine(u32 method); + void ProcessBindMethod(DmaPusher& dma_pusher, const MethodCall& method_call); + void ProcessFenceActionMethod(DmaPusher& dma_pusher); + void ProcessSemaphoreAcquire(DmaPusher& dma_pusher); + void ProcessSemaphoreRelease(DmaPusher& dma_pusher); + void ProcessSemaphoreTriggerMethod(DmaPusher& dma_pusher); + [[nodiscard]] bool ExecuteMethodOnEngine(DmaPusher& dma_pusher, u32 method); /// Mapping of command subchannels to their bound engine ids std::array bound_engines{}; @@ -157,8 +138,7 @@ private: }; #define ASSERT_REG_POSITION(field_name, position) \ - static_assert(offsetof(Regs, field_name) == position * 4, \ - "Field " #field_name " has invalid position") + static_assert(offsetof(Regs, field_name) == position * 4, "Field " #field_name " has invalid position") ASSERT_REG_POSITION(semaphore_address, 0x4); ASSERT_REG_POSITION(semaphore_sequence, 0x6); diff --git a/src/video_core/fence_manager.h b/src/video_core/fence_manager.h index e4c4329e81..1b11ff52d2 100644 --- a/src/video_core/fence_manager.h +++ b/src/video_core/fence_manager.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project @@ -76,9 +76,12 @@ public: TryReleasePendingFences(); } const bool should_flush = ShouldFlush(); - const bool delay_fence = Settings::IsGPULevelHigh() || (Settings::IsGPULevelMedium() && should_flush); + const bool antiflicker_toggled = Settings::values.antiflicker.GetValue(); + const bool delay_fence = Settings::IsGPULevelHigh() || + (Settings::IsGPULevelMedium() && should_flush) || + antiflicker_toggled; CommitAsyncFlushes(); - TFence new_fence = CreateFence(!should_flush); + TFence new_fence = CreateFence(!should_flush && !antiflicker_toggled); if constexpr (can_async_check) { guard.lock(); } diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 5f4054212f..548a8bf809 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -40,30 +40,31 @@ namespace Tegra { struct GPU::Impl { - explicit Impl(GPU& gpu_, Core::System& system_, bool is_async_, bool use_nvdec_) - : gpu{gpu_}, system{system_}, host1x{system.Host1x()}, use_nvdec{use_nvdec_}, - shader_notify{std::make_unique()}, is_async{is_async_}, - gpu_thread{system_, is_async_}, scheduler{std::make_unique(gpu)} {} + explicit Impl(Core::System& system_, bool is_async_, bool use_nvdec_) + : system{system_} + , use_nvdec{use_nvdec_} + , shader_notify() + , is_async{is_async_} + , gpu_thread{system_} + {} ~Impl() = default; std::shared_ptr CreateChannel(s32 channel_id) { auto channel_state = std::make_shared(channel_id); channels.emplace(channel_id, channel_state); - scheduler->DeclareChannel(channel_state); + scheduler.DeclareChannel(channel_state); return channel_state; } void BindChannel(s32 channel_id) { - if (bound_channel == channel_id) { - return; + if (bound_channel != channel_id) { + auto it = channels.find(channel_id); + ASSERT(it != channels.end()); + bound_channel = channel_id; + current_channel = it->second.get(); + renderer->ReadRasterizer()->BindChannel(*current_channel); } - auto it = channels.find(channel_id); - ASSERT(it != channels.end()); - bound_channel = channel_id; - current_channel = it->second.get(); - - rasterizer->BindChannel(*current_channel); } std::shared_ptr AllocateChannel() { @@ -71,13 +72,13 @@ struct GPU::Impl { } void InitChannel(Control::ChannelState& to_init, u64 program_id) { - to_init.Init(system, gpu, program_id); - to_init.BindRasterizer(rasterizer); - rasterizer->InitializeChannel(to_init); + to_init.Init(system, program_id); + to_init.BindRasterizer(renderer->ReadRasterizer()); + renderer->ReadRasterizer()->InitializeChannel(to_init); } void InitAddressSpace(Tegra::MemoryManager& memory_manager) { - memory_manager.BindRasterizer(rasterizer); + memory_manager.BindRasterizer(renderer->ReadRasterizer()); } void ReleaseChannel(Control::ChannelState& to_release) { @@ -87,26 +88,26 @@ struct GPU::Impl { /// Binds a renderer to the GPU. void BindRenderer(std::unique_ptr renderer_) { renderer = std::move(renderer_); - rasterizer = renderer->ReadRasterizer(); - host1x.MemoryManager().BindInterface(rasterizer); - host1x.gmmu_manager.BindRasterizer(rasterizer); + system.Host1x().memory_manager.BindInterface(renderer->ReadRasterizer()); + system.Host1x().gmmu_manager.BindRasterizer(renderer->ReadRasterizer()); } /// Flush all current written commands into the host GPU for execution. void FlushCommands() { - rasterizer->FlushCommands(); + renderer->ReadRasterizer()->FlushCommands(); } /// Synchronizes CPU writes with Host GPU memory. void InvalidateGPUCache() { - std::function callback_writes( - [this](PAddr address, size_t size) { rasterizer->OnCacheInvalidation(address, size); }); + std::function callback_writes([this](PAddr address, size_t size) { + renderer->ReadRasterizer()->OnCacheInvalidation(address, size); + }); system.GatherGPUDirtyMemory(callback_writes); } /// Signal the ending of command list. void OnCommandListEnd() { - rasterizer->ReleaseFences(false); + renderer->ReadRasterizer()->ReleaseFences(false); Settings::UpdateGPUAccuracy(); } @@ -143,62 +144,6 @@ struct GPU::Impl { } } - /// Returns a reference to the Maxwell3D GPU engine. - [[nodiscard]] Engines::Maxwell3D& Maxwell3D() { - ASSERT(current_channel); - return *current_channel->maxwell_3d; - } - - /// Returns a const reference to the Maxwell3D GPU engine. - [[nodiscard]] const Engines::Maxwell3D& Maxwell3D() const { - ASSERT(current_channel); - return *current_channel->maxwell_3d; - } - - /// Returns a reference to the KeplerCompute GPU engine. - [[nodiscard]] Engines::KeplerCompute& KeplerCompute() { - ASSERT(current_channel); - return *current_channel->kepler_compute; - } - - /// Returns a reference to the KeplerCompute GPU engine. - [[nodiscard]] const Engines::KeplerCompute& KeplerCompute() const { - ASSERT(current_channel); - return *current_channel->kepler_compute; - } - - /// Returns a reference to the GPU DMA pusher. - [[nodiscard]] Tegra::DmaPusher& DmaPusher() { - ASSERT(current_channel); - return *current_channel->dma_pusher; - } - - /// Returns a const reference to the GPU DMA pusher. - [[nodiscard]] const Tegra::DmaPusher& DmaPusher() const { - ASSERT(current_channel); - return *current_channel->dma_pusher; - } - - /// Returns a reference to the underlying renderer. - [[nodiscard]] VideoCore::RendererBase& Renderer() { - return *renderer; - } - - /// Returns a const reference to the underlying renderer. - [[nodiscard]] const VideoCore::RendererBase& Renderer() const { - return *renderer; - } - - /// Returns a reference to the shader notifier. - [[nodiscard]] VideoCore::ShaderNotify& ShaderNotify() { - return *shader_notify; - } - - /// Returns a const reference to the shader notifier. - [[nodiscard]] const VideoCore::ShaderNotify& ShaderNotify() const { - return *shader_notify; - } - [[nodiscard]] u64 GetTicks() const { u64 gpu_tick = system.CoreTiming().GetGPUTicks(); Settings::GpuOverclock overclock = Settings::values.fast_gpu_time.GetValue(); @@ -210,14 +155,6 @@ struct GPU::Impl { return gpu_tick; } - [[nodiscard]] bool IsAsync() const { - return is_async; - } - - [[nodiscard]] bool UseNvdec() const { - return use_nvdec; - } - void RendererFrameEndNotify() { system.GetPerfStats().EndGameFrame(); } @@ -227,7 +164,7 @@ struct GPU::Impl { /// core timing events. void Start() { Settings::UpdateGPUAccuracy(); - gpu_thread.StartThread(*renderer, renderer->Context(), *scheduler); + gpu_thread.StartThread(*renderer, renderer->Context(), scheduler); } void NotifyShutdown() { @@ -251,25 +188,24 @@ struct GPU::Impl { /// Push GPU command entries to be processed void PushGPUEntries(s32 channel, Tegra::CommandList&& entries) { - gpu_thread.SubmitList(channel, std::move(entries)); + gpu_thread.SubmitList(channel, std::move(entries), is_async); } /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory void FlushRegion(DAddr addr, u64 size) { - gpu_thread.FlushRegion(addr, size); + gpu_thread.FlushRegion(addr, size, is_async); } VideoCore::RasterizerDownloadArea OnCPURead(DAddr addr, u64 size) { - auto raster_area = rasterizer->GetFlushArea(addr, size); + auto raster_area = renderer->ReadRasterizer()->GetFlushArea(addr, size); if (raster_area.preemtive) { return raster_area; } raster_area.preemtive = true; const u64 fence = RequestSyncOperation([this, &raster_area]() { - rasterizer->FlushRegion(raster_area.start_address, - raster_area.end_address - raster_area.start_address); + renderer->ReadRasterizer()->FlushRegion(raster_area.start_address, raster_area.end_address - raster_area.start_address); }); - gpu_thread.TickGPU(); + gpu_thread.TickGPU(is_async); WaitForSyncOperation(fence); return raster_area; } @@ -280,16 +216,15 @@ struct GPU::Impl { } bool OnCPUWrite(DAddr addr, u64 size) { - return rasterizer->OnCPUWrite(addr, size); + return renderer->ReadRasterizer()->OnCPUWrite(addr, size); } /// Notify rasterizer that any caches of the specified region should be flushed and invalidated void FlushAndInvalidateRegion(DAddr addr, u64 size) { - gpu_thread.FlushAndInvalidateRegion(addr, size); + gpu_thread.FlushAndInvalidateRegion(addr, size, is_async); } - void RequestComposite(std::vector&& layers, - std::vector&& fences) { + void RequestComposite(std::vector&& layers, std::vector&& fences) { size_t num_fences{fences.size()}; size_t current_request_counter{}; { @@ -303,27 +238,26 @@ struct GPU::Impl { free_swap_counters.pop_front(); } } - const auto wait_fence = - RequestSyncOperation([this, current_request_counter, &layers, &fences, num_fences] { - auto& syncpoint_manager = host1x.GetSyncpointManager(); - if (num_fences == 0) { - renderer->Composite(layers); - } - const auto executer = [this, current_request_counter, layers_copy = layers]() { - { - std::unique_lock lk(request_swap_mutex); - if (--request_swap_counters[current_request_counter] != 0) { - return; - } - free_swap_counters.push_back(current_request_counter); + const auto wait_fence = RequestSyncOperation([this, current_request_counter, &layers, &fences, num_fences] { + auto& syncpoint_manager = system.Host1x().GetSyncpointManager(); + if (num_fences == 0) { + renderer->Composite(layers); + } + const auto executer = [this, current_request_counter, layers_copy = layers]() { + { + std::unique_lock lk(request_swap_mutex); + if (--request_swap_counters[current_request_counter] != 0) { + return; } - renderer->Composite(layers_copy); - }; - for (size_t i = 0; i < num_fences; i++) { - syncpoint_manager.RegisterGuestAction(fences[i].id, fences[i].value, executer); + free_swap_counters.push_back(current_request_counter); } - }); - gpu_thread.TickGPU(); + renderer->Composite(layers_copy); + }; + for (size_t i = 0; i < num_fences; i++) { + syncpoint_manager.RegisterGuestAction(fences[i].id, fences[i].value, executer); + } + }); + gpu_thread.TickGPU(is_async); WaitForSyncOperation(wait_fence); } @@ -332,23 +266,20 @@ struct GPU::Impl { const auto wait_fence = RequestSyncOperation([&] { out = renderer->GetAppletCaptureBuffer(); }); - gpu_thread.TickGPU(); + gpu_thread.TickGPU(is_async); WaitForSyncOperation(wait_fence); return out; } - GPU& gpu; Core::System& system; - Host1x::Host1x& host1x; std::unique_ptr renderer; - VideoCore::RasterizerInterface* rasterizer = nullptr; const bool use_nvdec; s32 new_channel_id{1}; /// Shader build notifier - std::unique_ptr shader_notify; + VideoCore::ShaderNotify shader_notify; /// When true, we are about to shut down emulation session, so terminate outstanding tasks std::atomic_bool shutting_down{}; @@ -372,7 +303,7 @@ struct GPU::Impl { VideoCommon::GPUThread::ThreadManager gpu_thread; std::unique_ptr cpu_context; - std::unique_ptr scheduler; + Tegra::Control::Scheduler scheduler; ankerl::unordered_dense::map> channels; Tegra::Control::ChannelState* current_channel; s32 bound_channel{-1}; @@ -383,7 +314,8 @@ struct GPU::Impl { }; GPU::GPU(Core::System& system, bool is_async, bool use_nvdec) - : impl{std::make_unique(*this, system, is_async, use_nvdec)} {} + : impl{std::make_unique(system, is_async, use_nvdec)} +{} GPU::~GPU() = default; @@ -424,8 +356,9 @@ void GPU::OnCommandListEnd() { } u64 GPU::RequestFlush(DAddr addr, std::size_t size) { - return impl->RequestSyncOperation( - [this, addr, size]() { impl->rasterizer->FlushRegion(addr, size); }); + return impl->RequestSyncOperation([this, addr, size]() { + impl->renderer->ReadRasterizer()->FlushRegion(addr, size); + }); } u64 GPU::CurrentSyncRequestFence() const { @@ -442,52 +375,52 @@ void GPU::TickWork() { /// Gets a mutable reference to the Host1x interface Host1x::Host1x& GPU::Host1x() { - return impl->host1x; + return impl->system.Host1x(); } /// Gets an immutable reference to the Host1x interface. const Host1x::Host1x& GPU::Host1x() const { - return impl->host1x; + return impl->system.Host1x(); } Engines::Maxwell3D& GPU::Maxwell3D() { - return impl->Maxwell3D(); + return impl->current_channel->payload->maxwell_3d; } const Engines::Maxwell3D& GPU::Maxwell3D() const { - return impl->Maxwell3D(); + return impl->current_channel->payload->maxwell_3d; } Engines::KeplerCompute& GPU::KeplerCompute() { - return impl->KeplerCompute(); + return impl->current_channel->payload->kepler_compute; } const Engines::KeplerCompute& GPU::KeplerCompute() const { - return impl->KeplerCompute(); + return impl->current_channel->payload->kepler_compute; } Tegra::DmaPusher& GPU::DmaPusher() { - return impl->DmaPusher(); + return impl->current_channel->payload->dma_pusher; } const Tegra::DmaPusher& GPU::DmaPusher() const { - return impl->DmaPusher(); + return impl->current_channel->payload->dma_pusher; } VideoCore::RendererBase& GPU::Renderer() { - return impl->Renderer(); + return *impl->renderer; } const VideoCore::RendererBase& GPU::Renderer() const { - return impl->Renderer(); + return *impl->renderer; } VideoCore::ShaderNotify& GPU::ShaderNotify() { - return impl->ShaderNotify(); + return impl->shader_notify; } const VideoCore::ShaderNotify& GPU::ShaderNotify() const { - return impl->ShaderNotify(); + return impl->shader_notify; } void GPU::RequestComposite(std::vector&& layers, @@ -504,11 +437,11 @@ u64 GPU::GetTicks() const { } bool GPU::IsAsync() const { - return impl->IsAsync(); + return impl->is_async; } bool GPU::UseNvdec() const { - return impl->UseNvdec(); + return impl->use_nvdec; } void GPU::RendererFrameEndNotify() { diff --git a/src/video_core/gpu_logging/freedreno_debug.cpp b/src/video_core/gpu_logging/freedreno_debug.cpp index 8b65bd0cd5..e01c154031 100644 --- a/src/video_core/gpu_logging/freedreno_debug.cpp +++ b/src/video_core/gpu_logging/freedreno_debug.cpp @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later -#ifdef ANDROID +#ifdef __ANDROID__ #include "video_core/gpu_logging/freedreno_debug.h" #include "common/logging.h" @@ -49,4 +49,4 @@ std::string FreedrenoDebugger::GetBreadcrumbs() { } // namespace GPU::Logging::Freedreno -#endif // ANDROID +#endif // __ANDROID__ diff --git a/src/video_core/gpu_logging/freedreno_debug.h b/src/video_core/gpu_logging/freedreno_debug.h index feb57a0b2a..7e4f812f46 100644 --- a/src/video_core/gpu_logging/freedreno_debug.h +++ b/src/video_core/gpu_logging/freedreno_debug.h @@ -3,7 +3,7 @@ #pragma once -#ifdef ANDROID +#ifdef __ANDROID__ #include @@ -29,4 +29,4 @@ private: } // namespace GPU::Logging::Freedreno -#endif // ANDROID +#endif // __ANDROID__ diff --git a/src/video_core/gpu_logging/gpu_logging.cpp b/src/video_core/gpu_logging/gpu_logging.cpp index 5f5e376e99..72e48e4996 100644 --- a/src/video_core/gpu_logging/gpu_logging.cpp +++ b/src/video_core/gpu_logging/gpu_logging.cpp @@ -4,6 +4,7 @@ #include "video_core/gpu_logging/gpu_logging.h" #include +#include #include #include "common/fs/file.h" @@ -280,13 +281,12 @@ void GPULogger::LogMemoryDeallocation(uintptr_t memory) { } void GPULogger::LogShaderCompilation(const std::string& shader_name, - const std::string& shader_info, - std::span spirv_code) { + const std::string& shader_info) { if (!initialized || current_level == LogLevel::Off) { return; } - if (!dump_shaders && current_level < LogLevel::Verbose) { + if (current_level < LogLevel::Verbose) { return; } @@ -294,38 +294,36 @@ void GPULogger::LogShaderCompilation(const std::string& shader_name, std::chrono::steady_clock::now().time_since_epoch()); const auto log_entry = fmt::format("[{}] [Shader] Compiled: {} ({})\n", - FormatTimestamp(timestamp), shader_name, shader_info); + FormatTimestamp(timestamp), shader_name, shader_info); WriteToLog(log_entry); +} - // Dump SPIR-V binary if enabled and we have data - if (dump_shaders && !spirv_code.empty()) { - using namespace Common::FS; - const auto& log_dir = GetEdenPath(EdenPath::LogDir); - const auto shaders_dir = log_dir / "shaders"; +bool IsActive() noexcept { + return Settings::values.gpu_log_level.GetValue() != Settings::GpuLogLevel::Off; +} - // Create directory on first dump - if (!shader_dump_dir_created) { - [[maybe_unused]] const bool created = CreateDir(shaders_dir); - shader_dump_dir_created = true; - } - - // Write SPIR-V binary file - const auto shader_path = shaders_dir / fmt::format("{}.spv", shader_name); - auto shader_file = std::make_unique( - shader_path, FileAccessMode::Write, FileType::BinaryFile); - - if (shader_file->IsOpen()) { - const size_t bytes_to_write = spirv_code.size() * sizeof(u32); - static_cast(shader_file->WriteSpan(spirv_code)); - shader_file->Close(); - - const auto dump_log = fmt::format("[{}] [Shader] Dumped SPIR-V: {} ({} bytes)\n", - FormatTimestamp(timestamp), shader_path.string(), bytes_to_write); - WriteToLog(dump_log); - } else { - LOG_WARNING(Render_Vulkan, "[GPU Logging] Failed to dump shader: {}", shader_path.string()); - } +void DumpSpirvShader(u64 shader_hash, std::span spirv_code) { + if (spirv_code.empty()) { + return; } + + using namespace Common::FS; + const auto& dump_dir = GetEdenPath(EdenPath::DumpDir); + + // Ensure DumpDir exists once. CreateDir is idempotent, so guarded to skip the syscall. + static std::once_flag dump_dir_flag; + std::call_once(dump_dir_flag, [&dump_dir]() { + [[maybe_unused]] const bool created = CreateDir(dump_dir); + }); + + const auto shader_path = dump_dir / fmt::format("{:016x}_{:016x}.spv", + Settings::GetCurrentProgramID(), shader_hash); + Common::FS::IOFile shader_file(shader_path, FileAccessMode::Write, FileType::BinaryFile); + if (!shader_file.IsOpen()) { + LOG_WARNING(Render_Vulkan, "[Shader Dump] Failed to open {}", shader_path.string()); + return; + } + static_cast(shader_file.WriteSpan(spirv_code)); } void GPULogger::LogPipelineStateChange(const std::string& state_info) { @@ -657,10 +655,6 @@ void GPULogger::EnableVulkanCallTracking(bool enabled) { track_vulkan_calls = enabled; } -void GPULogger::EnableShaderDumps(bool enabled) { - dump_shaders = enabled; -} - void GPULogger::EnableMemoryTracking(bool enabled) { track_memory = enabled; } diff --git a/src/video_core/gpu_logging/gpu_logging.h b/src/video_core/gpu_logging/gpu_logging.h index 134fa94c0f..b16988ed0b 100644 --- a/src/video_core/gpu_logging/gpu_logging.h +++ b/src/video_core/gpu_logging/gpu_logging.h @@ -87,8 +87,7 @@ public: void LogVulkanCall(const std::string& call_name, const std::string& params, int result); void LogMemoryAllocation(uintptr_t memory, u64 size, u32 memory_flags); void LogMemoryDeallocation(uintptr_t memory); - void LogShaderCompilation(const std::string& shader_name, const std::string& shader_info, - std::span spirv_code = {}); + void LogShaderCompilation(const std::string& shader_name, const std::string& shader_info); void LogPipelineStateChange(const std::string& state_info); void LogDriverDebugInfo(const std::string& debug_info); @@ -121,7 +120,6 @@ public: // Settings void SetLogLevel(LogLevel level); void EnableVulkanCallTracking(bool enabled); - void EnableShaderDumps(bool enabled); void EnableMemoryTracking(bool enabled); void EnableDriverDebugInfo(bool enabled); void SetRingBufferSize(size_t entries); @@ -171,7 +169,6 @@ private: // Feature flags bool track_vulkan_calls = true; - bool dump_shaders = false; bool track_memory = false; bool capture_driver_debug = false; @@ -179,15 +176,16 @@ private: std::set used_extensions; mutable std::mutex extension_mutex; - // Shader dump directory (created on demand) - bool shader_dump_dir_created = false; - // Stored state for crash dumps std::string stored_driver_debug_info; std::string stored_pipeline_state; mutable std::mutex state_mutex; }; +[[nodiscard]] bool IsActive() noexcept; + +void DumpSpirvShader(u64 shader_hash, std::span spirv_code); + // Helper to get stage name from index inline const char* GetShaderStageName(size_t stage_index) { static constexpr std::array stage_names{ diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp index d7c8ac391c..50570b596d 100644 --- a/src/video_core/gpu_thread.cpp +++ b/src/video_core/gpu_thread.cpp @@ -19,8 +19,9 @@ namespace VideoCommon::GPUThread { -ThreadManager::ThreadManager(Core::System& system_, bool is_async_) - : system{system_}, is_async{is_async_} {} +ThreadManager::ThreadManager(Core::System& system_) + : system{system_} +{} ThreadManager::~ThreadManager() = default; @@ -39,7 +40,7 @@ void ThreadManager::StartThread(VideoCore::RendererBase& renderer, Core::Fronten break; } if (auto* submit_list = std::get_if(&next.data)) { - scheduler.Push(submit_list->channel, std::move(submit_list->entries)); + scheduler.Push(system.GPU(), submit_list->channel, std::move(submit_list->entries)); } else if (std::holds_alternative(next.data)) { system.GPU().TickWork(); } else if (const auto* flush = std::get_if(&next.data)) { @@ -60,41 +61,40 @@ void ThreadManager::StartThread(VideoCore::RendererBase& renderer, Core::Fronten }); } -void ThreadManager::SubmitList(s32 channel, Tegra::CommandList&& entries) { - PushCommand(SubmitListCommand(channel, std::move(entries))); +void ThreadManager::SubmitList(s32 channel, Tegra::CommandList&& entries, bool is_async) { + PushCommand(SubmitListCommand(channel, std::move(entries)), false, is_async); } -void ThreadManager::FlushRegion(DAddr addr, u64 size) { +void ThreadManager::FlushRegion(DAddr addr, u64 size, bool is_async) { if (!is_async) { // Always flush with synchronous GPU mode - PushCommand(FlushRegionCommand(addr, size)); + PushCommand(FlushRegionCommand(addr, size), false, is_async); } - return; } -void ThreadManager::TickGPU() { - PushCommand(GPUTickCommand()); +void ThreadManager::TickGPU(bool is_async) { + PushCommand(GPUTickCommand(), false, is_async); } void ThreadManager::InvalidateRegion(DAddr addr, u64 size) { rasterizer->OnCacheInvalidation(addr, size); } -void ThreadManager::FlushAndInvalidateRegion(DAddr addr, u64 size) { +void ThreadManager::FlushAndInvalidateRegion(DAddr addr, u64 size, bool is_async) { if (Settings::IsGPULevelHigh()) { if (!is_async) { - PushCommand(FlushRegionCommand(addr, size)); + PushCommand(FlushRegionCommand(addr, size), false, is_async); } else { auto& gpu = system.GPU(); const u64 fence = gpu.RequestFlush(addr, size); - TickGPU(); + TickGPU(is_async); gpu.WaitForSyncOperation(fence); } } rasterizer->OnCacheInvalidation(addr, size); } -u64 ThreadManager::PushCommand(CommandData&& command_data, bool block) { +u64 ThreadManager::PushCommand(CommandData&& command_data, bool block, bool is_async) { if (!is_async) { // In synchronous GPU mode, block the caller until the command has executed block = true; diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h index ac1283a338..4c3fadce15 100644 --- a/src/video_core/gpu_thread.h +++ b/src/video_core/gpu_thread.h @@ -15,6 +15,7 @@ #include "common/bounded_threadsafe_queue.h" #include "common/polyfill_thread.h" +#include "video_core/dma_pusher.h" #include "video_core/framebuffer_config.h" namespace Tegra { @@ -103,7 +104,7 @@ struct SynchState final { /// Class used to manage the GPU thread class ThreadManager final { public: - explicit ThreadManager(Core::System& system_, bool is_async_); + explicit ThreadManager(Core::System& system_); ~ThreadManager(); /// Creates and starts the GPU thread. @@ -111,27 +112,25 @@ public: Tegra::Control::Scheduler& scheduler); /// Push GPU command entries to be processed - void SubmitList(s32 channel, Tegra::CommandList&& entries); + void SubmitList(s32 channel, Tegra::CommandList&& entries, bool is_async); /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory - void FlushRegion(DAddr addr, u64 size); + void FlushRegion(DAddr addr, u64 size, bool is_async); /// Notify rasterizer that any caches of the specified region should be invalidated void InvalidateRegion(DAddr addr, u64 size); /// Notify rasterizer that any caches of the specified region should be flushed and invalidated - void FlushAndInvalidateRegion(DAddr addr, u64 size); + void FlushAndInvalidateRegion(DAddr addr, u64 size, bool is_async); - void TickGPU(); + void TickGPU(bool is_async); private: /// Pushes a command to be executed by the GPU thread - u64 PushCommand(CommandData&& command_data, bool block = false); + u64 PushCommand(CommandData&& command_data, bool block, bool is_async); Core::System& system; - const bool is_async; VideoCore::RasterizerInterface* rasterizer = nullptr; - SynchState state; std::jthread thread; }; diff --git a/src/video_core/host1x/control.cpp b/src/video_core/host1x/control.cpp index 53b3063557..93ee57456b 100644 --- a/src/video_core/host1x/control.cpp +++ b/src/video_core/host1x/control.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: 2021 yuzu Emulator Project @@ -10,26 +10,22 @@ namespace Tegra::Host1x { -Control::Control(Host1x& host1x_) : host1x(host1x_) {} - -Control::~Control() = default; - -void Control::ProcessMethod(Method method, u32 argument) { +void Control::ProcessMethod(Host1x& host1x, Method method, u32 argument) { switch (method) { case Method::LoadSyncptPayload32: syncpoint_value = argument; break; case Method::WaitSyncpt: case Method::WaitSyncpt32: - Execute(argument); + Execute(host1x, argument); break; default: - UNIMPLEMENTED_MSG("Control method {:#X}", static_cast(method)); + UNIMPLEMENTED_MSG("Control method {:#X}", u32(method)); break; } } -void Control::Execute(u32 data) { +void Control::Execute(Host1x& host1x, u32 data) { LOG_TRACE(Service_NVDRV, "Control wait syncpt {} value {}", data, syncpoint_value); host1x.GetSyncpointManager().WaitHost(data, syncpoint_value); } diff --git a/src/video_core/host1x/control.h b/src/video_core/host1x/control.h index bd8a2d7ad4..4d87e63b32 100644 --- a/src/video_core/host1x/control.h +++ b/src/video_core/host1x/control.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2021 yuzu Emulator Project // SPDX-FileCopyrightText: 2021 Skyline Team and Contributors // SPDX-License-Identifier: GPL-3.0-or-later @@ -19,17 +22,11 @@ public: WaitSyncpt32 = 0x50, }; - explicit Control(Host1x& host1x); - ~Control(); - /// Writes the method into the state, Invoke Execute() if encountered - void ProcessMethod(Method method, u32 argument); - -private: + void ProcessMethod(Host1x& host1x, Method method, u32 argument); /// For Host1x, execute is waiting on a syncpoint previously written into the state - void Execute(u32 data); + void Execute(Host1x& host1x, u32 data); - Host1x& host1x; u32 syncpoint_value{}; }; diff --git a/src/video_core/host1x/ffmpeg.cpp b/src/video_core/host1x/ffmpeg.cpp index 80e48692f7..88df6695d8 100644 --- a/src/video_core/host1x/ffmpeg.cpp +++ b/src/video_core/host1x/ffmpeg.cpp @@ -28,7 +28,7 @@ namespace { constexpr AVPixelFormat PreferredGpuFormat = AV_PIX_FMT_NV12; constexpr AVPixelFormat PreferredCpuFormat = AV_PIX_FMT_YUV420P; constexpr std::array PreferredGpuDecoders = { -#if defined (_WIN32) +#if defined(_WIN32) AV_HWDEVICE_TYPE_CUDA, AV_HWDEVICE_TYPE_D3D11VA, AV_HWDEVICE_TYPE_DXVA2, @@ -39,7 +39,7 @@ constexpr std::array PreferredGpuDecoders = { AV_HWDEVICE_TYPE_DRM, #elif defined(__APPLE__) AV_HWDEVICE_TYPE_VIDEOTOOLBOX, -#elif defined(ANDROID) +#elif defined(__ANDROID__) AV_HWDEVICE_TYPE_MEDIACODEC, #elif defined(__unix__) AV_HWDEVICE_TYPE_CUDA, diff --git a/src/video_core/host1x/host1x.cpp b/src/video_core/host1x/host1x.cpp index b4e4a38c80..590b0e0ff2 100644 --- a/src/video_core/host1x/host1x.cpp +++ b/src/video_core/host1x/host1x.cpp @@ -27,22 +27,22 @@ void Host1x::StartDevice(s32 fd, ChannelType type, u32 syncpt) { #ifdef YUZU_LEGACY std::call_once(nvdec_first_init, []() {std::this_thread::sleep_for(std::chrono::milliseconds{500});}); // HACK: For Astroneer #endif - devices[fd] = std::make_unique(*this, fd, syncpt); + devices[fd].emplace(*this, fd, syncpt); break; case ChannelType::VIC: #ifdef YUZU_LEGACY std::call_once(vic_first_init, []() {std::this_thread::sleep_for(std::chrono::milliseconds{500});}); // HACK: For Astroneer #endif - devices[fd] = std::make_unique(*this, fd, syncpt); + devices[fd].emplace(*this, fd, syncpt); break; default: - LOG_ERROR(HW_GPU, "Unimplemented host1x device {}", static_cast(type)); + LOG_ERROR(HW_GPU, "Unimplemented host1x device {}", u32(type)); break; } } void Host1x::StopDevice(s32 fd, ChannelType type) { - devices.erase(fd); + devices[fd].emplace(); } } // namespace Tegra::Host1x diff --git a/src/video_core/host1x/host1x.h b/src/video_core/host1x/host1x.h index 838e749072..592ec449e8 100644 --- a/src/video_core/host1x/host1x.h +++ b/src/video_core/host1x/host1x.h @@ -8,10 +8,14 @@ #include #include -#include +#include #include "common/common_types.h" +// fd types? +#include "video_core/host1x/nvdec.h" +#include "video_core/host1x/vic.h" + #include "common/address_space.h" #include "video_core/cdma_pusher.h" #include "video_core/host1x/gpu_device_memory_manager.h" @@ -31,118 +35,90 @@ class Nvdec; class FrameQueue { public: + struct FrameDevice { + std::deque>> m_presentation_order; + std::unordered_map> m_decode_order; + }; + void Open(s32 fd) { std::scoped_lock l{m_mutex}; - m_presentation_order.insert({fd, {}}); - m_decode_order.insert({fd, {}}); + m_frame_devices.insert_or_assign(fd, FrameDevice{}); } void Close(s32 fd) { std::scoped_lock l{m_mutex}; - m_presentation_order.erase(fd); - m_decode_order.erase(fd); + m_frame_devices.erase(fd); } s32 VicFindNvdecFdFromOffset(u64 search_offset) { std::scoped_lock l{m_mutex}; - for (auto& map : m_presentation_order) { - for (auto& [offset, frame] : map.second) { - if (offset == search_offset) { - return map.first; - } - } - } - for (auto& map : m_decode_order) { - for (auto& [offset, frame] : map.second) { - if (offset == search_offset) { - return map.first; - } - } + for (auto const& [fd, dev] : m_frame_devices) { + for (auto const& [offset, frame] : dev.m_presentation_order) + if (offset == search_offset) + return fd; + for (auto const& [offset, frame] : dev.m_decode_order) + if (offset == search_offset) + return fd; } return -1; } void PushPresentOrder(s32 fd, u64 offset, std::shared_ptr&& frame) { std::scoped_lock l{m_mutex}; - auto map = m_presentation_order.find(fd); - if (map == m_presentation_order.end()) { - return; + if (auto const it = m_frame_devices.find(fd); it != m_frame_devices.end()) { + if (it->second.m_presentation_order.size() >= MAX_PRESENT_QUEUE) + it->second.m_presentation_order.pop_front(); + it->second.m_presentation_order.emplace_back(offset, std::move(frame)); } - - if (map->second.size() >= MAX_PRESENT_QUEUE) { - map->second.pop_front(); - } - - map->second.emplace_back(offset, std::move(frame)); } void PushDecodeOrder(s32 fd, u64 offset, std::shared_ptr&& frame) { std::scoped_lock l{m_mutex}; - auto map = m_decode_order.find(fd); - if (map == m_decode_order.end()) { - return; - } - - map->second.insert_or_assign(offset, std::move(frame)); - - if (map->second.size() > MAX_DECODE_MAP) { - auto it = map->second.begin(); - std::advance(it, map->second.size() - MAX_DECODE_MAP); - map->second.erase(map->second.begin(), it); + if (auto const it = m_frame_devices.find(fd); it != m_frame_devices.end()) { + it->second.m_decode_order.insert_or_assign(offset, std::move(frame)); + if (it->second.m_decode_order.size() > MAX_DECODE_MAP) { + auto it2 = it->second.m_decode_order.begin(); + std::advance(it2, it->second.m_decode_order.size() - MAX_DECODE_MAP); + it->second.m_decode_order.erase(it->second.m_decode_order.begin(), it2); + } } } std::shared_ptr GetFrame(s32 fd, u64 offset) { - if (fd == -1) { - return {}; + if (fd != -1) { + std::scoped_lock l{m_mutex}; + if (auto const it = m_frame_devices.find(fd); it != m_frame_devices.end()) { + if (it->second.m_presentation_order.size() > 0) + return GetPresentOrderLocked(fd); + if (it->second.m_decode_order.size() > 0) + return GetDecodeOrderLocked(fd, offset); + } } - - std::scoped_lock l{m_mutex}; - - auto present_map = m_presentation_order.find(fd); - if (present_map != m_presentation_order.end() && !present_map->second.empty()) { - return GetPresentOrderLocked(fd); - } - - auto decode_map = m_decode_order.find(fd); - if (decode_map != m_decode_order.end() && !decode_map->second.empty()) { - return GetDecodeOrderLocked(fd, offset); - } - return {}; } private: std::shared_ptr GetPresentOrderLocked(s32 fd) { - auto map = m_presentation_order.find(fd); - if (map == m_presentation_order.end() || map->second.empty()) { - return {}; + if (auto const it = m_frame_devices.find(fd); it != m_frame_devices.end()) { + auto frame = std::move(it->second.m_presentation_order.front().second); + it->second.m_presentation_order.pop_front(); + return frame; } - - auto frame = std::move(map->second.front().second); - map->second.pop_front(); - return frame; + return {}; } std::shared_ptr GetDecodeOrderLocked(s32 fd, u64 offset) { - auto map = m_decode_order.find(fd); - if (map == m_decode_order.end() || map->second.empty()) { - return {}; + if (auto const it = m_frame_devices.find(fd); it != m_frame_devices.end()) { + if (auto const it2 = it->second.m_decode_order.find(offset); it2 != it->second.m_decode_order.end()) { + // TODO: this "mapped" prevents us from fully embracing ankerl + return std::move(it->second.m_decode_order.extract(it2).mapped()); + } } - - auto it = map->second.find(offset); - if (it == map->second.end()) { - return {}; - } - // TODO: this "mapped" prevents us from fully embracing ankerl - return std::move(map->second.extract(it).mapped()); + return {}; } - using FramePtr = std::shared_ptr; - std::mutex m_mutex{}; - ankerl::unordered_dense::map>> m_presentation_order; - ankerl::unordered_dense::map> m_decode_order; + ankerl::unordered_dense::map m_frame_devices; static constexpr size_t MAX_PRESENT_QUEUE = 100; static constexpr size_t MAX_DECODE_MAP = 200; @@ -196,11 +172,11 @@ public: void StopDevice(s32 fd, ChannelType type); void PushEntries(s32 fd, ChCommandHeaderList&& entries) { - auto it = devices.find(fd); - if (it == devices.end()) { - return; + if (auto const nvdec = std::get_if(&devices[fd])) { + nvdec->PushEntries(std::move(entries)); + } else if (auto const vic = std::get_if(&devices[fd])) { + vic->PushEntries(std::move(entries)); } - it->second->PushEntries(std::move(entries)); } Core::System& system; @@ -209,7 +185,11 @@ public: Tegra::MemoryManager gmmu_manager; Common::FlatAllocator allocator; FrameQueue frame_queue; - ankerl::unordered_dense::map> devices; + std::array, 1024> devices; #ifdef YUZU_LEGACY std::once_flag nvdec_first_init; std::once_flag vic_first_init; diff --git a/src/video_core/host1x/vic.cpp b/src/video_core/host1x/vic.cpp index 906714cc16..f7830a3a45 100644 --- a/src/video_core/host1x/vic.cpp +++ b/src/video_core/host1x/vic.cpp @@ -39,7 +39,7 @@ extern "C" { #include "video_core/textures/decoders.h" #if defined(ARCHITECTURE_x86_64) -#include "common/x64/cpu_detect.h" +#include "common/cpu_features.h" #endif #if defined(ARCHITECTURE_x86_64) \ @@ -55,9 +55,8 @@ namespace Tegra::Host1x { namespace { static bool HasSSE41() { -#if defined(ARCHITECTURE_x86_64) - static bool has_sse41 = Common::GetCPUCaps().sse4_1; - return has_sse41; +#ifdef ARCHITECTURE_x86_64 + return Common::g_cpu_caps.sse4_1; #else return false; #endif @@ -408,15 +407,15 @@ void Vic::ReadInterlacedY8__V8U8_N420(const SlotStruct& slot, std::span offsets, std::shared_ptr frame, bool planar) noexcept { switch (slot.config.frame_format) { - case DXVAHD_FRAME_FORMAT::PROGRESSIVE: + case DxvhadFrameFormat::Progressive: ReadProgressiveY8__V8U8_N420(slot, offsets, std::move(frame), planar, false); break; - case DXVAHD_FRAME_FORMAT::TOP_FIELD: + case DxvhadFrameFormat::TopField: ReadInterlacedY8__V8U8_N420(slot, offsets, std::move(frame), planar, true); break; - case DXVAHD_FRAME_FORMAT::BOTTOM_FIELD: + case DxvhadFrameFormat::BottomField: ReadInterlacedY8__V8U8_N420(slot, offsets, std::move(frame), planar, false); break; default: @@ -861,7 +860,7 @@ void Vic::WriteY8__V8U8_N420(const OutputSurfaceConfig& output_surface_config) n }; switch (output_surface_config.out_block_kind) { - case BLK_KIND::GENERIC_16Bx2: { + case BlkKind::Generic_16Bx2: { u32 const block_height = u32(output_surface_config.out_block_height); auto const out_luma_swizzle_size = Texture::CalculateSize(true, BytesPerPixel, out_luma_width, out_luma_height, 1, block_height, 0); auto const out_chroma_swizzle_size = Texture::CalculateSize(true, BytesPerPixel * 2, out_chroma_width, out_chroma_height, 1, block_height, 0); @@ -890,7 +889,7 @@ void Vic::WriteY8__V8U8_N420(const OutputSurfaceConfig& output_surface_config) n Texture::SwizzleTexture(out_chroma, chroma_scratch, BytesPerPixel, out_chroma_width, out_chroma_height, 1, block_height, 0, 1); } } break; - case BLK_KIND::PITCH: { + case BlkKind::Pitch: { LOG_TRACE(HW_GPU, "Writing Y8__V8U8_N420 swizzled frame\n" "\tinput surface {}x{} stride {} size {:#X}\n" "\toutput luma {}x{} stride {} size {:#X} block height {} swizzled size 0x{:X}\n", @@ -1033,7 +1032,7 @@ void Vic::WriteABGR(const OutputSurfaceConfig& output_surface_config, VideoPixel }; switch (output_surface_config.out_block_kind) { - case BLK_KIND::GENERIC_16Bx2: { + case BlkKind::Generic_16Bx2: { const u32 block_height = u32(output_surface_config.out_block_height); auto const out_swizzle_size = Texture::CalculateSize(true, BytesPerPixel, out_luma_width, out_luma_height, 1, block_height, 0); LOG_TRACE(HW_GPU, "Writing ABGR swizzled frame\n" @@ -1052,7 +1051,7 @@ void Vic::WriteABGR(const OutputSurfaceConfig& output_surface_config, VideoPixel Texture::SwizzleTexture(out_luma, luma_scratch, BytesPerPixel, out_luma_width, out_luma_height, 1, block_height, 0, 1); } } break; - case BLK_KIND::PITCH: { + case BlkKind::Pitch: { LOG_TRACE(HW_GPU, "Writing ABGR pitch frame\n" "\tinput surface {}x{} stride {} size {:#X}" "\toutput surface {}x{} stride {} size {:#X}", diff --git a/src/video_core/host1x/vic.h b/src/video_core/host1x/vic.h index d728b38f91..23f3f66c18 100644 --- a/src/video_core/host1x/vic.h +++ b/src/video_core/host1x/vic.h @@ -6,16 +6,12 @@ #pragma once -#include -#include #include -#include -#include #include "common/common_types.h" #include "common/scratch_buffer.h" #include "video_core/cdma_pusher.h" -#include "video_core/host1x/host1x.h" +#include "video_core/host1x/ffmpeg.h" namespace Tegra::Host1x { class Host1x; @@ -138,52 +134,53 @@ enum SurfaceIndex : u32 { CombinedMotion = 7, }; -enum class DXVAHD_ALPHA_FILL_MODE : u32 { - OPAQUE = 0, - BACKGROUND = 1, - DESTINATION = 2, - SOURCE_STREAM = 3, - COMPOSITED = 4, - SOURCE_ALPHA = 5, +// Note: these will inevitably collide with Win32 defines if you use their UPPER_SNAKE_CASE naming +enum class DxvhadAlphaFillMode : u32 { + Opaque = 0, + Background = 1, + Destination = 2, + SourceStream = 3, + Composited = 4, + SourceAlpha = 5, }; -enum class DXVAHD_FRAME_FORMAT : u64 { - PROGRESSIVE = 0, - INTERLACED_TOP_FIELD_FIRST = 1, - INTERLACED_BOTTOM_FIELD_FIRST = 2, - TOP_FIELD = 3, - BOTTOM_FIELD = 4, - SUBPIC_PROGRESSIVE = 5, - SUBPIC_INTERLACED_TOP_FIELD_FIRST = 6, - SUBPIC_INTERLACED_BOTTOM_FIELD_FIRST = 7, - SUBPIC_TOP_FIELD = 8, - SUBPIC_BOTTOM_FIELD = 9, - TOP_FIELD_CHROMA_BOTTOM = 10, - BOTTOM_FIELD_CHROMA_TOP = 11, - SUBPIC_TOP_FIELD_CHROMA_BOTTOM = 12, - SUBPIC_BOTTOM_FIELD_CHROMA_TOP = 13, +enum class DxvhadFrameFormat : u64 { + Progressive = 0, + InterlacedTopFieldFirst = 1, + InterlacedBottomFieldFirst = 2, + TopField = 3, + BottomField = 4, + SubpicProgressive = 5, + SubpicInterlacedTopFieldFirst = 6, + SubpicInterlacedBottomFieldFirst = 7, + SubpicTopField = 8, + SubpicBottomField = 9, + TopFieldChromaBottom = 10, + BottomFieldChromaTop = 11, + SubpicTopFieldChromaBottom = 12, + SubpicBottomFieldChromaTop = 13, }; -enum class DXVAHD_DEINTERLACE_MODE_PRIVATE : u64 { - WEAVE = 0, - BOB_FIELD = 1, - BOB = 2, - NEWBOB = 3, - DISI1 = 4, - WEAVE_LUMA_BOB_FIELD_CHROMA = 5, - MAX = 0xF, +enum class DxvhadDeinterlaceModePrivate : u64 { + Weave = 0, + BobField = 1, + Bob = 2, + Newbob = 3, + Disi1 = 4, + WeaveLumaBobFieldChroma = 5, + Max = 0xF, }; -enum class BLK_KIND { - PITCH = 0, - GENERIC_16Bx2 = 1, +enum class BlkKind { + Pitch = 0, + Generic_16Bx2 = 1, // These are unsupported in the vic - BL_NAIVE = 2, - BL_KEPLER_XBAR_RAW = 3, - VP2_TILED = 15, + BlNaive = 2, + BlKeplerXbarRaw = 3, + Vp2Tiled = 15, }; -enum class BLEND_SRCFACTC : u32 { +enum class BlendSrcFactC : u32 { K1 = 0, K1_TIMES_DST = 1, NEG_K1_TIMES_DST = 2, @@ -191,7 +188,7 @@ enum class BLEND_SRCFACTC : u32 { ZERO = 4, }; -enum class BLEND_DSTFACTC : u32 { +enum class BlendDstFactC : u32 { K1 = 0, K2 = 1, K1_TIMES_DST = 2, @@ -201,7 +198,7 @@ enum class BLEND_DSTFACTC : u32 { ONE = 6, }; -enum class BLEND_SRCFACTA : u32 { +enum class BlendSrcFactA : u32 { K1 = 0, K2 = 1, NEG_K1_TIMES_DST = 2, @@ -209,7 +206,7 @@ enum class BLEND_SRCFACTA : u32 { MAX = 7, }; -enum class BLEND_DSTFACTA : u32 { +enum class BlendDstFactA : u32 { K2 = 0, NEG_K1_TIMES_SRC = 1, ZERO = 2, @@ -232,7 +229,7 @@ static_assert(sizeof(PipeConfig) == 0x10, "PipeConfig has the wrong size!"); struct OutputConfig { union { - BitField<0, 3, DXVAHD_ALPHA_FILL_MODE> alpha_fill_mode; + BitField<0, 3, DxvhadAlphaFillMode> alpha_fill_mode; BitField<3, 3, u64> alpha_fill_slot; BitField<6, 10, u64> background_a; BitField<16, 10, u64> background_r; @@ -265,7 +262,7 @@ struct OutputSurfaceConfig { BitField<0, 7, VideoPixelFormat> out_pixel_format; BitField<7, 2, u32> out_chroma_loc_horiz; BitField<9, 2, u32> out_chroma_loc_vert; - BitField<11, 4, BLK_KIND> out_block_kind; + BitField<11, 4, BlkKind> out_block_kind; BitField<15, 4, u32> out_block_height; // in gobs, log2 BitField<19, 3, u32> reserved0; BitField<22, 10, u32> reserved1; @@ -365,7 +362,7 @@ struct SlotConfig { BitField<14, 1, u64> prev_prev_motion_field_enable; BitField<15, 1, u64> combined_motion_field_enable; - BitField<16, 4, DXVAHD_FRAME_FORMAT> frame_format; + BitField<16, 4, DxvhadFrameFormat> frame_format; BitField<20, 2, u64> filter_length_y; // 0: 1-tap, 1: 2-tap, 2: 5-tap, 3: 10-tap BitField<22, 2, u64> filter_length_x; BitField<24, 12, u64> panoramic; @@ -377,7 +374,7 @@ struct SlotConfig { BitField<10, 10, u64> filter_detail; BitField<20, 10, u64> chroma_noise; BitField<30, 10, u64> chroma_detail; - BitField<40, 4, DXVAHD_DEINTERLACE_MODE_PRIVATE> deinterlace_mode; + BitField<40, 4, DxvhadDeinterlaceModePrivate> deinterlace_mode; BitField<44, 3, u64> motion_accumulation_weight; BitField<47, 11, u64> noise_iir; BitField<58, 4, u64> light_level; @@ -484,13 +481,13 @@ struct BlendingSlotStruct { BitField<26, 6, u32> reserved1; }; union { - BitField<0, 3, BLEND_SRCFACTC> src_factor_color_match_select; + BitField<0, 3, BlendSrcFactC> src_factor_color_match_select; BitField<3, 1, u32> reserved2; - BitField<4, 3, BLEND_DSTFACTC> dst_factor_color_match_select; + BitField<4, 3, BlendDstFactC> dst_factor_color_match_select; BitField<7, 1, u32> reserved3; - BitField<8, 3, BLEND_SRCFACTA> src_factor_a_match_select; + BitField<8, 3, BlendSrcFactA> src_factor_a_match_select; BitField<11, 1, u32> reserved4; - BitField<12, 3, BLEND_DSTFACTA> dst_factor_a_match_select; + BitField<12, 3, BlendDstFactA> dst_factor_a_match_select; BitField<15, 1, u32> reserved5; BitField<16, 4, u32> reserved6; BitField<20, 4, u32> reserved7; @@ -624,8 +621,8 @@ private: VicRegisters regs{}; Common::ScratchBuffer swizzle_scratch; - Common::ScratchBuffer output_surface; - Common::ScratchBuffer slot_surface; + Common::ScratchBuffer output_surface; + Common::ScratchBuffer slot_surface; Common::ScratchBuffer luma_scratch; Common::ScratchBuffer chroma_scratch; diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index 60b399ccba..9e8d76b104 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -76,6 +76,11 @@ set(SHADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/vulkan_quad_indexed.comp ${CMAKE_CURRENT_SOURCE_DIR}/vulkan_turbo_mode.comp ${CMAKE_CURRENT_SOURCE_DIR}/vulkan_uint8.comp + + # Snapdragon Game Super Resolution + ${CMAKE_CURRENT_SOURCE_DIR}/sgsr1_shader.vert + ${CMAKE_CURRENT_SOURCE_DIR}/sgsr1_shader_mobile.frag + ${CMAKE_CURRENT_SOURCE_DIR}/sgsr1_shader_mobile_edge_direction.frag ) if (PLATFORM_HAIKU) @@ -90,7 +95,7 @@ if ("${GLSLANGVALIDATOR}" STREQUAL "GLSLANGVALIDATOR-NOTFOUND") message(FATAL_ERROR "Required program `glslangValidator` not found.") endif() -set(GLSL_FLAGS "") +set(GLSL_FLAGS "-DUseUniformBlock=1") set(SPIR_V_VERSION "spirv1.3") set(QUIET_FLAG "--quiet") diff --git a/src/video_core/host_shaders/sgsr1_shader.vert b/src/video_core/host_shaders/sgsr1_shader.vert new file mode 100644 index 0000000000..0d2a935a9d --- /dev/null +++ b/src/video_core/host_shaders/sgsr1_shader.vert @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#version 450 + +layout(push_constant) uniform constants { + vec2 scale; + vec2 size; + vec2 resize_factor; + float edge_sharpness; +}; +layout(location = 0) out highp vec2 texcoord; + +void main() { + float x = float((gl_VertexIndex & 1) << 2); + float y = float((gl_VertexIndex & 2) << 1); + gl_Position = vec4(x - 1.0f, y - 1.0f, 0.0, 1.0f) * vec4(sign(resize_factor), 1.f, 1.f); + texcoord = vec2(x, y) * abs(resize_factor) * 0.5; +} diff --git a/src/video_core/host_shaders/sgsr1_shader_mobile.frag b/src/video_core/host_shaders/sgsr1_shader_mobile.frag new file mode 100644 index 0000000000..2e62d60af3 --- /dev/null +++ b/src/video_core/host_shaders/sgsr1_shader_mobile.frag @@ -0,0 +1,82 @@ +// SPDX-FileCopyrightText: Copyright (c) 2025, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause + +#version 460 core + +precision highp float; +precision highp int; + +// Operation modes: RGBA -> 1, RGBY -> 3, LERP -> 4 +#define OPERATION_MODE 1 +#define EDGE_THRESHOLD (8.0 / 255.0) + +layout(push_constant) uniform constants { + vec2 scale; + vec2 size; + vec2 resize_factor; + float edge_sharpness; +}; +layout(set = 0, binding = 0) uniform sampler2D sampler0; +layout(location=0) in vec2 texcoord; +layout(location=0) out vec4 frag_color; + +vec4 weightY(vec4 dx, vec4 dy, vec4 std) { + vec4 x = ((dx * dx) + (dy * dy)) * 0.55f + std; + return (x - 1.f) * (x - 4.f) * 3.8125f; // approx. of (x - 1) * (x - 4)^3 +} + +void main() { + vec4 color = textureLod(sampler0, texcoord.xy, 0.0f); + // image coord + vec2 icoord = (texcoord * size + vec2(-0.5f, 0.5f)); + vec2 icoord_pixel = floor(icoord); + vec2 coord = icoord_pixel * scale; + vec2 pl = icoord - icoord_pixel; + // left: 0, right: 1, upDown: 2 + mat3x4 dg = mat3x4( + textureGather(sampler0, coord, 1), + textureGather(sampler0, coord + vec2(2.f * scale.x, 0.0f), 1), + vec4( + textureGather(sampler0, coord + vec2(scale.x, -scale.y), 1).wz, + textureGather(sampler0, coord + vec2(scale.x, +scale.y), 1).yx + ) + ); + float edgeVote = abs(dg[0].z - dg[0].y) + abs(color.y - dg[0].y) + abs(color.y - dg[0].z); + if (edgeVote > EDGE_THRESHOLD) { + float mean = (dg[0].y + dg[0].z + dg[1].x + dg[1].w) * 0.25f; + dg = dg - mean; + vec4 sum = abs(dg[0]) + abs(dg[1]) + abs(dg[2]); + float std = 2.181818f / (sum.x + sum.y + sum.z + sum.w); + mat2x4 w = mat2x4( + weightY( + pl.xxxx + vec4(+1.0f, +0.0f, +0.0f, +1.0f), + pl.yyyy + vec4(-1.0f, -1.0f, +0.0f, +0.0f), + clamp(abs(dg[0]) * std, 0.0f, 1.0f) + ) + weightY( + pl.xxxx + vec4(-1.0f, -2.0f, -2.0f, -1.0f), + pl.yyyy + vec4(-1.0f, -1.0f, +0.0f, +0.0f), + clamp(abs(dg[1]) * std, 0.0f, 1.0f) + ) + weightY( + pl.xxxx + vec4(+0.0f, -1.0f, -1.0f, +0.0f), + pl.yyyy + vec4(+1.0f, +1.0f, -2.0f, -2.0f), + clamp(abs(dg[2]) * std, 0.0f, 1.0f) + ), + dg[0] + dg[1] + dg[2] + ); + // compute final y with bounds + vec2 yb = vec2( + min(min(dg[0].y, dg[0].z), min(dg[1].x, dg[1].w)), // min + max(max(dg[0].y, dg[0].z), max(dg[1].x, dg[1].w)) // max + ); + vec2 fvy = vec2( + w[0].x + w[0].y + w[0].z + w[0].w, + w[1].x + w[1].y + w[1].z + w[1].w + ); + float fy = clamp((fvy.y / fvy.x) * edge_sharpness, yb[0], yb[1]); + // Smooth high contrast input + float dy = clamp(fy - color.y + mean, -23.0f / 255.0f, 23.0f / 255.0f); + color = clamp(color + dy, 0.0f, 1.0f); + } + color.w = 1.0f; //assume alpha channel is not used + frag_color.xyzw = color; +} \ No newline at end of file diff --git a/src/video_core/host_shaders/sgsr1_shader_mobile_edge_direction.frag b/src/video_core/host_shaders/sgsr1_shader_mobile_edge_direction.frag new file mode 100644 index 0000000000..10a8ff8e00 --- /dev/null +++ b/src/video_core/host_shaders/sgsr1_shader_mobile_edge_direction.frag @@ -0,0 +1,115 @@ +// SPDX-FileCopyrightText: Copyright (c) 2025, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause + +#version 460 core + +//precision float; +//precision int; + +// Operation modes: RGBA -> 1, RGBY -> 3, LERP -> 4 +#define OperationMode 1 +#define EdgeThreshold 8.0/255.0 + +layout( push_constant ) uniform constants { + vec4 ViewportInfo[1]; + vec2 ResizeFactor; + float EdgeSharpness; +}; +layout(set = 0, binding = 0) uniform sampler2D ps0; +layout(location=0) in vec2 in_TEXCOORD0; +layout(location=0) out vec4 out_Target0; + +float fastLanczos2(float x) { + float wA = x-4.0; + float wB = x*wA-wA; + wA *= wA; + return wB*wA; +} + +vec2 weightY(float dx, float dy, float c, vec3 data) { + float std = data.x; + vec2 dir = data.yz; + float edgeDis = ((dx*dir.y)+(dy*dir.x)); + float x = (((dx*dx)+(dy*dy))+((edgeDis*edgeDis)*((clamp(((c*c)*std),0.0,1.0)*0.7)+-1.0))); + float w = fastLanczos2(x); + return vec2(w, w * c); +} + +vec2 edgeDirection(vec4 left, vec4 right) { + vec2 dir; + float RxLz = (right.x + (-left.z)); + float RwLy = (right.w + (-left.y)); + vec2 delta; + delta.x = (RxLz + RwLy); + delta.y = (RxLz + (-RwLy)); + float lengthInv = inversesqrt((delta.x * delta.x+ 3.075740e-05) + (delta.y * delta.y)); + dir.x = (delta.x * lengthInv); + dir.y = (delta.y * lengthInv); + return dir; +} + +void main() { + vec4 color; + if(OperationMode == 1) + color.xyz = textureLod(ps0, in_TEXCOORD0.xy, 0.0).xyz; + else + color.xyzw = textureLod(ps0, in_TEXCOORD0.xy, 0.0).xyzw; + + if ( OperationMode!=4) { + vec2 imgCoord = ((in_TEXCOORD0.xy*ViewportInfo[0].zw)+vec2(-0.5,0.5)); + vec2 imgCoordPixel = floor(imgCoord); + vec2 coord = (imgCoordPixel*ViewportInfo[0].xy); + vec2 pl = imgCoord - imgCoordPixel; + vec4 left = textureGather(ps0, coord, OperationMode); + float edgeVote = abs(left.z - left.y) + abs(color[OperationMode] - left.y) + abs(color[OperationMode] - left.z) ; + if(edgeVote > EdgeThreshold) { + coord.x += ViewportInfo[0].x; + + vec2 IR_highp_vec2_0 = coord + vec2(ViewportInfo[0].x, 0.0); + vec4 right = textureGather(ps0, IR_highp_vec2_0, OperationMode); + vec4 upDown; + vec2 IR_highp_vec2_1 = coord + vec2(0.0, -ViewportInfo[0].y); + upDown.xy = textureGather(ps0, IR_highp_vec2_1, OperationMode).wz; + vec2 IR_highp_vec2_2 = coord + vec2(0.0, ViewportInfo[0].y); + upDown.zw = textureGather(ps0, IR_highp_vec2_2, OperationMode).yx; + + float mean = (left.y+left.z+right.x+right.w)*0.25; + left = left - vec4(mean); + right = right - vec4(mean); + upDown = upDown - vec4(mean); + color.w =color[OperationMode] - mean; + + float sum = (((((abs(left.x)+abs(left.y))+abs(left.z))+abs(left.w))+(((abs(right.x)+abs(right.y))+abs(right.z))+abs(right.w)))+(((abs(upDown.x)+abs(upDown.y))+abs(upDown.z))+abs(upDown.w))); + float sumMean = 1.014185e+01/sum; + float std = (sumMean*sumMean); + + vec3 data = vec3(std, edgeDirection(left, right)); + vec2 aWY = weightY(pl.x, pl.y+1.0, upDown.x,data); + aWY += weightY(pl.x-1.0, pl.y+1.0, upDown.y,data); + aWY += weightY(pl.x-1.0, pl.y-2.0, upDown.z,data); + aWY += weightY(pl.x, pl.y-2.0, upDown.w,data); + aWY += weightY(pl.x+1.0, pl.y-1.0, left.x,data); + aWY += weightY(pl.x, pl.y-1.0, left.y,data); + aWY += weightY(pl.x, pl.y, left.z,data); + aWY += weightY(pl.x+1.0, pl.y, left.w,data); + aWY += weightY(pl.x-1.0, pl.y-1.0, right.x,data); + aWY += weightY(pl.x-2.0, pl.y-1.0, right.y,data); + aWY += weightY(pl.x-2.0, pl.y, right.z,data); + aWY += weightY(pl.x-1.0, pl.y, right.w,data); + + float finalY = aWY.y/aWY.x; + float maxY = max(max(left.y,left.z),max(right.x,right.w)); + float minY = min(min(left.y,left.z),min(right.x,right.w)); + float deltaY = clamp(EdgeSharpness*finalY, minY, maxY) -color.w; + + //smooth high contrast input + deltaY = clamp(deltaY, -23.0 / 255.0, 23.0 / 255.0); + + color.x = clamp((color.x+deltaY),0.0,1.0); + color.y = clamp((color.y+deltaY),0.0,1.0); + color.z = clamp((color.z+deltaY),0.0,1.0); + } + } + color.w = 1.0; //assume alpha channel is not used + out_Target0.xyzw = color; +} \ No newline at end of file diff --git a/src/video_core/macro.cpp b/src/video_core/macro.cpp index 606162ba8d..1632d03664 100644 --- a/src/video_core/macro.cpp +++ b/src/video_core/macro.cpp @@ -11,17 +11,9 @@ #include #include + #ifdef ARCHITECTURE_x86_64 -// xbyak hates human beings -#ifdef __GNUC__ -#pragma GCC diagnostic ignored "-Wconversion" -#pragma GCC diagnostic ignored "-Wshadow" -#endif -#ifdef __clang__ -#pragma clang diagnostic ignored "-Wconversion" -#pragma clang diagnostic ignored "-Wshadow" -#endif -#include +#include "common/x64/xbyak.h" #endif #include "common/assert.h" @@ -39,10 +31,6 @@ #include "common/assert.h" #include "common/bit_field.h" #include "common/logging.h" -#ifdef ARCHITECTURE_x86_64 -#include "common/x64/xbyak_abi.h" -#include "common/x64/xbyak_util.h" -#endif #include "video_core/engines/maxwell_3d.h" namespace Tegra { @@ -76,10 +64,10 @@ bool IsTopologySafe(Maxwell3D::Regs::PrimitiveTopology topology) { } // Anonymous namespace -void HLE_DrawArraysIndirect::Execute(Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method) { +void HLE_DrawArraysIndirect::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method) { auto topology = static_cast(parameters[0]); if (!maxwell3d.AnyParametersDirty() || !IsTopologySafe(topology)) { - Fallback(maxwell3d, parameters); + Fallback(system, maxwell3d, parameters); return; } @@ -105,7 +93,7 @@ void HLE_DrawArraysIndirect::Execute(Engines::Maxwell3D& maxwell3d, std::span parameters) { +void HLE_DrawArraysIndirect::Fallback(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span parameters) { SCOPE_EXIT { if (extended) { maxwell3d.engine_state = Maxwell3D::EngineHint::None; @@ -135,10 +123,10 @@ void HLE_DrawArraysIndirect::Fallback(Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method) { +void HLE_DrawIndexedIndirect::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method) { auto topology = static_cast(parameters[0]); if (!maxwell3d.AnyParametersDirty() || !IsTopologySafe(topology)) { - Fallback(maxwell3d, parameters); + Fallback(system, maxwell3d, parameters); return; } @@ -173,7 +161,7 @@ void HLE_DrawIndexedIndirect::Execute(Engines::Maxwell3D& maxwell3d, std::span parameters) { +void HLE_DrawIndexedIndirect::Fallback(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span parameters) { maxwell3d.RefreshParameters(); const u32 instance_count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]); const u32 element_base = parameters[4]; @@ -196,7 +184,7 @@ void HLE_DrawIndexedIndirect::Fallback(Engines::Maxwell3D& maxwell3d, std::span< maxwell3d.replace_table.clear(); } } -void HLE_MultiLayerClear::Execute(Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method) { +void HLE_MultiLayerClear::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method) { maxwell3d.RefreshParameters(); ASSERT(parameters.size() == 1); @@ -208,47 +196,44 @@ void HLE_MultiLayerClear::Execute(Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method) { +void HLE_MultiDrawIndexedIndirectCount::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method) { const auto topology = Maxwell3D::Regs::PrimitiveTopology(parameters[2]); - if (!IsTopologySafe(topology)) { - Fallback(maxwell3d, parameters); - return; + if (IsTopologySafe(topology)) { + const u32 start_indirect = parameters[0]; + const u32 end_indirect = parameters[1]; + if (start_indirect >= end_indirect) { + // Nothing to do. + return; + } + const u32 padding = parameters[3]; // padding is in words + // size of each indirect segment + const u32 indirect_words = 5 + padding; + const u32 stride = indirect_words * sizeof(u32); + const std::size_t draw_count = end_indirect - start_indirect; + const u32 estimate = u32(maxwell3d.EstimateIndexBufferSize()); + maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; + auto& params = maxwell3d.draw_manager.indirect_state; + params.is_byte_count = false; + params.is_indexed = true; + params.include_count = true; + params.count_start_address = maxwell3d.GetMacroAddress(4); + params.indirect_start_address = maxwell3d.GetMacroAddress(5); + params.buffer_size = stride * draw_count; + params.max_draw_counts = draw_count; + params.stride = stride; + maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; + maxwell3d.engine_state = Maxwell3D::EngineHint::OnHLEMacro; + maxwell3d.SetHLEReplacementAttributeType(0, 0x640, Maxwell3D::HLEReplacementAttributeType::BaseVertex); + maxwell3d.SetHLEReplacementAttributeType(0, 0x644, Maxwell3D::HLEReplacementAttributeType::BaseInstance); + maxwell3d.SetHLEReplacementAttributeType(0, 0x648, Maxwell3D::HLEReplacementAttributeType::DrawID); + maxwell3d.draw_manager.DrawIndexedIndirect(maxwell3d, topology, 0, estimate); + maxwell3d.engine_state = Maxwell3D::EngineHint::None; + maxwell3d.replace_table.clear(); + } else { + Fallback(system, maxwell3d, parameters); } - - const u32 start_indirect = parameters[0]; - const u32 end_indirect = parameters[1]; - if (start_indirect >= end_indirect) { - // Nothing to do. - return; - } - - const u32 padding = parameters[3]; // padding is in words - - // size of each indirect segment - const u32 indirect_words = 5 + padding; - const u32 stride = indirect_words * sizeof(u32); - const std::size_t draw_count = end_indirect - start_indirect; - const u32 estimate = static_cast(maxwell3d.EstimateIndexBufferSize()); - maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; - auto& params = maxwell3d.draw_manager.indirect_state; - params.is_byte_count = false; - params.is_indexed = true; - params.include_count = true; - params.count_start_address = maxwell3d.GetMacroAddress(4); - params.indirect_start_address = maxwell3d.GetMacroAddress(5); - params.buffer_size = stride * draw_count; - params.max_draw_counts = draw_count; - params.stride = stride; - maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; - maxwell3d.engine_state = Maxwell3D::EngineHint::OnHLEMacro; - maxwell3d.SetHLEReplacementAttributeType(0, 0x640, Maxwell3D::HLEReplacementAttributeType::BaseVertex); - maxwell3d.SetHLEReplacementAttributeType(0, 0x644, Maxwell3D::HLEReplacementAttributeType::BaseInstance); - maxwell3d.SetHLEReplacementAttributeType(0, 0x648, Maxwell3D::HLEReplacementAttributeType::DrawID); - maxwell3d.draw_manager.DrawIndexedIndirect(maxwell3d, topology, 0, estimate); - maxwell3d.engine_state = Maxwell3D::EngineHint::None; - maxwell3d.replace_table.clear(); } -void HLE_MultiDrawIndexedIndirectCount::Fallback(Engines::Maxwell3D& maxwell3d, std::span parameters) { +void HLE_MultiDrawIndexedIndirectCount::Fallback(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span parameters) { SCOPE_EXIT { // Clean everything. maxwell3d.regs.vertex_id_base = 0x0; @@ -262,7 +247,7 @@ void HLE_MultiDrawIndexedIndirectCount::Fallback(Engines::Maxwell3D& maxwell3d, // Nothing to do. return; } - const auto topology = static_cast(parameters[2]); + const auto topology = Maxwell3D::Regs::PrimitiveTopology(parameters[2]); const u32 padding = parameters[3]; const std::size_t max_draws = parameters[4]; const u32 indirect_words = 5 + padding; @@ -277,41 +262,41 @@ void HLE_MultiDrawIndexedIndirectCount::Fallback(Engines::Maxwell3D& maxwell3d, maxwell3d.engine_state = Maxwell3D::EngineHint::OnHLEMacro; maxwell3d.SetHLEReplacementAttributeType(0, 0x640, Maxwell3D::HLEReplacementAttributeType::BaseVertex); maxwell3d.SetHLEReplacementAttributeType(0, 0x644, Maxwell3D::HLEReplacementAttributeType::BaseInstance); - maxwell3d.CallMethod(0x8e3, 0x648, true); - maxwell3d.CallMethod(0x8e4, static_cast(index), true); + maxwell3d.CallMethod(system, 0x8e3, 0x648, true); + maxwell3d.CallMethod(system, 0x8e4, u32(index), true); maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; maxwell3d.draw_manager.DrawIndex(maxwell3d, topology, parameters[base + 2], parameters[base], base_vertex, base_instance, parameters[base + 1]); } } -void HLE_DrawIndirectByteCount::Execute(Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method) { +void HLE_DrawIndirectByteCount::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method) { const bool force = maxwell3d.Rasterizer().HasDrawTransformFeedback(); - if (!force) { - Fallback(maxwell3d, parameters); - return; + if (force) { + auto topology = Maxwell3D::Regs::PrimitiveTopology(parameters[0] & 0xFFFFU); + auto& params = maxwell3d.draw_manager.indirect_state; + params.is_byte_count = true; + params.is_indexed = false; + params.include_count = false; + params.count_start_address = 0; + params.indirect_start_address = maxwell3d.GetMacroAddress(2); + params.buffer_size = 4; + params.max_draw_counts = 1; + params.stride = parameters[1]; + maxwell3d.regs.draw.begin = parameters[0]; + maxwell3d.regs.draw_auto_stride = parameters[1]; + maxwell3d.regs.draw_auto_byte_count = parameters[2]; + maxwell3d.draw_manager.DrawArrayIndirect(maxwell3d, topology); + } else { + Fallback(system, maxwell3d, parameters); } - auto topology = Maxwell3D::Regs::PrimitiveTopology(parameters[0] & 0xFFFFU); - auto& params = maxwell3d.draw_manager.indirect_state; - params.is_byte_count = true; - params.is_indexed = false; - params.include_count = false; - params.count_start_address = 0; - params.indirect_start_address = maxwell3d.GetMacroAddress(2); - params.buffer_size = 4; - params.max_draw_counts = 1; - params.stride = parameters[1]; - maxwell3d.regs.draw.begin = parameters[0]; - maxwell3d.regs.draw_auto_stride = parameters[1]; - maxwell3d.regs.draw_auto_byte_count = parameters[2]; - maxwell3d.draw_manager.DrawArrayIndirect(maxwell3d, topology); } -void HLE_DrawIndirectByteCount::Fallback(Engines::Maxwell3D& maxwell3d, std::span parameters) { +void HLE_DrawIndirectByteCount::Fallback(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span parameters) { maxwell3d.RefreshParameters(); maxwell3d.regs.draw.begin = parameters[0]; maxwell3d.regs.draw_auto_stride = parameters[1]; maxwell3d.regs.draw_auto_byte_count = parameters[2]; maxwell3d.draw_manager.DrawArray(maxwell3d, maxwell3d.regs.draw.topology, 0, maxwell3d.regs.draw_auto_byte_count / maxwell3d.regs.draw_auto_stride, 0, 1); } -void HLE_C713C83D8F63CCF3::Execute(Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method) { +void HLE_C713C83D8F63CCF3::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method) { maxwell3d.RefreshParameters(); const u32 offset = (parameters[0] & 0x3FFFFFFF) << 2; const u32 address = maxwell3d.regs.shadow_scratch[24]; @@ -321,7 +306,7 @@ void HLE_C713C83D8F63CCF3::Execute(Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method) { +void HLE_D7333D26E0A93EDE::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method) { maxwell3d.RefreshParameters(); const size_t index = parameters[0]; const u32 address = maxwell3d.regs.shadow_scratch[42 + index]; @@ -331,7 +316,7 @@ void HLE_D7333D26E0A93EDE::Execute(Engines::Maxwell3D& maxwell3d, std::span> 24) & 0xFF; const_buffer.address_low = address << 8; } -void HLE_BindShader::Execute(Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method) { +void HLE_BindShader::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method) { maxwell3d.RefreshParameters(); auto& regs = maxwell3d.regs; const u32 index = parameters[0]; @@ -355,7 +340,7 @@ void HLE_BindShader::Execute(Engines::Maxwell3D& maxwell3d, std::span bind_group.raw_config = 0x11; maxwell3d.ProcessCBBind(bind_group_id); } -void HLE_SetRasterBoundingBox::Execute(Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method) { +void HLE_SetRasterBoundingBox::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method) { maxwell3d.RefreshParameters(); const u32 raster_mode = parameters[0]; auto& regs = maxwell3d.regs; @@ -364,7 +349,7 @@ void HLE_SetRasterBoundingBox::Execute(Engines::Maxwell3D& maxwell3d, std::span< regs.raster_bounding_box.raw = raster_mode & 0xFFFFF00F; regs.raster_bounding_box.pad.Assign(scratch_data & raster_enabled); } -void HLE_ClearConstBuffer::Execute(Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method) { +void HLE_ClearConstBuffer::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method) { static constexpr std::array zeroes{}; //must be bigger than either 7000 or 5F00 maxwell3d.RefreshParameters(); auto& regs = maxwell3d.regs; @@ -374,7 +359,7 @@ void HLE_ClearConstBuffer::Execute(Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method) { +void HLE_ClearMemory::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method) { maxwell3d.RefreshParameters(); const u32 needed_memory = parameters[2] / sizeof(u32); if (needed_memory > zero_memory.size()) { @@ -385,10 +370,10 @@ void HLE_ClearMemory::Execute(Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method) { +void HLE_TransformFeedbackSetup::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method) { maxwell3d.RefreshParameters(); auto& regs = maxwell3d.regs; regs.transform_feedback_enabled = 1; @@ -400,8 +385,8 @@ void HLE_TransformFeedbackSetup::Execute(Engines::Maxwell3D& maxwell3d, std::spa regs.upload.line_count = 1; regs.upload.dest.address_high = parameters[0]; regs.upload.dest.address_low = parameters[1]; - maxwell3d.CallMethod(size_t(MAXWELL3D_REG_INDEX(launch_dma)), 0x1011, true); - maxwell3d.CallMethod(size_t(MAXWELL3D_REG_INDEX(inline_data)), regs.transform_feedback.controls[0].stride, true); + maxwell3d.CallMethod(system, size_t(MAXWELL3D_REG_INDEX(launch_dma)), 0x1011, true); + maxwell3d.CallMethod(system, size_t(MAXWELL3D_REG_INDEX(inline_data)), regs.transform_feedback.controls[0].stride, true); maxwell3d.Rasterizer().RegisterTransformFeedback(regs.upload.dest.Address()); } @@ -441,7 +426,7 @@ void HLE_TransformFeedbackSetup::Execute(Engines::Maxwell3D& maxwell3d, std::spa } } -void MacroInterpreterImpl::Execute(Engines::Maxwell3D& maxwell3d, std::span params, u32 method) { +void MacroInterpreterImpl::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span params, u32 method) { Reset(); registers[1] = params[0]; @@ -451,7 +436,7 @@ void MacroInterpreterImpl::Execute(Engines::Maxwell3D& maxwell3d, std::span> opcode.bf_src_bit) & opcode.GetBitfieldMask(); dst &= ~(opcode.GetBitfieldMask() << opcode.bf_dst_bit); dst |= src << opcode.bf_dst_bit; - ProcessResult(maxwell3d, opcode.result_operation, opcode.dst, dst); + ProcessResult(system, maxwell3d, opcode.result_operation, opcode.dst, dst); break; } case Macro::Operation::ExtractShiftLeftImmediate: { @@ -513,7 +498,7 @@ bool MacroInterpreterImpl::Step(Engines::Maxwell3D& maxwell3d, bool is_delay_slo u32 result = ((src >> dst) & opcode.GetBitfieldMask()) << opcode.bf_dst_bit; - ProcessResult(maxwell3d, opcode.result_operation, opcode.dst, result); + ProcessResult(system, maxwell3d, opcode.result_operation, opcode.dst, result); break; } case Macro::Operation::ExtractShiftLeftRegister: { @@ -522,12 +507,12 @@ bool MacroInterpreterImpl::Step(Engines::Maxwell3D& maxwell3d, bool is_delay_slo u32 result = ((src >> opcode.bf_src_bit) & opcode.GetBitfieldMask()) << dst; - ProcessResult(maxwell3d, opcode.result_operation, opcode.dst, result); + ProcessResult(system, maxwell3d, opcode.result_operation, opcode.dst, result); break; } case Macro::Operation::Read: { u32 result = Read(maxwell3d, GetRegister(opcode.src_a) + opcode.immediate); - ProcessResult(maxwell3d, opcode.result_operation, opcode.dst, result); + ProcessResult(system, maxwell3d, opcode.result_operation, opcode.dst, result); break; } case Macro::Operation::Branch: { @@ -543,7 +528,7 @@ bool MacroInterpreterImpl::Step(Engines::Maxwell3D& maxwell3d, bool is_delay_slo delayed_pc = base_address + opcode.GetBranchTarget(); // Execute one more instruction due to the delay slot. - return Step(maxwell3d, true); + return Step(system, maxwell3d, true); } break; } @@ -556,7 +541,7 @@ bool MacroInterpreterImpl::Step(Engines::Maxwell3D& maxwell3d, bool is_delay_slo // cause an exit if it's executed inside a delay slot. if (opcode.is_exit && !is_delay_slot) { // Exit has a delay slot, execute the next instruction - Step(maxwell3d, true); + Step(system, maxwell3d, true); return false; } return true; @@ -603,7 +588,7 @@ u32 MacroInterpreterImpl::GetALUResult(Macro::ALUOperation operation, u32 src_a, } /// Performs the result operation on the input result and stores it in the specified register (if necessary). -void MacroInterpreterImpl::ProcessResult(Engines::Maxwell3D& maxwell3d, Macro::ResultOperation operation, u32 reg, u32 result) { +void MacroInterpreterImpl::ProcessResult(Core::System& system, Engines::Maxwell3D& maxwell3d, Macro::ResultOperation operation, u32 reg, u32 result) { switch (operation) { case Macro::ResultOperation::IgnoreAndFetch: // Fetch parameter and ignore result. @@ -621,12 +606,12 @@ void MacroInterpreterImpl::ProcessResult(Engines::Maxwell3D& maxwell3d, Macro::R case Macro::ResultOperation::FetchAndSend: // Fetch parameter and send result. SetRegister(reg, FetchParameter()); - Send(maxwell3d, result); + Send(system, maxwell3d, result); break; case Macro::ResultOperation::MoveAndSend: // Move and send result. SetRegister(reg, result); - Send(maxwell3d, result); + Send(system, maxwell3d, result); break; case Macro::ResultOperation::FetchAndSetMethod: // Fetch parameter and use result as Method Address. @@ -637,13 +622,13 @@ void MacroInterpreterImpl::ProcessResult(Engines::Maxwell3D& maxwell3d, Macro::R // Move result and use as Method Address, then fetch and send parameter. SetRegister(reg, result); SetMethodAddress(result); - Send(maxwell3d, FetchParameter()); + Send(system, maxwell3d, FetchParameter()); break; case Macro::ResultOperation::MoveAndSetMethodSend: // Move result and use as Method Address, then send bits 12:17 of result. SetRegister(reg, result); SetMethodAddress(result); - Send(maxwell3d, (result >> 12) & 0b111111); + Send(system, maxwell3d, (result >> 12) & 0b111111); break; default: UNIMPLEMENTED_MSG("Unimplemented result operation {}", operation); @@ -684,8 +669,8 @@ void MacroInterpreterImpl::SetRegister(u32 register_id, u32 value) { } /// Calls a GPU Engine method with the input parameter. -void MacroInterpreterImpl::Send(Engines::Maxwell3D& maxwell3d, u32 value) { - maxwell3d.CallMethod(method_address.address, value, true); +void MacroInterpreterImpl::Send(Core::System& system, Engines::Maxwell3D& maxwell3d, u32 value) { + maxwell3d.CallMethod(system, method_address.address, value, true); // Increment the method address by the method increment. method_address.address.Assign(method_address.address.Value() + method_address.increment.Value()); } @@ -736,34 +721,35 @@ static const auto default_cg_mode = nullptr; //Allow RWE #endif struct MacroJITx64Impl final : public Xbyak::CodeGenerator, public DynamicCachedMacro { - explicit MacroJITx64Impl(std::span code_) + explicit MacroJITx64Impl(Core::System& system, std::span code_) : Xbyak::CodeGenerator(MAX_CODE_SIZE, default_cg_mode) , code{code_} { - Compile(); + Compile(system); } - void Execute(Engines::Maxwell3D& maxwell3d, std::span parameters, u32 method) override; + void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span parameters, u32 method) override; - void Compile_ALU(Macro::Opcode opcode); - void Compile_AddImmediate(Macro::Opcode opcode); - void Compile_ExtractInsert(Macro::Opcode opcode); - void Compile_ExtractShiftLeftImmediate(Macro::Opcode opcode); - void Compile_ExtractShiftLeftRegister(Macro::Opcode opcode); - void Compile_Read(Macro::Opcode opcode); + void Compile_ALU(Core::System& system, Macro::Opcode opcode); + void Compile_AddImmediate(Core::System& system, Macro::Opcode opcode); + void Compile_ExtractInsert(Core::System& system, Macro::Opcode opcode); + void Compile_ExtractShiftLeftImmediate(Core::System& system, Macro::Opcode opcode); + void Compile_ExtractShiftLeftRegister(Core::System& system, Macro::Opcode opcode); + void Compile_Read(Core::System& system, Macro::Opcode opcode); void Compile_Branch(Macro::Opcode opcode); void Optimizer_ScanFlags(); - void Compile(); - bool Compile_NextInstruction(); + void Compile(Core::System& system); + bool Compile_NextInstruction(Core::System& system); Xbyak::Reg32 Compile_FetchParameter(); Xbyak::Reg32 Compile_GetRegister(u32 index, Xbyak::Reg32 dst); - void Compile_ProcessResult(Macro::ResultOperation operation, u32 reg); - void Compile_Send(Xbyak::Reg32 value); + void Compile_ProcessResult(Core::System& system, Macro::ResultOperation operation, u32 reg); + void Compile_Send(Core::System& system, Xbyak::Reg32 value); Macro::Opcode GetOpCode() const; struct JITState { - Engines::Maxwell3D* maxwell3d{}; + Engines::Maxwell3D* maxwell3d = nullptr; + Core::System* system = nullptr; std::array registers{}; u32 carry_flag{}; }; @@ -789,15 +775,16 @@ struct MacroJITx64Impl final : public Xbyak::CodeGenerator, public DynamicCached std::span code; }; -void MacroJITx64Impl::Execute(Engines::Maxwell3D& maxwell3d, std::span parameters, u32 method) { +void MacroJITx64Impl::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span parameters, u32 method) { ASSERT_OR_EXECUTE(program != nullptr, { return; }); JITState state{}; state.maxwell3d = &maxwell3d; + state.system = &system; state.registers = {}; program(&state, parameters.data(), parameters.data() + parameters.size()); } -void MacroJITx64Impl::Compile_ALU(Macro::Opcode opcode) { +void MacroJITx64Impl::Compile_ALU(Core::System& system, Macro::Opcode opcode) { const bool is_a_zero = opcode.src_a == 0; const bool is_b_zero = opcode.src_b == 0; const bool valid_operation = !is_a_zero && !is_b_zero; @@ -914,10 +901,10 @@ void MacroJITx64Impl::Compile_ALU(Macro::Opcode opcode) { UNIMPLEMENTED_MSG("Unimplemented ALU operation {}", opcode.alu_operation.Value()); break; } - Compile_ProcessResult(opcode.result_operation, opcode.dst); + Compile_ProcessResult(system, opcode.result_operation, opcode.dst); } -void MacroJITx64Impl::Compile_AddImmediate(Macro::Opcode opcode) { +void MacroJITx64Impl::Compile_AddImmediate(Core::System& system, Macro::Opcode opcode) { if (optimizer.skip_dummy_addimmediate) { // Games tend to use this as an exit instruction placeholder. It's to encode an instruction // without doing anything. In our case we can just not emit anything. @@ -952,10 +939,10 @@ void MacroJITx64Impl::Compile_AddImmediate(Macro::Opcode opcode) { sub(result, opcode.immediate * -1); } } - Compile_ProcessResult(opcode.result_operation, opcode.dst); + Compile_ProcessResult(system, opcode.result_operation, opcode.dst); } -void MacroJITx64Impl::Compile_ExtractInsert(Macro::Opcode opcode) { +void MacroJITx64Impl::Compile_ExtractInsert(Core::System& system, Macro::Opcode opcode) { auto dst = Compile_GetRegister(opcode.src_a, RESULT); auto src = Compile_GetRegister(opcode.src_b, eax); @@ -966,10 +953,10 @@ void MacroJITx64Impl::Compile_ExtractInsert(Macro::Opcode opcode) { shl(src, opcode.bf_dst_bit); or_(dst, src); - Compile_ProcessResult(opcode.result_operation, opcode.dst); + Compile_ProcessResult(system, opcode.result_operation, opcode.dst); } -void MacroJITx64Impl::Compile_ExtractShiftLeftImmediate(Macro::Opcode opcode) { +void MacroJITx64Impl::Compile_ExtractShiftLeftImmediate(Core::System& system, Macro::Opcode opcode) { const auto dst = Compile_GetRegister(opcode.src_a, ecx); const auto src = Compile_GetRegister(opcode.src_b, RESULT); @@ -977,10 +964,10 @@ void MacroJITx64Impl::Compile_ExtractShiftLeftImmediate(Macro::Opcode opcode) { and_(src, opcode.GetBitfieldMask()); shl(src, opcode.bf_dst_bit); - Compile_ProcessResult(opcode.result_operation, opcode.dst); + Compile_ProcessResult(system, opcode.result_operation, opcode.dst); } -void MacroJITx64Impl::Compile_ExtractShiftLeftRegister(Macro::Opcode opcode) { +void MacroJITx64Impl::Compile_ExtractShiftLeftRegister(Core::System& system, Macro::Opcode opcode) { const auto dst = Compile_GetRegister(opcode.src_a, ecx); const auto src = Compile_GetRegister(opcode.src_b, RESULT); @@ -988,10 +975,10 @@ void MacroJITx64Impl::Compile_ExtractShiftLeftRegister(Macro::Opcode opcode) { and_(src, opcode.GetBitfieldMask()); shl(src, dst.cvt8()); - Compile_ProcessResult(opcode.result_operation, opcode.dst); + Compile_ProcessResult(system, opcode.result_operation, opcode.dst); } -void MacroJITx64Impl::Compile_Read(Macro::Opcode opcode) { +void MacroJITx64Impl::Compile_Read(Core::System& system, Macro::Opcode opcode) { if (optimizer.zero_reg_skip && opcode.src_a == 0) { if (opcode.immediate == 0) { xor_(RESULT, RESULT); @@ -1017,23 +1004,21 @@ void MacroJITx64Impl::Compile_Read(Macro::Opcode opcode) { int3(); L(pass_range_check); } - mov(rax, qword[STATE]); - mov(RESULT, - dword[rax + offsetof(Engines::Maxwell3D, regs) + - offsetof(Engines::Maxwell3D::Regs, reg_array) + RESULT.cvt64() * sizeof(u32)]); - - Compile_ProcessResult(opcode.result_operation, opcode.dst); + mov(rax, qword[STATE + offsetof(JITState, maxwell3d)]); + mov(RESULT, dword[rax + offsetof(Engines::Maxwell3D, regs) + offsetof(Engines::Maxwell3D::Regs, reg_array) + RESULT.cvt64() * sizeof(u32)]); + Compile_ProcessResult(system, opcode.result_operation, opcode.dst); } -static void MacroJIT_SendThunk(Engines::Maxwell3D* maxwell3d, Macro::MethodAddress method_address, u32 value) { - maxwell3d->CallMethod(method_address.address, value, true); +static void MacroJIT_SendThunk(Core::System* system, Engines::Maxwell3D* maxwell3d, Macro::MethodAddress method_address, u32 value) { + maxwell3d->CallMethod(*system, method_address.address, value, true); } -void MacroJITx64Impl::Compile_Send(Xbyak::Reg32 value) { +void MacroJITx64Impl::Compile_Send(Core::System& system, Xbyak::Reg32 value) { Common::X64::ABI_PushRegistersAndAdjustStack(*this, PersistentCallerSavedRegs(), 0); - mov(Common::X64::ABI_PARAM1, qword[STATE]); - mov(Common::X64::ABI_PARAM2.cvt32(), METHOD_ADDRESS); - mov(Common::X64::ABI_PARAM3.cvt32(), value); + mov(Common::X64::ABI_PARAM1, qword[STATE + offsetof(JITState, system)]); + mov(Common::X64::ABI_PARAM2, qword[STATE + offsetof(JITState, maxwell3d)]); + mov(Common::X64::ABI_PARAM3.cvt32(), METHOD_ADDRESS); + mov(Common::X64::ABI_PARAM4.cvt32(), value); Common::X64::CallFarFunction(*this, &MacroJIT_SendThunk); Common::X64::ABI_PopRegistersAndAdjustStack(*this, PersistentCallerSavedRegs(), 0); @@ -1057,9 +1042,8 @@ void MacroJITx64Impl::Compile_Send(Xbyak::Reg32 value) { } void MacroJITx64Impl::Compile_Branch(Macro::Opcode opcode) { - ASSERT_MSG(!is_delay_slot, "Executing a branch in a delay slot is not valid"); - const s32 jump_address = - static_cast(pc) + static_cast(opcode.GetBranchTarget() / sizeof(s32)); + ASSERT(!is_delay_slot && "Executing a branch in a delay slot is not valid"); + const s32 jump_address = s32(pc) + s32(opcode.GetBranchTarget() / sizeof(s32)); Xbyak::Label end; auto value = Compile_GetRegister(opcode.src_a, eax); @@ -1128,7 +1112,9 @@ void MacroJITx64Impl::Optimizer_ScanFlags() { } } -void MacroJITx64Impl::Compile() { +void MacroJITx64Impl::Compile(Core::System& system) { + // Matching PROTECT_RE needed for W^X systems + setProtectMode(Xbyak::CodeArray::ProtectMode::PROTECT_RW); labels.fill(Xbyak::Label()); Common::X64::ABI_PushRegistersAndAdjustStack(*this, Common::X64::ABI_ALL_CALLEE_SAVED, 8); @@ -1168,7 +1154,7 @@ void MacroJITx64Impl::Compile() { next_opcode = {}; } pc = i; - Compile_NextInstruction(); + Compile_NextInstruction(system); } L(end_of_code); @@ -1176,10 +1162,11 @@ void MacroJITx64Impl::Compile() { Common::X64::ABI_PopRegistersAndAdjustStack(*this, Common::X64::ABI_ALL_CALLEE_SAVED, 8); ret(); ready(); + setProtectMode(Xbyak::CodeArray::ProtectMode::PROTECT_RE); program = getCode(); } -bool MacroJITx64Impl::Compile_NextInstruction() { +bool MacroJITx64Impl::Compile_NextInstruction(Core::System& system) { const auto opcode = GetOpCode(); if (labels[pc].getAddress()) { return false; @@ -1189,22 +1176,22 @@ bool MacroJITx64Impl::Compile_NextInstruction() { switch (opcode.operation) { case Macro::Operation::ALU: - Compile_ALU(opcode); + Compile_ALU(system, opcode); break; case Macro::Operation::AddImmediate: - Compile_AddImmediate(opcode); + Compile_AddImmediate(system, opcode); break; case Macro::Operation::ExtractInsert: - Compile_ExtractInsert(opcode); + Compile_ExtractInsert(system, opcode); break; case Macro::Operation::ExtractShiftLeftImmediate: - Compile_ExtractShiftLeftImmediate(opcode); + Compile_ExtractShiftLeftImmediate(system, opcode); break; case Macro::Operation::ExtractShiftLeftRegister: - Compile_ExtractShiftLeftRegister(opcode); + Compile_ExtractShiftLeftRegister(system, opcode); break; case Macro::Operation::Read: - Compile_Read(opcode); + Compile_Read(system, opcode); break; case Macro::Operation::Branch: Compile_Branch(opcode); @@ -1276,7 +1263,7 @@ Xbyak::Reg32 MacroJITx64Impl::Compile_GetRegister(u32 index, Xbyak::Reg32 dst) { return dst; } -void MacroJITx64Impl::Compile_ProcessResult(Macro::ResultOperation operation, u32 reg) { +void MacroJITx64Impl::Compile_ProcessResult(Core::System& system, Macro::ResultOperation operation, u32 reg) { const auto SetRegister = [this](u32 reg_index, const Xbyak::Reg32& result) { // Register 0 is supposed to always return 0. NOP is implemented as a store to the zero // register. @@ -1301,12 +1288,12 @@ void MacroJITx64Impl::Compile_ProcessResult(Macro::ResultOperation operation, u3 case Macro::ResultOperation::FetchAndSend: // Fetch parameter and send result. SetRegister(reg, Compile_FetchParameter()); - Compile_Send(RESULT); + Compile_Send(system, RESULT); break; case Macro::ResultOperation::MoveAndSend: // Move and send result. SetRegister(reg, RESULT); - Compile_Send(RESULT); + Compile_Send(system, RESULT); break; case Macro::ResultOperation::FetchAndSetMethod: // Fetch parameter and use result as Method Address. @@ -1317,7 +1304,7 @@ void MacroJITx64Impl::Compile_ProcessResult(Macro::ResultOperation operation, u3 // Move result and use as Method Address, then fetch and send parameter. SetRegister(reg, RESULT); SetMethodAddress(RESULT); - Compile_Send(Compile_FetchParameter()); + Compile_Send(system, Compile_FetchParameter()); break; case Macro::ResultOperation::MoveAndSetMethodSend: // Move result and use as Method Address, then send bits 12:17 of result. @@ -1325,7 +1312,7 @@ void MacroJITx64Impl::Compile_ProcessResult(Macro::ResultOperation operation, u3 SetMethodAddress(RESULT); shr(RESULT, 12); and_(RESULT, 0b111111); - Compile_Send(RESULT); + Compile_Send(system, RESULT); break; default: UNIMPLEMENTED_MSG("Unimplemented macro operation {}", operation); @@ -1341,22 +1328,14 @@ Macro::Opcode MacroJITx64Impl::GetOpCode() const { #endif static void Dump(u64 hash, std::span code, bool decompiled = false) { - const auto base_dir{Common::FS::GetEdenPath(Common::FS::EdenPath::DumpDir)}; - const auto macro_dir{base_dir / "macros"}; - if (!Common::FS::CreateDir(base_dir) || !Common::FS::CreateDir(macro_dir)) { - LOG_ERROR(Common_Filesystem, "Failed to create macro dump directories"); + const auto dump_dir{Common::FS::GetEdenPath(Common::FS::EdenPath::DumpDir)}; + if (!Common::FS::CreateDir(dump_dir)) { + LOG_ERROR(Common_Filesystem, "Failed to create dump directory"); return; } - auto name{macro_dir / fmt::format("{:016x}.macro", hash)}; - - if (decompiled) { - auto new_name{macro_dir / fmt::format("decompiled_{:016x}.macro", hash)}; - if (Common::FS::Exists(name)) { - (void)Common::FS::RenameFile(name, new_name); - return; - } - name = new_name; - } + const char* const variant_suffix = decompiled ? "jit" : "raw"; + const auto name{dump_dir / fmt::format("{:016x}_{:016x}_{}.macro", + Settings::GetCurrentProgramID(), hash, variant_suffix)}; std::fstream macro_file(name, std::ios::out | std::ios::binary); if (!macro_file) { @@ -1366,36 +1345,36 @@ static void Dump(u64 hash, std::span code, bool decompiled = false) { macro_file.write(reinterpret_cast(code.data()), code.size_bytes()); } -void MacroEngine::Execute(Engines::Maxwell3D& maxwell3d, u32 method, std::span parameters) { - auto const execute_variant = [&maxwell3d, ¶meters, method](AnyCachedMacro& acm) { +void MacroEngine::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, u32 method, std::span parameters) { + auto const execute_variant = [&system, &maxwell3d, ¶meters, method](AnyCachedMacro& acm) { if (auto a = std::get_if(&acm)) - a->Execute(maxwell3d, parameters, method); + return a->Execute(system, maxwell3d, parameters, method); if (auto a = std::get_if(&acm)) - a->Execute(maxwell3d, parameters, method); + return a->Execute(system, maxwell3d, parameters, method); if (auto a = std::get_if(&acm)) - a->Execute(maxwell3d, parameters, method); + return a->Execute(system, maxwell3d, parameters, method); if (auto a = std::get_if(&acm)) - a->Execute(maxwell3d, parameters, method); + return a->Execute(system, maxwell3d, parameters, method); if (auto a = std::get_if(&acm)) - a->Execute(maxwell3d, parameters, method); + return a->Execute(system, maxwell3d, parameters, method); if (auto a = std::get_if(&acm)) - a->Execute(maxwell3d, parameters, method); + return a->Execute(system, maxwell3d, parameters, method); if (auto a = std::get_if(&acm)) - a->Execute(maxwell3d, parameters, method); + return a->Execute(system, maxwell3d, parameters, method); if (auto a = std::get_if(&acm)) - a->Execute(maxwell3d, parameters, method); + return a->Execute(system, maxwell3d, parameters, method); if (auto a = std::get_if(&acm)) - a->Execute(maxwell3d, parameters, method); + return a->Execute(system, maxwell3d, parameters, method); if (auto a = std::get_if(&acm)) - a->Execute(maxwell3d, parameters, method); + return a->Execute(system, maxwell3d, parameters, method); if (auto a = std::get_if(&acm)) - a->Execute(maxwell3d, parameters, method); + return a->Execute(system, maxwell3d, parameters, method); if (auto a = std::get_if(&acm)) - a->Execute(maxwell3d, parameters, method); + return a->Execute(system, maxwell3d, parameters, method); if (auto a = std::get_if(&acm)) - a->Execute(maxwell3d, parameters, method); + return a->Execute(system, maxwell3d, parameters, method); if (auto a = std::get_if>(&acm)) - a->get()->Execute(maxwell3d, parameters, method); + return a->get()->Execute(system, maxwell3d, parameters, method); }; if (auto const it = macro_cache.find(method); it != macro_cache.end()) { auto& ci = it->second; @@ -1426,9 +1405,9 @@ void MacroEngine::Execute(Engines::Maxwell3D& maxwell3d, u32 method, std::spansecond); + ci.program = Compile(system, maxwell3d, macro_code->second); ci.hash = Common::HashValue(macro_code->second); } if (CanBeHLEProgram(ci.hash) && !Settings::values.disable_macro_hle) { @@ -1443,10 +1422,10 @@ void MacroEngine::Execute(Engines::Maxwell3D& maxwell3d, u32 method, std::span code) { +AnyCachedMacro MacroEngine::Compile(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span code) { #ifdef ARCHITECTURE_x86_64 if (!is_interpreted) - return std::make_unique(code); + return std::make_unique(system, code); #endif return MacroInterpreterImpl(code); } diff --git a/src/video_core/macro.h b/src/video_core/macro.h index a9a8f2de04..43b32ed920 100644 --- a/src/video_core/macro.h +++ b/src/video_core/macro.h @@ -14,6 +14,10 @@ #include "common/bit_field.h" #include "common/common_types.h" +namespace Core { +class System; +} + namespace Tegra { namespace Engines { @@ -106,61 +110,61 @@ struct HLEMacro { /// also assigning the base vertex/instance. struct HLE_DrawArraysIndirect final { HLE_DrawArraysIndirect(bool extended_) noexcept : extended{extended_} {} - void Execute(Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method); - void Fallback(Engines::Maxwell3D& maxwell3d, std::span parameters); + void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method); + void Fallback(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span parameters); bool extended; }; /// @note: these macros have two versions, a normal and extended version, with the extended version /// also assigning the base vertex/instance. struct HLE_DrawIndexedIndirect final { explicit HLE_DrawIndexedIndirect(bool extended_) noexcept : extended{extended_} {} - void Execute(Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method); - void Fallback(Engines::Maxwell3D& maxwell3d, std::span parameters); + void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method); + void Fallback(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span parameters); bool extended; }; struct HLE_MultiLayerClear final { - void Execute(Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method); + void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method); }; struct HLE_MultiDrawIndexedIndirectCount final { - void Execute(Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method); - void Fallback(Engines::Maxwell3D& maxwell3d, std::span parameters); + void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method); + void Fallback(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span parameters); }; struct HLE_DrawIndirectByteCount final { - void Execute(Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method); - void Fallback(Engines::Maxwell3D& maxwell3d, std::span parameters); + void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method); + void Fallback(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span parameters); }; struct HLE_C713C83D8F63CCF3 final { - void Execute(Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method); + void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method); }; struct HLE_D7333D26E0A93EDE final { - void Execute(Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method); + void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method); }; struct HLE_BindShader final { - void Execute(Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method); + void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method); }; struct HLE_SetRasterBoundingBox final { - void Execute(Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method); + void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method); }; struct HLE_ClearConstBuffer final { HLE_ClearConstBuffer(size_t base_size_) noexcept : base_size{base_size_} {} - void Execute(Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method); + void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method); size_t base_size; }; struct HLE_ClearMemory final { - void Execute(Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method); + void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method); std::vector zero_memory; }; struct HLE_TransformFeedbackSetup final { - void Execute(Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method); + void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method); }; struct MacroInterpreterImpl final { MacroInterpreterImpl() {} MacroInterpreterImpl(std::span code_) : code{code_} {} - void Execute(Engines::Maxwell3D& maxwell3d, std::span params, u32 method); + void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span params, u32 method); void Reset(); - bool Step(Engines::Maxwell3D& maxwell3d, bool is_delay_slot); + bool Step(Core::System& system, Engines::Maxwell3D& maxwell3d, bool is_delay_slot); u32 GetALUResult(Macro::ALUOperation operation, u32 src_a, u32 src_b); - void ProcessResult(Engines::Maxwell3D& maxwell3d, Macro::ResultOperation operation, u32 reg, u32 result); + void ProcessResult(Core::System& system, Engines::Maxwell3D& maxwell3d, Macro::ResultOperation operation, u32 reg, u32 result); bool EvaluateBranchCondition(Macro::BranchCondition cond, u32 value) const; Macro::Opcode GetOpcode() const; u32 GetRegister(u32 register_id) const; @@ -169,7 +173,7 @@ struct MacroInterpreterImpl final { [[nodiscard]] inline void SetMethodAddress(u32 address) noexcept { method_address.raw = address; } - void Send(Engines::Maxwell3D& maxwell3d, u32 value); + void Send(Core::System& system, Engines::Maxwell3D& maxwell3d, u32 value); u32 Read(Engines::Maxwell3D& maxwell3d, u32 method) const; u32 FetchParameter(); /// General purpose macro registers. @@ -192,7 +196,7 @@ struct DynamicCachedMacro { /// Executes the macro code with the specified input parameters. /// @param parameters The parameters of the macro /// @param method The method to execute - virtual void Execute(Engines::Maxwell3D& maxwell3d, std::span parameters, u32 method) = 0; + virtual void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span parameters, u32 method) = 0; }; using AnyCachedMacro = std::variant< @@ -227,8 +231,8 @@ struct MacroEngine { uploaded_macro_code.erase(method); } // Compiles the macro if its not in the cache, and executes the compiled macro - void Execute(Engines::Maxwell3D& maxwell3d, u32 method, std::span parameters); - AnyCachedMacro Compile(Engines::Maxwell3D& maxwell3d, std::span code); + void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, u32 method, std::span parameters); + AnyCachedMacro Compile(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span code); struct CacheInfo { AnyCachedMacro program; u64 hash{}; diff --git a/src/video_core/pte_kind.h b/src/video_core/pte_kind.h index 591d7214b2..d1495d94b0 100644 --- a/src/video_core/pte_kind.h +++ b/src/video_core/pte_kind.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -9,28 +12,22 @@ namespace Tegra { // https://github.com/NVIDIA/open-gpu-doc/blob/master/manuals/volta/gv100/dev_mmu.ref.txt enum class PTEKind : u8 { - INVALID = 0xff, - PITCH = 0x00, - Z16 = 0x01, - Z16_2C = 0x02, - Z16_MS2_2C = 0x03, - Z16_MS4_2C = 0x04, - Z16_MS8_2C = 0x05, - Z16_MS16_2C = 0x06, - Z16_2Z = 0x07, - Z16_MS2_2Z = 0x08, - Z16_MS4_2Z = 0x09, - Z16_MS8_2Z = 0x0a, - Z16_MS16_2Z = 0x0b, - Z16_2CZ = 0x36, - Z16_MS2_2CZ = 0x37, - Z16_MS4_2CZ = 0x38, - Z16_MS8_2CZ = 0x39, - Z16_MS16_2CZ = 0x5f, - Z16_4CZ = 0x0c, - Z16_MS2_4CZ = 0x0d, - Z16_MS4_4CZ = 0x0e, - Z16_MS8_4CZ = 0x0f, + PITCH = 0x0, + Z16 = 0x1, + Z16_2C = 0x2, + Z16_MS2_2C = 0x3, + Z16_MS4_2C = 0x4, + Z16_MS8_2C = 0x5, + Z16_MS16_2C = 0x6, + Z16_2Z = 0x7, + Z16_MS2_2Z = 0x8, + Z16_MS4_2Z = 0x9, + Z16_MS8_2Z = 0xa, + Z16_MS16_2Z = 0xb, + Z16_4CZ = 0xc, + Z16_MS2_4CZ = 0xd, + Z16_MS4_4CZ = 0xe, + Z16_MS8_4CZ = 0xf, Z16_MS16_4CZ = 0x10, S8Z24 = 0x11, S8Z24_1Z = 0x12, @@ -43,7 +40,7 @@ enum class PTEKind : u8 { S8Z24_MS4_2CZ = 0x19, S8Z24_MS8_2CZ = 0x1a, S8Z24_MS16_2CZ = 0x1b, - S8Z24_2CS = 0x1c, + S8Z24_2CS = 0x1C, S8Z24_MS2_2CS = 0x1d, S8Z24_MS4_2CS = 0x1e, S8Z24_MS8_2CS = 0x1f, @@ -57,6 +54,8 @@ enum class PTEKind : u8 { V8Z24_MS4_VC4 = 0x27, V8Z24_MS8_VC8 = 0x28, V8Z24_MS8_VC24 = 0x29, + S8 = 0x2a, + S8_2S = 0x2b, V8Z24_MS4_VC12_1ZV = 0x2e, V8Z24_MS4_VC4_1ZV = 0x2f, V8Z24_MS8_VC8_1ZV = 0x30, @@ -99,15 +98,9 @@ enum class PTEKind : u8 { Z24S8_MS8_4CSZV = 0x59, Z24S8_MS16_4CSZV = 0x5a, Z24V8_MS4_VC12 = 0x5b, - Z24V8_MS4_VC4 = 0x5c, + Z24V8_MS4_VC4 = 0x5C, Z24V8_MS8_VC8 = 0x5d, Z24V8_MS8_VC24 = 0x5e, - YUV_B8C1_2Y = 0x60, - YUV_B8C2_2Y = 0x61, - YUV_B10C1_2Y = 0x62, - YUV_B10C2_2Y = 0x6b, - YUV_B12C1_2Y = 0x6c, - YUV_B12C2_2Y = 0x6d, Z24V8_MS4_VC12_1ZV = 0x63, Z24V8_MS4_VC4_1ZV = 0x64, Z24V8_MS8_VC8_1ZV = 0x65, @@ -129,7 +122,7 @@ enum class PTEKind : u8 { Z24V8_MS8_VC8_4CSZV = 0x79, Z24V8_MS8_VC24_4CSZV = 0x7a, ZF32 = 0x7b, - ZF32_1Z = 0x7c, + ZF32_1Z = 0x7C, ZF32_MS2_1Z = 0x7d, ZF32_MS4_1Z = 0x7e, ZF32_MS8_1Z = 0x7f, @@ -198,6 +191,9 @@ enum class PTEKind : u8 { ZF32_X24S8_MS4_1CS = 0xc6, ZF32_X24S8_MS8_1CS = 0xc7, ZF32_X24S8_MS16_1CS = 0xc8, + SMASKED_MESSAGE = 0xca, + SMHOST_MESSAGE = 0xcb, + C64_MS2_2CRA = 0xcd, ZF32_X24S8_2CSZV = 0xce, ZF32_X24S8_MS2_2CSZV = 0xcf, ZF32_X24S8_MS4_2CSZV = 0xd0, @@ -208,9 +204,6 @@ enum class PTEKind : u8 { ZF32_X24S8_MS4_2CS = 0xd5, ZF32_X24S8_MS8_2CS = 0xd6, ZF32_X24S8_MS16_2CS = 0xd7, - S8 = 0x2a, - S8_2S = 0x2b, - GENERIC_16BX2 = 0xfe, C32_2C = 0xd8, C32_2CBR = 0xd9, C32_2CBA = 0xda, @@ -218,13 +211,12 @@ enum class PTEKind : u8 { C32_2BRA = 0xdc, C32_MS2_2C = 0xdd, C32_MS2_2CBR = 0xde, - C32_MS2_4CBRA = 0xcc, + C32_MS2_2CRA = 0xcc, C32_MS4_2C = 0xdf, C32_MS4_2CBR = 0xe0, C32_MS4_2CBA = 0xe1, C32_MS4_2CRA = 0xe2, C32_MS4_2BRA = 0xe3, - C32_MS4_4CBRA = 0x2c, C32_MS8_MS16_2C = 0xe4, C32_MS8_MS16_2CRA = 0xe5, C64_2C = 0xe6, @@ -234,13 +226,11 @@ enum class PTEKind : u8 { C64_2BRA = 0xea, C64_MS2_2C = 0xeb, C64_MS2_2CBR = 0xec, - C64_MS2_4CBRA = 0xcd, C64_MS4_2C = 0xed, C64_MS4_2CBR = 0xee, C64_MS4_2CBA = 0xef, C64_MS4_2CRA = 0xf0, C64_MS4_2BRA = 0xf1, - C64_MS4_4CBRA = 0x2d, C64_MS8_MS16_2C = 0xf2, C64_MS8_MS16_2CRA = 0xf3, C128_2C = 0xf4, @@ -253,8 +243,8 @@ enum class PTEKind : u8 { C128_MS8_MS16_2CR = 0xfb, X8C24 = 0xfc, PITCH_NO_SWIZZLE = 0xfd, - SMSKED_MESSAGE = 0xca, - SMHOST_MESSAGE = 0xcb, + GENERIC_16BX2 = 0xfe, + INVALID = 0xff, }; constexpr bool IsPitchKind(PTEKind kind) { diff --git a/src/video_core/renderer_opengl/gl_blit_screen.cpp b/src/video_core/renderer_opengl/gl_blit_screen.cpp index 4b75e1b949..23e5eb7481 100644 --- a/src/video_core/renderer_opengl/gl_blit_screen.cpp +++ b/src/video_core/renderer_opengl/gl_blit_screen.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 Torzu Emulator Project @@ -115,6 +115,8 @@ void BlitScreen::CreateWindowAdapt() { window_adapt = MakeMmpx(device); break; case Settings::ScalingFilter::Fsr: + case Settings::ScalingFilter::Sgsr: + case Settings::ScalingFilter::SgsrEdge: case Settings::ScalingFilter::Bilinear: default: window_adapt = MakeBilinear(device); diff --git a/src/video_core/renderer_opengl/gl_compute_pipeline.cpp b/src/video_core/renderer_opengl/gl_compute_pipeline.cpp index d1c61be743..f0a7baf9aa 100644 --- a/src/video_core/renderer_opengl/gl_compute_pipeline.cpp +++ b/src/video_core/renderer_opengl/gl_compute_pipeline.cpp @@ -90,7 +90,7 @@ void ComputePipeline::Configure() { desc.is_written); ++ssbo_index; } - texture_cache.SynchronizeComputeDescriptors(); + texture_cache.SynchronizeDescriptors(true); boost::container::static_vector views; boost::container::static_vector samplers; @@ -148,14 +148,14 @@ void ComputePipeline::Configure() { const auto handle{read_handle(desc, index)}; views.push_back({handle.first}); - VideoCommon::SamplerId sampler = texture_cache.GetComputeSamplerId(handle.second); + VideoCommon::SamplerId sampler = texture_cache.GetSamplerId(handle.second, true); samplers.push_back(sampler); } } for (const auto& desc : info.image_descriptors) { add_image(desc, desc.is_written); } - texture_cache.FillComputeImageViews(std::span(views.data(), views.size())); + texture_cache.FillImageViews(std::span(views.data(), views.size()), true); if (!is_built) { WaitForBuild(); diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp index 0829e6dd33..924ba2bbed 100644 --- a/src/video_core/renderer_opengl/gl_device.cpp +++ b/src/video_core/renderer_opengl/gl_device.cpp @@ -203,6 +203,7 @@ Device::Device(Core::Frontend::EmuWindow& emu_window) { max_varyings = GetInteger(GL_MAX_VARYING_VECTORS); max_compute_shared_memory_size = GetInteger(GL_MAX_COMPUTE_SHARED_MEMORY_SIZE); max_glasm_storage_buffer_blocks = GetInteger(GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS); + max_user_clip_distances = GetInteger(GL_MAX_CLIP_DISTANCES); has_warp_intrinsics = GLAD_GL_NV_gpu_shader5 && GLAD_GL_NV_shader_thread_group && GLAD_GL_NV_shader_thread_shuffle; has_shader_ballot = GLAD_GL_ARB_shader_ballot; diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h index 17b5a828f2..200e77e6e4 100644 --- a/src/video_core/renderer_opengl/gl_device.h +++ b/src/video_core/renderer_opengl/gl_device.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project @@ -47,6 +47,10 @@ public: return max_compute_shared_memory_size; } + u32 GetMaxUserClipDistances() const { + return max_user_clip_distances; + } + u32 GetMaxGLASMStorageBufferBlocks() const { return max_glasm_storage_buffer_blocks; } @@ -202,6 +206,7 @@ private: u32 max_varyings{}; u32 max_compute_shared_memory_size{}; u32 max_glasm_storage_buffer_blocks{}; + u32 max_user_clip_distances{}; bool has_warp_intrinsics{}; bool has_shader_ballot{}; diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp index ee3498428e..83545463ac 100644 --- a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp +++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp @@ -283,7 +283,7 @@ bool GraphicsPipeline::ConfigureImpl(bool is_indexed) { size_t views_index{}; size_t samplers_index{}; - texture_cache.SynchronizeGraphicsDescriptors(); + texture_cache.SynchronizeDescriptors(false); buffer_cache.SetUniformBuffersState(enabled_uniform_buffer_masks, &uniform_buffer_sizes); buffer_cache.runtime.SetBaseUniformBindings(base_uniform_bindings); @@ -354,7 +354,7 @@ bool GraphicsPipeline::ConfigureImpl(bool is_indexed) { const auto handle{read_handle(desc, index)}; views[views_index++] = {handle.first}; - VideoCommon::SamplerId sampler{texture_cache.GetGraphicsSamplerId(handle.second)}; + VideoCommon::SamplerId sampler{texture_cache.GetSamplerId(handle.second, false)}; samplers[samplers_index++] = sampler; } } @@ -379,7 +379,7 @@ bool GraphicsPipeline::ConfigureImpl(bool is_indexed) { if constexpr (Spec::enabled_stages[4]) { config_stage(4); } - texture_cache.FillGraphicsImageViews(std::span(views.data(), views_index)); + texture_cache.FillImageViews(std::span(views.data(), views_index), false, Spec::has_images); texture_cache.UpdateRenderTargets(false); state_tracker.BindFramebuffer(texture_cache.GetFramebuffer()->Handle()); diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 70f244809f..1d77d28c46 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -353,13 +353,13 @@ void RasterizerOpenGL::DrawTexture() { gpu.TickWork(); }; - texture_cache.SynchronizeGraphicsDescriptors(); + texture_cache.SynchronizeDescriptors(false); texture_cache.UpdateRenderTargets(false); SyncState(); const auto& draw_texture_state = maxwell3d->draw_manager.draw_texture_state; - const auto& sampler = texture_cache.GetGraphicsSampler(draw_texture_state.src_sampler); + const auto& sampler = texture_cache.GetSampler(draw_texture_state.src_sampler, true); const auto& texture = texture_cache.GetImageView(draw_texture_state.src_texture); const auto Scale = [&](auto dim) -> s32 { diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index deedf1aa6b..bc88796e3f 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -238,7 +238,11 @@ ShaderCache::ShaderCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, .ignore_nan_fp_comparisons = true, .gl_max_compute_smem_size = device.GetMaxComputeSharedMemorySize(), .min_ssbo_alignment = device.GetShaderStorageBufferAlignment(), - .max_user_clip_distances = 8, + // Use the host limit, but never more than the guest can produce. Maxwell exposes 8 clip + // distances and the SPIR-V output array is sized for at most 8, so clamping here keeps a + // host that reports a different count from under- or over-running that array. + .max_user_clip_distances = + std::min(device.GetMaxUserClipDistances(), Maxwell::Regs::NumClipDistances), }, host_info{ .support_float64 = true, @@ -477,7 +481,7 @@ std::unique_ptr ShaderCache::CreateGraphicsPipeline( const u32 cfg_offset = u32(env.StartAddress() + sizeof(Shader::ProgramHeader)); Shader::Maxwell::Flow::CFG cfg(env, pools.flow_block, cfg_offset, index == 0); - if (Settings::values.dump_shaders) { + if (Settings::values.dump_guest_shaders) { env.Dump(hash, key.unique_hashes[index]); } @@ -574,7 +578,7 @@ std::unique_ptr ShaderCache::CreateComputePipeline( Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()}; - if (Settings::values.dump_shaders) { + if (Settings::values.dump_guest_shaders) { env.Dump(hash, key.unique_hash); } diff --git a/src/video_core/renderer_opengl/gl_state_tracker.cpp b/src/video_core/renderer_opengl/gl_state_tracker.cpp index d53b422cac..dc91723300 100644 --- a/src/video_core/renderer_opengl/gl_state_tracker.cpp +++ b/src/video_core/renderer_opengl/gl_state_tracker.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -204,7 +207,7 @@ void SetupDirtyMisc(Tables& tables) { } // Anonymous namespace void StateTracker::SetupTables(Tegra::Control::ChannelState& channel_state) { - auto& tables{channel_state.maxwell_3d->dirty.tables}; + auto& tables{channel_state.payload->maxwell_3d.dirty.tables}; SetupDirtyFlags(tables); SetupDirtyColorMasks(tables); SetupDirtyViewports(tables); @@ -231,7 +234,7 @@ void StateTracker::SetupTables(Tegra::Control::ChannelState& channel_state) { } void StateTracker::ChangeChannel(Tegra::Control::ChannelState& channel_state) { - flags = &channel_state.maxwell_3d->dirty.flags; + flags = &channel_state.payload->maxwell_3d.dirty.flags; } void StateTracker::InvalidateState() { diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp index 789f4da2ed..d99e528bc0 100644 --- a/src/video_core/renderer_vulkan/blit_image.cpp +++ b/src/video_core/renderer_vulkan/blit_image.cpp @@ -436,7 +436,7 @@ void TransitionImageLayout(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayo .layerCount = 1, }, }; - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, 0, barrier); } @@ -502,9 +502,9 @@ BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_, two_textures_set_layout(device.GetLogical().CreateDescriptorSetLayout( TWO_TEXTURES_DESCRIPTOR_SET_LAYOUT_CREATE_INFO)), one_texture_descriptor_allocator{ - descriptor_pool.Allocator(*one_texture_set_layout, TEXTURE_DESCRIPTOR_BANK_INFO<1>)}, + descriptor_pool.Allocator(device_, scheduler_, *one_texture_set_layout, TEXTURE_DESCRIPTOR_BANK_INFO<1>)}, two_textures_descriptor_allocator{ - descriptor_pool.Allocator(*two_textures_set_layout, TEXTURE_DESCRIPTOR_BANK_INFO<2>)}, + descriptor_pool.Allocator(device_, scheduler_, *two_textures_set_layout, TEXTURE_DESCRIPTOR_BANK_INFO<2>)}, one_texture_pipeline_layout(device.GetLogical().CreatePipelineLayout(PipelineLayoutCreateInfo( one_texture_set_layout.address(), PUSH_CONSTANT_RANGE))), diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp index 930f06daf6..77ef8bf7bc 100644 --- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp +++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp @@ -217,6 +217,12 @@ FormatInfo SurfaceFormat(const Device& device, FormatType format_type, bool with SURFACE_FORMAT_ELEM(VK_FORMAT_ASTC_6x5_UNORM_BLOCK, 0, ASTC_2D_6X5_UNORM) \ SURFACE_FORMAT_ELEM(VK_FORMAT_ASTC_6x5_SRGB_BLOCK, 0, ASTC_2D_6X5_SRGB) \ SURFACE_FORMAT_ELEM(VK_FORMAT_E5B9G9R9_UFLOAT_PACK32, 0, E5B9G9R9_FLOAT) \ + SURFACE_FORMAT_ELEM(VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK, 0, ETC2_RGB_UNORM) \ + SURFACE_FORMAT_ELEM(VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK, 0, ETC2_RGBA_UNORM) \ + SURFACE_FORMAT_ELEM(VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK, 0, ETC2_RGB_PTA_UNORM) \ + SURFACE_FORMAT_ELEM(VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK, 0, ETC2_RGB_SRGB) \ + SURFACE_FORMAT_ELEM(VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK, 0, ETC2_RGBA_SRGB) \ + SURFACE_FORMAT_ELEM(VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK, 0, ETC2_RGB_PTA_SRGB) \ /* Depth formats */ \ SURFACE_FORMAT_ELEM(VK_FORMAT_D32_SFLOAT, usage_attachable, D32_FLOAT) \ SURFACE_FORMAT_ELEM(VK_FORMAT_D16_UNORM, usage_attachable, D16_UNORM) \ @@ -253,8 +259,8 @@ FormatInfo SurfaceFormat(const Device& device, FormatType format_type, bool with break; } } - // Transcode on hardware that doesn't support BCn natively if (!device.IsOptimalBcnSupported() && VideoCore::Surface::IsPixelFormatBCn(pixel_format)) { + // Transcode on hardware that doesn't support BCn natively if (pixel_format == PixelFormat::BC4_SNORM) { tuple.format = VK_FORMAT_R8_SNORM; } else if (pixel_format == PixelFormat::BC4_UNORM) { @@ -270,6 +276,9 @@ FormatInfo SurfaceFormat(const Device& device, FormatType format_type, bool with } else { tuple.format = VK_FORMAT_A8B8G8R8_UNORM_PACK32; } + } else if (!device.IsOptimalEtc2Supported() && VideoCore::Surface::IsPixelFormatETC2(pixel_format)) { + // Transcode on hardware that doesn't support ETC2 natively + tuple.format = is_srgb ? VK_FORMAT_A8B8G8R8_SRGB_PACK32 : VK_FORMAT_A8B8G8R8_UNORM_PACK32; } bool const attachable = (tuple.usage & usage_attachable) != 0; bool const storage = (tuple.usage & usage_storage) != 0; diff --git a/src/video_core/renderer_vulkan/pipeline_statistics.cpp b/src/video_core/renderer_vulkan/pipeline_statistics.cpp index ad288436a8..04f42c71a6 100644 --- a/src/video_core/renderer_vulkan/pipeline_statistics.cpp +++ b/src/video_core/renderer_vulkan/pipeline_statistics.cpp @@ -31,14 +31,13 @@ static u64 GetUint64(const VkPipelineExecutableStatisticKHR& statistic) { } } -PipelineStatistics::PipelineStatistics(const Device& device_) : device{device_} {} +PipelineStatistics::PipelineStatistics(const Device& device_) {} -void PipelineStatistics::Collect(VkPipeline pipeline) { - const auto& dev{device.GetLogical()}; - const std::vector properties{dev.GetPipelineExecutablePropertiesKHR(pipeline)}; +void PipelineStatistics::Collect(const Device& device, VkPipeline pipeline) { + const std::vector properties{device.GetLogical().GetPipelineExecutablePropertiesKHR(pipeline)}; const u32 num_executables{static_cast(properties.size())}; for (u32 executable = 0; executable < num_executables; ++executable) { - const auto statistics{dev.GetPipelineExecutableStatisticsKHR(pipeline, executable)}; + const auto statistics{device.GetLogical().GetPipelineExecutableStatisticsKHR(pipeline, executable)}; if (statistics.empty()) { continue; } diff --git a/src/video_core/renderer_vulkan/pipeline_statistics.h b/src/video_core/renderer_vulkan/pipeline_statistics.h index 197bb09363..83e70b704f 100644 --- a/src/video_core/renderer_vulkan/pipeline_statistics.h +++ b/src/video_core/renderer_vulkan/pipeline_statistics.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -16,9 +19,7 @@ class Device; class PipelineStatistics { public: explicit PipelineStatistics(const Device& device_); - - void Collect(VkPipeline pipeline); - + void Collect(const Device& device, VkPipeline pipeline); void Report() const; private: @@ -30,8 +31,6 @@ private: u64 branches_count{}; u64 basic_block_count{}; }; - - const Device& device; mutable std::mutex mutex; std::vector collected_stats; }; diff --git a/src/video_core/renderer_vulkan/present/anti_alias_pass.h b/src/video_core/renderer_vulkan/present/anti_alias_pass.h index 4990e87502..8061d01208 100644 --- a/src/video_core/renderer_vulkan/present/anti_alias_pass.h +++ b/src/video_core/renderer_vulkan/present/anti_alias_pass.h @@ -11,12 +11,12 @@ namespace Vulkan { class Scheduler; +class Device; class AntiAliasPass { public: virtual ~AntiAliasPass() = default; - virtual void Draw(Scheduler& scheduler, size_t image_index, VkImage* inout_image, - VkImageView* inout_image_view) = 0; + virtual void Draw(const Device& device, Scheduler& scheduler, size_t image_index, VkImage* inout_image, VkImageView* inout_image_view) = 0; }; } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/fsr.cpp b/src/video_core/renderer_vulkan/present/fsr.cpp index ba6252ed95..894fb144f0 100644 --- a/src/video_core/renderer_vulkan/present/fsr.cpp +++ b/src/video_core/renderer_vulkan/present/fsr.cpp @@ -25,74 +25,74 @@ using namespace FSR; using PushConstants = std::array; -FSR::FSR(const Device& device, MemoryAllocator& memory_allocator, size_t image_count, - VkExtent2D extent) - : m_device{device}, m_memory_allocator{memory_allocator}, - m_image_count{image_count}, m_extent{extent} { - - CreateImages(); - CreateRenderPasses(); - CreateSampler(); - CreateShaders(); - CreateDescriptorPool(); - CreateDescriptorSetLayout(); - CreateDescriptorSets(); - CreatePipelineLayouts(); - CreatePipelines(); +FSR::FSR(const Device& device, MemoryAllocator& memory_allocator, size_t image_count, VkExtent2D extent) + : m_memory_allocator{memory_allocator} + , m_image_count{image_count} + , m_extent{extent} +{ + CreateImages(device); + CreateRenderPasses(device); + CreateSampler(device); + CreateShaders(device); + CreateDescriptorPool(device); + CreateDescriptorSetLayout(device); + CreateDescriptorSets(device); + CreatePipelineLayouts(device); + CreatePipelines(device); } -void FSR::CreateImages() { +void FSR::CreateImages(const Device& device) { m_dynamic_images.resize(m_image_count); for (auto& images : m_dynamic_images) { images.images[Easu] = CreateWrappedImage(m_memory_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT); images.images[Rcas] = CreateWrappedImage(m_memory_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT); - images.image_views[Easu] = CreateWrappedImageView(m_device, images.images[Easu], VK_FORMAT_R16G16B16A16_SFLOAT); - images.image_views[Rcas] = CreateWrappedImageView(m_device, images.images[Rcas], VK_FORMAT_R16G16B16A16_SFLOAT); + images.image_views[Easu] = CreateWrappedImageView(device, images.images[Easu], VK_FORMAT_R16G16B16A16_SFLOAT); + images.image_views[Rcas] = CreateWrappedImageView(device, images.images[Rcas], VK_FORMAT_R16G16B16A16_SFLOAT); } } -void FSR::CreateRenderPasses() { - m_renderpass = CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16B16A16_SFLOAT); +void FSR::CreateRenderPasses(const Device& device) { + m_renderpass = CreateWrappedRenderPass(device, VK_FORMAT_R16G16B16A16_SFLOAT); for (auto& images : m_dynamic_images) { - images.framebuffers[Easu] = CreateWrappedFramebuffer(m_device, m_renderpass, images.image_views[Easu], m_extent); - images.framebuffers[Rcas] = CreateWrappedFramebuffer(m_device, m_renderpass, images.image_views[Rcas], m_extent); + images.framebuffers[Easu] = CreateWrappedFramebuffer(device, m_renderpass, images.image_views[Easu], m_extent); + images.framebuffers[Rcas] = CreateWrappedFramebuffer(device, m_renderpass, images.image_views[Rcas], m_extent); } } -void FSR::CreateSampler() { - m_sampler = CreateBilinearSampler(m_device); +void FSR::CreateSampler(const Device& device) { + m_sampler = CreateBilinearSampler(device); } -void FSR::CreateShaders() { - m_vert_shader = BuildShader(m_device, VULKAN_FIDELITYFX_FSR_VERT_SPV); +void FSR::CreateShaders(const Device& device) { + m_vert_shader = BuildShader(device, VULKAN_FIDELITYFX_FSR_VERT_SPV); - if (m_device.IsFloat16Supported()) { - m_easu_shader = BuildShader(m_device, VULKAN_FIDELITYFX_FSR_EASU_FP16_FRAG_SPV); - m_rcas_shader = BuildShader(m_device, VULKAN_FIDELITYFX_FSR_RCAS_FP16_FRAG_SPV); + if (device.IsFloat16Supported()) { + m_easu_shader = BuildShader(device, VULKAN_FIDELITYFX_FSR_EASU_FP16_FRAG_SPV); + m_rcas_shader = BuildShader(device, VULKAN_FIDELITYFX_FSR_RCAS_FP16_FRAG_SPV); } else { - m_easu_shader = BuildShader(m_device, VULKAN_FIDELITYFX_FSR_EASU_FP32_FRAG_SPV); - m_rcas_shader = BuildShader(m_device, VULKAN_FIDELITYFX_FSR_RCAS_FP32_FRAG_SPV); + m_easu_shader = BuildShader(device, VULKAN_FIDELITYFX_FSR_EASU_FP32_FRAG_SPV); + m_rcas_shader = BuildShader(device, VULKAN_FIDELITYFX_FSR_RCAS_FP32_FRAG_SPV); } } -void FSR::CreateDescriptorPool() { +void FSR::CreateDescriptorPool(const Device& device) { // EASU: 1 descriptor // RCAS: 1 descriptor // 2 descriptors, 2 descriptor sets per invocation - m_descriptor_pool = CreateWrappedDescriptorPool(m_device, 2 * m_image_count, 2 * m_image_count); + m_descriptor_pool = CreateWrappedDescriptorPool(device, 2 * m_image_count, 2 * m_image_count); } -void FSR::CreateDescriptorSetLayout() { - m_descriptor_set_layout = CreateWrappedDescriptorSetLayout(m_device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}); +void FSR::CreateDescriptorSetLayout(const Device& device) { + m_descriptor_set_layout = CreateWrappedDescriptorSetLayout(device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}); } -void FSR::CreateDescriptorSets() { +void FSR::CreateDescriptorSets(const Device& device) { std::vector layouts(MaxFsrStage, *m_descriptor_set_layout); for (auto& images : m_dynamic_images) images.descriptor_sets = CreateWrappedDescriptorSets(m_descriptor_pool, layouts); } -void FSR::CreatePipelineLayouts() { +void FSR::CreatePipelineLayouts(const Device& device) { const VkPushConstantRange range{ .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, .offset = 0, @@ -108,17 +108,17 @@ void FSR::CreatePipelineLayouts() { .pPushConstantRanges = &range, }; - m_pipeline_layout = m_device.GetLogical().CreatePipelineLayout(ci); + m_pipeline_layout = device.GetLogical().CreatePipelineLayout(ci); } -void FSR::CreatePipelines() { - m_easu_pipeline = CreateWrappedPipeline(m_device, m_renderpass, m_pipeline_layout, +void FSR::CreatePipelines(const Device& device) { + m_easu_pipeline = CreateWrappedPipeline(device, m_renderpass, m_pipeline_layout, std::tie(m_vert_shader, m_easu_shader)); - m_rcas_pipeline = CreateWrappedPipeline(m_device, m_renderpass, m_pipeline_layout, + m_rcas_pipeline = CreateWrappedPipeline(device, m_renderpass, m_pipeline_layout, std::tie(m_vert_shader, m_rcas_shader)); } -void FSR::UpdateDescriptorSets(VkImageView image_view, size_t image_index) { +void FSR::UpdateDescriptorSets(const Device& device, VkImageView image_view, size_t image_index) { Images& images = m_dynamic_images[image_index]; std::vector image_infos; image_infos.reserve(2); @@ -126,10 +126,10 @@ void FSR::UpdateDescriptorSets(VkImageView image_view, size_t image_index) { CreateWriteDescriptorSet(image_infos, *m_sampler, image_view, images.descriptor_sets[Easu], 0), CreateWriteDescriptorSet(image_infos, *m_sampler, *images.image_views[Easu], images.descriptor_sets[Rcas], 0) }; - m_device.GetLogical().UpdateDescriptorSets(updates, {}); + device.GetLogical().UpdateDescriptorSets(updates, {}); } -void FSR::UploadImages(Scheduler& scheduler) { +void FSR::UploadImages(const Device& device, Scheduler& scheduler) { if (!m_images_ready) { m_images_ready = true; scheduler.Record([&](vk::CommandBuffer cmdbuf) { @@ -142,7 +142,7 @@ void FSR::UploadImages(Scheduler& scheduler) { } } -VkImageView FSR::Draw(Scheduler& scheduler, size_t image_index, VkImage source_image, +VkImageView FSR::Draw(const Device& device, Scheduler& scheduler, size_t image_index, VkImage source_image, VkImageView source_image_view, VkExtent2D input_image_extent, const Common::Rectangle& crop_rect) { Images& images = m_dynamic_images[image_index]; @@ -179,8 +179,8 @@ VkImageView FSR::Draw(Scheduler& scheduler, size_t image_index, VkImage source_i static_cast(Settings::values.fsr_sharpening_slider.GetValue()) / 100.0f; FsrRcasCon(rcas_con.data(), sharpening); - UploadImages(scheduler); - UpdateDescriptorSets(source_image_view, image_index); + UploadImages(device, scheduler); + UpdateDescriptorSets(device, source_image_view, image_index); scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([=](vk::CommandBuffer cmdbuf) { diff --git a/src/video_core/renderer_vulkan/present/fsr.h b/src/video_core/renderer_vulkan/present/fsr.h index 8602e81465..7762480757 100644 --- a/src/video_core/renderer_vulkan/present/fsr.h +++ b/src/video_core/renderer_vulkan/present/fsr.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -14,27 +17,25 @@ class Scheduler; class FSR { public: - explicit FSR(const Device& device, MemoryAllocator& memory_allocator, size_t image_count, - VkExtent2D extent); - VkImageView Draw(Scheduler& scheduler, size_t image_index, VkImage source_image, + explicit FSR(const Device& device, MemoryAllocator& memory_allocator, size_t image_count, VkExtent2D extent); + VkImageView Draw(const Device& device, Scheduler& scheduler, size_t image_index, VkImage source_image, VkImageView source_image_view, VkExtent2D input_image_extent, const Common::Rectangle& crop_rect); private: - void CreateImages(); - void CreateRenderPasses(); - void CreateSampler(); - void CreateShaders(); - void CreateDescriptorPool(); - void CreateDescriptorSetLayout(); - void CreateDescriptorSets(); - void CreatePipelineLayouts(); - void CreatePipelines(); + void CreateImages(const Device& device); + void CreateRenderPasses(const Device& device); + void CreateSampler(const Device& device); + void CreateShaders(const Device& device); + void CreateDescriptorPool(const Device& device); + void CreateDescriptorSetLayout(const Device& device); + void CreateDescriptorSets(const Device& device); + void CreatePipelineLayouts(const Device& device); + void CreatePipelines(const Device& device); - void UploadImages(Scheduler& scheduler); - void UpdateDescriptorSets(VkImageView image_view, size_t image_index); + void UploadImages(const Device& device, Scheduler& scheduler); + void UpdateDescriptorSets(const Device& device, VkImageView image_view, size_t image_index); - const Device& m_device; MemoryAllocator& m_memory_allocator; const size_t m_image_count; const VkExtent2D m_extent; diff --git a/src/video_core/renderer_vulkan/present/fxaa.cpp b/src/video_core/renderer_vulkan/present/fxaa.cpp index bdafd1f4d0..62f79d490c 100644 --- a/src/video_core/renderer_vulkan/present/fxaa.cpp +++ b/src/video_core/renderer_vulkan/present/fxaa.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -14,61 +17,60 @@ namespace Vulkan { FXAA::FXAA(const Device& device, MemoryAllocator& allocator, size_t image_count, VkExtent2D extent) - : m_device(device), m_allocator(allocator), m_extent(extent), - m_image_count(static_cast(image_count)) { - CreateImages(); - CreateRenderPasses(); - CreateSampler(); - CreateShaders(); - CreateDescriptorPool(); - CreateDescriptorSetLayouts(); - CreateDescriptorSets(); - CreatePipelineLayouts(); - CreatePipelines(); + : m_extent(extent) + , m_image_count(u32(image_count)) +{ + CreateImages(device, allocator); + CreateRenderPasses(device); + CreateSampler(device); + CreateShaders(device); + CreateDescriptorPool(device); + CreateDescriptorSetLayouts(device); + CreateDescriptorSets(device); + CreatePipelineLayouts(device); + CreatePipelines(device); } FXAA::~FXAA() = default; -void FXAA::CreateImages() { +void FXAA::CreateImages(const Device& device, MemoryAllocator& allocator) { for (u32 i = 0; i < m_image_count; i++) { Image& image = m_dynamic_images.emplace_back(); - - image.image = CreateWrappedImage(m_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT); - image.image_view = - CreateWrappedImageView(m_device, image.image, VK_FORMAT_R16G16B16A16_SFLOAT); + image.image = CreateWrappedImage(allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT); + image.image_view = CreateWrappedImageView(device, image.image, VK_FORMAT_R16G16B16A16_SFLOAT); } } -void FXAA::CreateRenderPasses() { - m_renderpass = CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16B16A16_SFLOAT); +void FXAA::CreateRenderPasses(const Device& device) { + m_renderpass = CreateWrappedRenderPass(device, VK_FORMAT_R16G16B16A16_SFLOAT); for (auto& image : m_dynamic_images) { image.framebuffer = - CreateWrappedFramebuffer(m_device, m_renderpass, image.image_view, m_extent); + CreateWrappedFramebuffer(device, m_renderpass, image.image_view, m_extent); } } -void FXAA::CreateSampler() { - m_sampler = CreateWrappedSampler(m_device); +void FXAA::CreateSampler(const Device& device) { + m_sampler = CreateWrappedSampler(device); } -void FXAA::CreateShaders() { - m_vertex_shader = CreateWrappedShaderModule(m_device, FXAA_VERT_SPV); - m_fragment_shader = CreateWrappedShaderModule(m_device, FXAA_FRAG_SPV); +void FXAA::CreateShaders(const Device& device) { + m_vertex_shader = CreateWrappedShaderModule(device, FXAA_VERT_SPV); + m_fragment_shader = CreateWrappedShaderModule(device, FXAA_FRAG_SPV); } -void FXAA::CreateDescriptorPool() { +void FXAA::CreateDescriptorPool(const Device& device) { // 2 descriptors, 1 descriptor set per image - m_descriptor_pool = CreateWrappedDescriptorPool(m_device, 2 * m_image_count, m_image_count); + m_descriptor_pool = CreateWrappedDescriptorPool(device, 2 * m_image_count, m_image_count); } -void FXAA::CreateDescriptorSetLayouts() { +void FXAA::CreateDescriptorSetLayouts(const Device& device) { m_descriptor_set_layout = - CreateWrappedDescriptorSetLayout(m_device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + CreateWrappedDescriptorSetLayout(device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}); } -void FXAA::CreateDescriptorSets() { +void FXAA::CreateDescriptorSets(const Device& device) { VkDescriptorSetLayout layout = *m_descriptor_set_layout; for (auto& images : m_dynamic_images) { @@ -76,30 +78,28 @@ void FXAA::CreateDescriptorSets() { } } -void FXAA::CreatePipelineLayouts() { - m_pipeline_layout = CreateWrappedPipelineLayout(m_device, m_descriptor_set_layout); +void FXAA::CreatePipelineLayouts(const Device& device) { + m_pipeline_layout = CreateWrappedPipelineLayout(device, m_descriptor_set_layout); } -void FXAA::CreatePipelines() { - m_pipeline = CreateWrappedPipeline(m_device, m_renderpass, m_pipeline_layout, +void FXAA::CreatePipelines(const Device& device) { + m_pipeline = CreateWrappedPipeline(device, m_renderpass, m_pipeline_layout, std::tie(m_vertex_shader, m_fragment_shader)); } -void FXAA::UpdateDescriptorSets(VkImageView image_view, size_t image_index) { +void FXAA::UpdateDescriptorSets(const Device& device, VkImageView image_view, size_t image_index) { Image& image = m_dynamic_images[image_index]; std::vector image_infos; std::vector updates; image_infos.reserve(2); - updates.push_back( - CreateWriteDescriptorSet(image_infos, *m_sampler, image_view, image.descriptor_sets[0], 0)); - updates.push_back( - CreateWriteDescriptorSet(image_infos, *m_sampler, image_view, image.descriptor_sets[0], 1)); + updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, image_view, image.descriptor_sets[0], 0)); + updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, image_view, image.descriptor_sets[0], 1)); - m_device.GetLogical().UpdateDescriptorSets(updates, {}); + device.GetLogical().UpdateDescriptorSets(updates, {}); } -void FXAA::UploadImages(Scheduler& scheduler) { +void FXAA::UploadImages(const Device& device, Scheduler& scheduler) { if (m_images_ready) { return; } @@ -114,8 +114,7 @@ void FXAA::UploadImages(Scheduler& scheduler) { m_images_ready = true; } -void FXAA::Draw(Scheduler& scheduler, size_t image_index, VkImage* inout_image, - VkImageView* inout_image_view) { +void FXAA::Draw(const Device& device, Scheduler& scheduler, size_t image_index, VkImage* inout_image, VkImageView* inout_image_view) { const Image& image{m_dynamic_images[image_index]}; const VkImage input_image{*inout_image}; const VkImage output_image{*image.image}; @@ -126,8 +125,8 @@ void FXAA::Draw(Scheduler& scheduler, size_t image_index, VkImage* inout_image, const VkPipelineLayout layout{*m_pipeline_layout}; const VkExtent2D extent{m_extent}; - UploadImages(scheduler); - UpdateDescriptorSets(*inout_image_view, image_index); + UploadImages(device, scheduler); + UpdateDescriptorSets(device, *inout_image_view, image_index); scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([=](vk::CommandBuffer cmdbuf) { diff --git a/src/video_core/renderer_vulkan/present/fxaa.h b/src/video_core/renderer_vulkan/present/fxaa.h index 97a2e5c1cb..7eaf31f81c 100644 --- a/src/video_core/renderer_vulkan/present/fxaa.h +++ b/src/video_core/renderer_vulkan/present/fxaa.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -15,38 +18,23 @@ class StagingBufferPool; class FXAA final : public AntiAliasPass { public: - explicit FXAA(const Device& device, MemoryAllocator& allocator, size_t image_count, - VkExtent2D extent); + explicit FXAA(const Device& device, MemoryAllocator& allocator, size_t image_count, VkExtent2D extent); ~FXAA() override; - void Draw(Scheduler& scheduler, size_t image_index, VkImage* inout_image, - VkImageView* inout_image_view) override; + void Draw(const Device& device, Scheduler& scheduler, size_t image_index, VkImage* inout_image, VkImageView* inout_image_view) override; private: - void CreateImages(); - void CreateRenderPasses(); - void CreateSampler(); - void CreateShaders(); - void CreateDescriptorPool(); - void CreateDescriptorSetLayouts(); - void CreateDescriptorSets(); - void CreatePipelineLayouts(); - void CreatePipelines(); - void UpdateDescriptorSets(VkImageView image_view, size_t image_index); - void UploadImages(Scheduler& scheduler); - - const Device& m_device; - MemoryAllocator& m_allocator; - const VkExtent2D m_extent; - const u32 m_image_count; - - vk::ShaderModule m_vertex_shader{}; - vk::ShaderModule m_fragment_shader{}; - vk::DescriptorPool m_descriptor_pool{}; - vk::DescriptorSetLayout m_descriptor_set_layout{}; - vk::PipelineLayout m_pipeline_layout{}; - vk::Pipeline m_pipeline{}; - vk::RenderPass m_renderpass{}; + void CreateImages(const Device& device, MemoryAllocator& allocator); + void CreateRenderPasses(const Device& device); + void CreateSampler(const Device& device); + void CreateShaders(const Device& device); + void CreateDescriptorPool(const Device& device); + void CreateDescriptorSetLayouts(const Device& device); + void CreateDescriptorSets(const Device& device); + void CreatePipelineLayouts(const Device& device); + void CreatePipelines(const Device& device); + void UpdateDescriptorSets(const Device& device, VkImageView image_view, size_t image_index); + void UploadImages(const Device& device, Scheduler& scheduler); struct Image { vk::DescriptorSets descriptor_sets{}; @@ -55,9 +43,17 @@ private: vk::ImageView image_view{}; }; std::vector m_dynamic_images{}; - bool m_images_ready{}; - + const VkExtent2D m_extent; + const u32 m_image_count; + vk::ShaderModule m_vertex_shader{}; + vk::ShaderModule m_fragment_shader{}; + vk::DescriptorPool m_descriptor_pool{}; + vk::DescriptorSetLayout m_descriptor_set_layout{}; + vk::PipelineLayout m_pipeline_layout{}; + vk::Pipeline m_pipeline{}; + vk::RenderPass m_renderpass{}; vk::Sampler m_sampler{}; + bool m_images_ready{}; }; } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/layer.cpp b/src/video_core/renderer_vulkan/present/layer.cpp index b462c672cc..14935f92bb 100644 --- a/src/video_core/renderer_vulkan/present/layer.cpp +++ b/src/video_core/renderer_vulkan/present/layer.cpp @@ -15,6 +15,7 @@ #include "common/settings.h" #include "video_core/framebuffer_config.h" #include "video_core/renderer_vulkan/present/fsr.h" +#include "video_core/renderer_vulkan/present/sgsr.h" #include "video_core/renderer_vulkan/present/fxaa.h" #include "video_core/renderer_vulkan/present/layer.h" #include "video_core/renderer_vulkan/present/present_push_constants.h" @@ -55,15 +56,21 @@ VkFormat GetFormat(const Tegra::FramebufferConfig& framebuffer) { } // Anonymous namespace -Layer::Layer(const Device& device_, MemoryAllocator& memory_allocator_, Scheduler& scheduler_, - Tegra::MaxwellDeviceMemoryManager& device_memory_, size_t image_count_, - VkExtent2D output_size, VkDescriptorSetLayout layout, const PresentFilters& filters_) - : device(device_), memory_allocator(memory_allocator_), scheduler(scheduler_), - device_memory(device_memory_), filters(filters_), image_count(image_count_) { - CreateDescriptorPool(); - CreateDescriptorSets(layout); +Layer::Layer(const Device& device, MemoryAllocator& memory_allocator_, Scheduler& scheduler_, Tegra::MaxwellDeviceMemoryManager& device_memory_, size_t image_count_, VkExtent2D output_size, VkDescriptorSetLayout layout, const PresentFilters& filters_) + : memory_allocator(memory_allocator_) + , scheduler(scheduler_) + , device_memory(device_memory_) + , filters(filters_) + , image_count(image_count_) +{ + CreateDescriptorPool(device); + CreateDescriptorSets(device, layout); if (filters.get_scaling_filter() == Settings::ScalingFilter::Fsr) { - fsr.emplace(device, memory_allocator, image_count, output_size); + sr_filter.emplace(device, memory_allocator, image_count, output_size); + } else if (filters.get_scaling_filter() == Settings::ScalingFilter::Sgsr) { + sr_filter.emplace(device, memory_allocator, image_count, output_size, false); + } else if (filters.get_scaling_filter() == Settings::ScalingFilter::SgsrEdge) { + sr_filter.emplace(device, memory_allocator, image_count, output_size, true); } } @@ -71,7 +78,7 @@ Layer::~Layer() { ReleaseRawImages(); } -void Layer::ConfigureDraw(PresentPushConstants* out_push_constants, +void Layer::ConfigureDraw(const Device& device, PresentPushConstants* out_push_constants, VkDescriptorSet* out_descriptor_set, RasterizerVulkan& rasterizer, VkSampler sampler, size_t image_index, const Tegra::FramebufferConfig& framebuffer, @@ -84,8 +91,8 @@ void Layer::ConfigureDraw(PresentPushConstants* out_push_constants, const u32 scaled_height = texture_info ? texture_info->scaled_height : texture_height; const bool use_accelerated = texture_info.has_value(); - RefreshResources(framebuffer); - SetAntiAliasPass(); + RefreshResources(device, framebuffer); + SetAntiAliasPass(device); // Finish any pending renderpass scheduler.RequestOutsideRenderPassOperationContext(); @@ -103,9 +110,9 @@ void Layer::ConfigureDraw(PresentPushConstants* out_push_constants, texture_info ? texture_info->image_view : *raw_image_views[image_index]; if (auto* fxaa = std::get_if(&anti_alias)) { - fxaa->Draw(scheduler, image_index, &source_image, &source_image_view); + fxaa->Draw(device, scheduler, image_index, &source_image, &source_image_view); } else if (auto* smaa = std::get_if(&anti_alias)) { - smaa->Draw(scheduler, image_index, &source_image, &source_image_view); + smaa->Draw(device, scheduler, image_index, &source_image, &source_image_view); } auto crop_rect = Tegra::NormalizeCrop(framebuffer, texture_width, texture_height); @@ -114,28 +121,31 @@ void Layer::ConfigureDraw(PresentPushConstants* out_push_constants, .height = scaled_height, }; - if (fsr) { - source_image_view = fsr->Draw(scheduler, image_index, source_image, source_image_view, render_extent, crop_rect); + if (auto* fsr = std::get_if(&sr_filter)) { + source_image_view = fsr->Draw(device, scheduler, image_index, source_image, source_image_view, render_extent, crop_rect); + crop_rect = {0, 0, 1, 1}; + } else if (auto* sgsr = std::get_if(&sr_filter)) { + source_image_view = sgsr->Draw(device, scheduler, image_index, source_image, source_image_view, render_extent, crop_rect); crop_rect = {0, 0, 1, 1}; } - SetMatrixData(*out_push_constants, layout); - SetVertexData(*out_push_constants, layout, crop_rect); + SetMatrixData(device, *out_push_constants, layout); + SetVertexData(device, *out_push_constants, layout, crop_rect); - UpdateDescriptorSet(source_image_view, sampler, image_index); + UpdateDescriptorSet(device, source_image_view, sampler, image_index); *out_descriptor_set = descriptor_sets[image_index]; } -void Layer::CreateDescriptorPool() { +void Layer::CreateDescriptorPool(const Device& device) { descriptor_pool = CreateWrappedDescriptorPool(device, image_count, image_count); } -void Layer::CreateDescriptorSets(VkDescriptorSetLayout layout) { +void Layer::CreateDescriptorSets(const Device& device, VkDescriptorSetLayout layout) { const std::vector layouts(image_count, layout); descriptor_sets = CreateWrappedDescriptorSets(descriptor_pool, layouts); } -void Layer::CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer) { +void Layer::CreateStagingBuffer(const Device& device, const Tegra::FramebufferConfig& framebuffer) { const VkBufferCreateInfo ci{ .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .pNext = nullptr, @@ -151,7 +161,7 @@ void Layer::CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer) { buffer = memory_allocator.CreateBuffer(ci, MemoryUsage::Upload); } -void Layer::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) { +void Layer::CreateRawImages(const Device& device, const Tegra::FramebufferConfig& framebuffer) { const auto format = GetFormat(framebuffer); resource_ticks.resize(image_count); raw_images.resize(image_count); @@ -164,7 +174,7 @@ void Layer::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) { } } -void Layer::RefreshResources(const Tegra::FramebufferConfig& framebuffer) { +void Layer::RefreshResources(const Device& device, const Tegra::FramebufferConfig& framebuffer) { if (framebuffer.width == raw_width && framebuffer.height == raw_height && framebuffer.pixel_format == pixel_format && !raw_images.empty()) { return; @@ -176,11 +186,11 @@ void Layer::RefreshResources(const Tegra::FramebufferConfig& framebuffer) { anti_alias.emplace(); ReleaseRawImages(); - CreateStagingBuffer(framebuffer); - CreateRawImages(framebuffer); + CreateStagingBuffer(device, framebuffer); + CreateRawImages(device, framebuffer); } -void Layer::SetAntiAliasPass() { +void Layer::SetAntiAliasPass(const Device& device) { if (!std::holds_alternative(anti_alias) && anti_alias_setting == filters.get_anti_aliasing()) return; @@ -221,20 +231,17 @@ u64 Layer::GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer, return GetSizeInBytes(framebuffer) * image_index; } -void Layer::SetMatrixData(PresentPushConstants& data, - const Layout::FramebufferLayout& layout) const { - data.modelview_matrix = - MakeOrthographicMatrix(static_cast(layout.width), static_cast(layout.height)); +void Layer::SetMatrixData(const Device& device, PresentPushConstants& data, const Layout::FramebufferLayout& layout) const { + data.modelview_matrix = MakeOrthographicMatrix(f32(layout.width), static_cast(layout.height)); } -void Layer::SetVertexData(PresentPushConstants& data, const Layout::FramebufferLayout& layout, - const Common::Rectangle& crop) const { +void Layer::SetVertexData(const Device& device, PresentPushConstants& data, const Layout::FramebufferLayout& layout, const Common::Rectangle& crop) const { // Map the coordinates to the screen. const auto& screen = layout.screen; - const auto x = static_cast(screen.left); - const auto y = static_cast(screen.top); - const auto w = static_cast(screen.GetWidth()); - const auto h = static_cast(screen.GetHeight()); + const auto x = f32(screen.left); + const auto y = f32(screen.top); + const auto w = f32(screen.GetWidth()); + const auto h = f32(screen.GetHeight()); data.vertices[0] = ScreenRectVertex(x, y, crop.left, crop.top); data.vertices[1] = ScreenRectVertex(x + w, y, crop.right, crop.top); @@ -242,7 +249,7 @@ void Layer::SetVertexData(PresentPushConstants& data, const Layout::FramebufferL data.vertices[3] = ScreenRectVertex(x + w, y + h, crop.right, crop.bottom); } -void Layer::UpdateDescriptorSet(VkImageView image_view, VkSampler sampler, size_t image_index) { +void Layer::UpdateDescriptorSet(const Device& device, VkImageView image_view, VkSampler sampler, size_t image_index) { const VkDescriptorImageInfo image_info{ .sampler = sampler, .imageView = image_view, diff --git a/src/video_core/renderer_vulkan/present/layer.h b/src/video_core/renderer_vulkan/present/layer.h index d38b81823e..4d75e86ba6 100644 --- a/src/video_core/renderer_vulkan/present/layer.h +++ b/src/video_core/renderer_vulkan/present/layer.h @@ -13,6 +13,7 @@ #include "video_core/host1x/gpu_device_memory_manager.h" #include "video_core/vulkan_common/vulkan_wrapper.h" #include "video_core/renderer_vulkan/present/fsr.h" +#include "video_core/renderer_vulkan/present/sgsr.h" #include "video_core/renderer_vulkan/present/fxaa.h" #include "video_core/renderer_vulkan/present/smaa.h" @@ -51,33 +52,31 @@ public: const PresentFilters& filters); ~Layer(); - void ConfigureDraw(PresentPushConstants* out_push_constants, + void ConfigureDraw(const Device& device, PresentPushConstants* out_push_constants, VkDescriptorSet* out_descriptor_set, RasterizerVulkan& rasterizer, VkSampler sampler, size_t image_index, const Tegra::FramebufferConfig& framebuffer, const Layout::FramebufferLayout& layout); private: - void CreateDescriptorPool(); - void CreateDescriptorSets(VkDescriptorSetLayout layout); - void CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer); - void CreateRawImages(const Tegra::FramebufferConfig& framebuffer); + void CreateDescriptorPool(const Device& device); + void CreateDescriptorSets(const Device& device, VkDescriptorSetLayout layout); + void CreateStagingBuffer(const Device& device, const Tegra::FramebufferConfig& framebuffer); + void CreateRawImages(const Device& device, const Tegra::FramebufferConfig& framebuffer); - void RefreshResources(const Tegra::FramebufferConfig& framebuffer); - void SetAntiAliasPass(); + void RefreshResources(const Device& device, const Tegra::FramebufferConfig& framebuffer); + void SetAntiAliasPass(const Device& device); void ReleaseRawImages(); u64 CalculateBufferSize(const Tegra::FramebufferConfig& framebuffer) const; u64 GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer, size_t image_index) const; - void SetMatrixData(PresentPushConstants& data, const Layout::FramebufferLayout& layout) const; - void SetVertexData(PresentPushConstants& data, const Layout::FramebufferLayout& layout, - const Common::Rectangle& crop) const; - void UpdateDescriptorSet(VkImageView image_view, VkSampler sampler, size_t image_index); + void SetMatrixData(const Device& device, PresentPushConstants& data, const Layout::FramebufferLayout& layout) const; + void SetVertexData(const Device& device, PresentPushConstants& data, const Layout::FramebufferLayout& layout, const Common::Rectangle& crop) const; + void UpdateDescriptorSet(const Device& device, VkImageView image_view, VkSampler sampler, size_t image_index); void UpdateRawImage(const Tegra::FramebufferConfig& framebuffer, size_t image_index); private: - const Device& device; MemoryAllocator& memory_allocator; Scheduler& scheduler; Tegra::MaxwellDeviceMemoryManager& device_memory; @@ -95,7 +94,7 @@ private: Settings::AntiAliasing anti_alias_setting{}; std::variant anti_alias{}; - std::optional fsr{}; + std::variant sr_filter{}; std::vector resource_ticks{}; }; diff --git a/src/video_core/renderer_vulkan/present/sgsr.cpp b/src/video_core/renderer_vulkan/present/sgsr.cpp new file mode 100644 index 0000000000..5fc746458d --- /dev/null +++ b/src/video_core/renderer_vulkan/present/sgsr.cpp @@ -0,0 +1,142 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "common/common_types.h" +#include "common/div_ceil.h" +#include "common/settings.h" + +//#include "video_core/sgsr.h" +#include "video_core/host_shaders/sgsr1_shader_mobile_frag_spv.h" +#include "video_core/host_shaders/sgsr1_shader_mobile_edge_direction_frag_spv.h" +#include "video_core/host_shaders/sgsr1_shader_vert_spv.h" +#include "video_core/renderer_vulkan/present/sgsr.h" +#include "video_core/renderer_vulkan/present/util.h" +#include "video_core/renderer_vulkan/vk_scheduler.h" +#include "video_core/renderer_vulkan/vk_shader_util.h" +#include "video_core/vulkan_common/vulkan_device.h" + +namespace Vulkan { + +using PushConstants = std::array; + +SGSR::SGSR(const Device& device, MemoryAllocator& memory_allocator, size_t image_count, VkExtent2D extent, bool edge_dir) + : m_memory_allocator{memory_allocator} + , m_image_count{image_count} + , m_extent{extent} + , m_edge_dir{edge_dir} +{ + // Not finished yet initializing at ctor time? + m_dynamic_images.resize(m_image_count); + for (auto& images : m_dynamic_images) { + images.image = CreateWrappedImage(m_memory_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT); + images.image_view = CreateWrappedImageView(device, images.image, VK_FORMAT_R16G16B16A16_SFLOAT); + } + + m_renderpass = CreateWrappedRenderPass(device, VK_FORMAT_R16G16B16A16_SFLOAT); + for (auto& images : m_dynamic_images) + images.framebuffer = CreateWrappedFramebuffer(device, m_renderpass, images.image_view, m_extent); + + m_sampler = CreateBilinearSampler(device); + m_vert_shader = BuildShader(device, SGSR1_SHADER_VERT_SPV); + m_stage_shader = m_edge_dir + ? BuildShader(device, SGSR1_SHADER_MOBILE_EDGE_DIRECTION_FRAG_SPV) + : BuildShader(device, SGSR1_SHADER_MOBILE_FRAG_SPV); + // 2 descriptors, 2 descriptor sets per invocation + m_descriptor_pool = CreateWrappedDescriptorPool(device, m_image_count, m_image_count); + m_descriptor_set_layout = CreateWrappedDescriptorSetLayout(device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}); + + VkDescriptorSetLayout layout = *m_descriptor_set_layout; + for (auto& images : m_dynamic_images) + images.descriptor_sets = CreateWrappedDescriptorSets(m_descriptor_pool, layout); + + const VkPushConstantRange range{ + .stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, + .offset = 0, + .size = sizeof(PushConstants), + }; + VkPipelineLayoutCreateInfo ci{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .setLayoutCount = 1, + .pSetLayouts = m_descriptor_set_layout.address(), + .pushConstantRangeCount = 1, + .pPushConstantRanges = &range, + }; + m_pipeline_layout = device.GetLogical().CreatePipelineLayout(ci); + m_stage_pipeline = CreateWrappedPipeline(device, m_renderpass, m_pipeline_layout, std::tie(m_vert_shader, m_stage_shader)); +} + +void SGSR::UpdateDescriptorSets(const Device& device, VkImageView image_view, size_t image_index) { + Images& images = m_dynamic_images[image_index]; + std::vector image_infos; + std::vector updates; + image_infos.reserve(1); + updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, image_view, images.descriptor_sets[0], 0)); + device.GetLogical().UpdateDescriptorSets(updates, {}); +} + +void SGSR::UploadImages(const Device& device, Scheduler& scheduler) { + if (!m_images_ready) { + scheduler.Record([&](vk::CommandBuffer cmdbuf) { + for (auto& image : m_dynamic_images) + ClearColorImage(cmdbuf, *image.image); + }); + scheduler.Finish(); + m_images_ready = true; + } +} + +VkImageView SGSR::Draw(const Device& device, Scheduler& scheduler, size_t image_index, VkImage source_image, VkImageView source_image_view, VkExtent2D input_image_extent, const Common::Rectangle& crop_rect) { + Images& images = m_dynamic_images[image_index]; + auto const output_image = *images.image; + auto const descriptor_set = images.descriptor_sets[0]; + auto const framebuffer = *images.framebuffer; + auto const pipeline = *m_stage_pipeline; + + VkPipelineLayout layout = *m_pipeline_layout; + VkRenderPass renderpass = *m_renderpass; + VkExtent2D extent = m_extent; + + const f32 input_image_width = f32(input_image_extent.width); + const f32 input_image_height = f32(input_image_extent.height); + const f32 viewport_width = (crop_rect.right - crop_rect.left) * input_image_width; + const f32 viewport_height = (crop_rect.bottom - crop_rect.top) * input_image_height; + // expected [0, 2] + const f32 sharpening = f32(Settings::values.fsr_sharpening_slider.GetValue()) / 100.0f; + + // p = (tex * viewport) / input = [0,n] (normalized texcoords) + // p * input = [0,1024], [0,768] + // layout( push_constant ) uniform constants { + // highp vec4 ViewportInfo[1]; + // highp vec2 ResizeFactor; + // highp float EdgeSharpness; + // }; + PushConstants viewport_con{}; + viewport_con[0] = std::bit_cast(std::abs(1.f / viewport_width)); + viewport_con[1] = std::bit_cast(std::abs(1.f / viewport_height)); + viewport_con[2] = std::bit_cast(std::abs(viewport_width)); + viewport_con[3] = std::bit_cast(std::abs(viewport_height)); + viewport_con[4] = std::bit_cast(viewport_width / input_image_width); + viewport_con[5] = std::bit_cast(viewport_height / input_image_height); + viewport_con[6] = std::bit_cast(sharpening); + + UploadImages(device, scheduler); + UpdateDescriptorSets(device, source_image_view, image_index); + + scheduler.RequestOutsideRenderPassOperationContext(); + scheduler.Record([=](vk::CommandBuffer cmdbuf) { + TransitionImageLayout(cmdbuf, source_image, VK_IMAGE_LAYOUT_GENERAL); + TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL); + BeginRenderPass(cmdbuf, renderpass, framebuffer, extent); + cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set, {}); + cmdbuf.PushConstants(layout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, viewport_con); + cmdbuf.Draw(3, 1, 0, 0); + cmdbuf.EndRenderPass(); + TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL); + }); + return *images.image_view; +} + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/sgsr.h b/src/video_core/renderer_vulkan/present/sgsr.h new file mode 100644 index 0000000000..42ba00898f --- /dev/null +++ b/src/video_core/renderer_vulkan/present/sgsr.h @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/math_util.h" +#include "video_core/vulkan_common/vulkan_memory_allocator.h" +#include "video_core/vulkan_common/vulkan_wrapper.h" + +namespace Vulkan { + +class Device; +class Scheduler; + +class SGSR { +public: + static constexpr size_t SGSR_STAGE_COUNT = 1; + explicit SGSR(const Device& device, MemoryAllocator& memory_allocator, size_t image_count, VkExtent2D extent, bool edge_dir); + VkImageView Draw(const Device& device, Scheduler& scheduler, size_t image_index, VkImage source_image, VkImageView source_image_view, VkExtent2D input_image_extent, const Common::Rectangle& crop_rect); +private: + void Initialize(const Device& device); + void UploadImages(const Device& device, Scheduler& scheduler); + void UpdateDescriptorSets(const Device& device, VkImageView image_view, size_t image_index); + + MemoryAllocator& m_memory_allocator; + const size_t m_image_count; + const VkExtent2D m_extent; + + vk::DescriptorPool m_descriptor_pool; + vk::DescriptorSetLayout m_descriptor_set_layout; + vk::PipelineLayout m_pipeline_layout; + vk::ShaderModule m_vert_shader; + vk::ShaderModule m_stage_shader; + vk::Pipeline m_stage_pipeline; + vk::RenderPass m_renderpass; + vk::Sampler m_sampler; + + struct Images { + vk::DescriptorSets descriptor_sets; + vk::Image image; + vk::ImageView image_view; + vk::Framebuffer framebuffer; + }; + std::vector m_dynamic_images; + bool m_images_ready{}; + bool m_edge_dir{}; +}; + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/smaa.cpp b/src/video_core/renderer_vulkan/present/smaa.cpp index 686112f2f0..28b0427fa0 100644 --- a/src/video_core/renderer_vulkan/present/smaa.cpp +++ b/src/video_core/renderer_vulkan/present/smaa.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project @@ -27,22 +27,24 @@ namespace Vulkan { SMAA::SMAA(const Device& device, MemoryAllocator& allocator, size_t image_count, VkExtent2D extent) - : m_device(device), m_allocator(allocator), m_extent(extent), - m_image_count(static_cast(image_count)) { - CreateImages(); - CreateRenderPasses(); - CreateSampler(); - CreateShaders(); - CreateDescriptorPool(); - CreateDescriptorSetLayouts(); - CreateDescriptorSets(); - CreatePipelineLayouts(); - CreatePipelines(); + : m_allocator(allocator) + , m_extent(extent) + , m_image_count(u32(image_count)) +{ + CreateImages(device); + CreateRenderPasses(device); + CreateSampler(device); + CreateShaders(device); + CreateDescriptorPool(device); + CreateDescriptorSetLayouts(device); + CreateDescriptorSets(device); + CreatePipelineLayouts(device); + CreatePipelines(device); } SMAA::~SMAA() = default; -void SMAA::CreateImages() { +void SMAA::CreateImages(const Device& device) { static constexpr VkExtent2D area_extent{AREATEX_WIDTH, AREATEX_HEIGHT}; static constexpr VkExtent2D search_extent{SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT}; @@ -50,9 +52,9 @@ void SMAA::CreateImages() { m_static_images[Search] = CreateWrappedImage(m_allocator, search_extent, VK_FORMAT_R8_UNORM); m_static_image_views[Area] = - CreateWrappedImageView(m_device, m_static_images[Area], VK_FORMAT_R8G8_UNORM); + CreateWrappedImageView(device, m_static_images[Area], VK_FORMAT_R8G8_UNORM); m_static_image_views[Search] = - CreateWrappedImageView(m_device, m_static_images[Search], VK_FORMAT_R8_UNORM); + CreateWrappedImageView(device, m_static_images[Search], VK_FORMAT_R8_UNORM); for (u32 i = 0; i < m_image_count; i++) { Images& images = m_dynamic_images.emplace_back(); @@ -64,39 +66,39 @@ void SMAA::CreateImages() { CreateWrappedImage(m_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT); images.image_views[Blend] = - CreateWrappedImageView(m_device, images.images[Blend], VK_FORMAT_R16G16B16A16_SFLOAT); + CreateWrappedImageView(device, images.images[Blend], VK_FORMAT_R16G16B16A16_SFLOAT); images.image_views[Edges] = - CreateWrappedImageView(m_device, images.images[Edges], VK_FORMAT_R16G16_SFLOAT); + CreateWrappedImageView(device, images.images[Edges], VK_FORMAT_R16G16_SFLOAT); images.image_views[Output] = - CreateWrappedImageView(m_device, images.images[Output], VK_FORMAT_R16G16B16A16_SFLOAT); + CreateWrappedImageView(device, images.images[Output], VK_FORMAT_R16G16B16A16_SFLOAT); } } -void SMAA::CreateRenderPasses() { - m_renderpasses[EdgeDetection] = CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16_SFLOAT); +void SMAA::CreateRenderPasses(const Device& device) { + m_renderpasses[EdgeDetection] = CreateWrappedRenderPass(device, VK_FORMAT_R16G16_SFLOAT); m_renderpasses[BlendingWeightCalculation] = - CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16B16A16_SFLOAT); + CreateWrappedRenderPass(device, VK_FORMAT_R16G16B16A16_SFLOAT); m_renderpasses[NeighborhoodBlending] = - CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16B16A16_SFLOAT); + CreateWrappedRenderPass(device, VK_FORMAT_R16G16B16A16_SFLOAT); for (auto& images : m_dynamic_images) { images.framebuffers[EdgeDetection] = CreateWrappedFramebuffer( - m_device, m_renderpasses[EdgeDetection], images.image_views[Edges], m_extent); + device, m_renderpasses[EdgeDetection], images.image_views[Edges], m_extent); images.framebuffers[BlendingWeightCalculation] = - CreateWrappedFramebuffer(m_device, m_renderpasses[BlendingWeightCalculation], + CreateWrappedFramebuffer(device, m_renderpasses[BlendingWeightCalculation], images.image_views[Blend], m_extent); images.framebuffers[NeighborhoodBlending] = CreateWrappedFramebuffer( - m_device, m_renderpasses[NeighborhoodBlending], images.image_views[Output], m_extent); + device, m_renderpasses[NeighborhoodBlending], images.image_views[Output], m_extent); } } -void SMAA::CreateSampler() { - m_sampler = CreateWrappedSampler(m_device); +void SMAA::CreateSampler(const Device& device) { + m_sampler = CreateWrappedSampler(device); } -void SMAA::CreateShaders() { +void SMAA::CreateShaders(const Device& device) { // These match the order of the SMAAStage enum static constexpr std::array vert_shader_sources{ ARRAY_TO_SPAN(SMAA_EDGE_DETECTION_VERT_SPV), @@ -110,33 +112,33 @@ void SMAA::CreateShaders() { }; for (size_t i = 0; i < MaxSMAAStage; i++) { - m_vertex_shaders[i] = CreateWrappedShaderModule(m_device, vert_shader_sources[i]); - m_fragment_shaders[i] = CreateWrappedShaderModule(m_device, frag_shader_sources[i]); + m_vertex_shaders[i] = CreateWrappedShaderModule(device, vert_shader_sources[i]); + m_fragment_shaders[i] = CreateWrappedShaderModule(device, frag_shader_sources[i]); } } -void SMAA::CreateDescriptorPool() { +void SMAA::CreateDescriptorPool(const Device& device) { // Edge detection: 1 descriptor // Blending weight calculation: 3 descriptors // Neighborhood blending: 2 descriptors // 6 descriptors, 3 descriptor sets per image - m_descriptor_pool = CreateWrappedDescriptorPool(m_device, 6 * m_image_count, 3 * m_image_count); + m_descriptor_pool = CreateWrappedDescriptorPool(device, 6 * m_image_count, 3 * m_image_count); } -void SMAA::CreateDescriptorSetLayouts() { +void SMAA::CreateDescriptorSetLayouts(const Device& device) { m_descriptor_set_layouts[EdgeDetection] = - CreateWrappedDescriptorSetLayout(m_device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}); + CreateWrappedDescriptorSetLayout(device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}); m_descriptor_set_layouts[BlendingWeightCalculation] = - CreateWrappedDescriptorSetLayout(m_device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + CreateWrappedDescriptorSetLayout(device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}); m_descriptor_set_layouts[NeighborhoodBlending] = - CreateWrappedDescriptorSetLayout(m_device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + CreateWrappedDescriptorSetLayout(device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}); } -void SMAA::CreateDescriptorSets() { +void SMAA::CreateDescriptorSets(const Device& device) { std::vector layouts(m_descriptor_set_layouts.size()); std::ranges::transform(m_descriptor_set_layouts, layouts.begin(), [](auto& layout) { return *layout; }); @@ -146,21 +148,21 @@ void SMAA::CreateDescriptorSets() { } } -void SMAA::CreatePipelineLayouts() { +void SMAA::CreatePipelineLayouts(const Device& device) { for (size_t i = 0; i < MaxSMAAStage; i++) { - m_pipeline_layouts[i] = CreateWrappedPipelineLayout(m_device, m_descriptor_set_layouts[i]); + m_pipeline_layouts[i] = CreateWrappedPipelineLayout(device, m_descriptor_set_layouts[i]); } } -void SMAA::CreatePipelines() { +void SMAA::CreatePipelines(const Device& device) { for (size_t i = 0; i < MaxSMAAStage; i++) { m_pipelines[i] = - CreateWrappedPipeline(m_device, m_renderpasses[i], m_pipeline_layouts[i], + CreateWrappedPipeline(device, m_renderpasses[i], m_pipeline_layouts[i], std::tie(m_vertex_shaders[i], m_fragment_shaders[i])); } } -void SMAA::UpdateDescriptorSets(VkImageView image_view, size_t image_index) { +void SMAA::UpdateDescriptorSets(const Device& device, VkImageView image_view, size_t image_index) { Images& images = m_dynamic_images[image_index]; std::vector image_infos; std::vector updates; @@ -184,10 +186,10 @@ void SMAA::UpdateDescriptorSets(VkImageView image_view, size_t image_index) { updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, *images.image_views[Blend], images.descriptor_sets[NeighborhoodBlending], 1)); - m_device.GetLogical().UpdateDescriptorSets(updates, {}); + device.GetLogical().UpdateDescriptorSets(updates, {}); } -void SMAA::UploadImages(Scheduler& scheduler) { +void SMAA::UploadImages(const Device& device, Scheduler& scheduler) { if (m_images_ready) { return; } @@ -195,9 +197,9 @@ void SMAA::UploadImages(Scheduler& scheduler) { static constexpr VkExtent2D area_extent{AREATEX_WIDTH, AREATEX_HEIGHT}; static constexpr VkExtent2D search_extent{SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT}; - UploadImage(m_device, m_allocator, scheduler, m_static_images[Area], area_extent, + UploadImage(device, m_allocator, scheduler, m_static_images[Area], area_extent, VK_FORMAT_R8G8_UNORM, ARRAY_TO_SPAN(areaTexBytes)); - UploadImage(m_device, m_allocator, scheduler, m_static_images[Search], search_extent, + UploadImage(device, m_allocator, scheduler, m_static_images[Search], search_extent, VK_FORMAT_R8_UNORM, ARRAY_TO_SPAN(searchTexBytes)); scheduler.Record([&](vk::CommandBuffer cmdbuf) { @@ -212,8 +214,7 @@ void SMAA::UploadImages(Scheduler& scheduler) { m_images_ready = true; } -void SMAA::Draw(Scheduler& scheduler, size_t image_index, VkImage* inout_image, - VkImageView* inout_image_view) { +void SMAA::Draw(const Device& device, Scheduler& scheduler, size_t image_index, VkImage* inout_image, VkImageView* inout_image_view) { Images& images = m_dynamic_images[image_index]; VkImage input_image = *inout_image; @@ -232,8 +233,8 @@ void SMAA::Draw(Scheduler& scheduler, size_t image_index, VkImage* inout_image, *images.framebuffers[BlendingWeightCalculation]; VkFramebuffer neighborhood_blending_framebuffer = *images.framebuffers[NeighborhoodBlending]; - UploadImages(scheduler); - UpdateDescriptorSets(*inout_image_view, image_index); + UploadImages(device, scheduler); + UpdateDescriptorSets(device, *inout_image_view, image_index); scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([=, this](vk::CommandBuffer cmdbuf) { diff --git a/src/video_core/renderer_vulkan/present/smaa.h b/src/video_core/renderer_vulkan/present/smaa.h index fdf6def070..a547161395 100644 --- a/src/video_core/renderer_vulkan/present/smaa.h +++ b/src/video_core/renderer_vulkan/present/smaa.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -16,11 +19,10 @@ class StagingBufferPool; class SMAA final : public AntiAliasPass { public: - explicit SMAA(const Device& device, MemoryAllocator& allocator, size_t image_count, - VkExtent2D extent); + explicit SMAA(const Device& device, MemoryAllocator& allocator, size_t image_count, VkExtent2D extent); ~SMAA() override; - void Draw(Scheduler& scheduler, size_t image_index, VkImage* inout_image, + void Draw(const Device& device, Scheduler& scheduler, size_t image_index, VkImage* inout_image, VkImageView* inout_image_view) override; private: @@ -44,19 +46,18 @@ private: MaxDynamicImage = 3, }; - void CreateImages(); - void CreateRenderPasses(); - void CreateSampler(); - void CreateShaders(); - void CreateDescriptorPool(); - void CreateDescriptorSetLayouts(); - void CreateDescriptorSets(); - void CreatePipelineLayouts(); - void CreatePipelines(); - void UpdateDescriptorSets(VkImageView image_view, size_t image_index); - void UploadImages(Scheduler& scheduler); + void CreateImages(const Device& device); + void CreateRenderPasses(const Device& device); + void CreateSampler(const Device& device); + void CreateShaders(const Device& device); + void CreateDescriptorPool(const Device& device); + void CreateDescriptorSetLayouts(const Device& device); + void CreateDescriptorSets(const Device& device); + void CreatePipelineLayouts(const Device& device); + void CreatePipelines(const Device& device); + void UpdateDescriptorSets(const Device& device, VkImageView image_view, size_t image_index); + void UploadImages(const Device& device, Scheduler& scheduler); - const Device& m_device; MemoryAllocator& m_allocator; const VkExtent2D m_extent; const u32 m_image_count; diff --git a/src/video_core/renderer_vulkan/present/util.cpp b/src/video_core/renderer_vulkan/present/util.cpp index a2c4727703..2b83902bc0 100644 --- a/src/video_core/renderer_vulkan/present/util.cpp +++ b/src/video_core/renderer_vulkan/present/util.cpp @@ -69,7 +69,7 @@ void TransitionImageLayout(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayo .layerCount = 1, }, }; - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, 0, barrier); } @@ -171,10 +171,10 @@ void DownloadColorImage(vk::CommandBuffer& cmdbuf, VkImage image, VkBuffer buffe .imageOffset{.x = 0, .y = 0, .z = 0}, .imageExtent{extent}, }; - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, + cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, read_barrier); cmdbuf.CopyImageToBuffer(image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, buffer, copy); - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, 0, memory_write_barrier, nullptr, image_write_barrier); } diff --git a/src/video_core/renderer_vulkan/present/window_adapt_pass.cpp b/src/video_core/renderer_vulkan/present/window_adapt_pass.cpp index 22ffacf119..549b0660c1 100644 --- a/src/video_core/renderer_vulkan/present/window_adapt_pass.cpp +++ b/src/video_core/renderer_vulkan/present/window_adapt_pass.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -15,19 +18,20 @@ namespace Vulkan { -WindowAdaptPass::WindowAdaptPass(const Device& device_, VkFormat frame_format, - vk::Sampler&& sampler_, vk::ShaderModule&& fragment_shader_) - : device(device_), sampler(std::move(sampler_)), fragment_shader(std::move(fragment_shader_)) { - CreateDescriptorSetLayout(); - CreatePipelineLayout(); - CreateVertexShader(); - CreateRenderPass(frame_format); - CreatePipelines(); +WindowAdaptPass::WindowAdaptPass(const Device& device, VkFormat frame_format, vk::Sampler&& sampler_, vk::ShaderModule&& fragment_shader_) + : sampler(std::move(sampler_)) + , fragment_shader(std::move(fragment_shader_)) +{ + CreateDescriptorSetLayout(device); + CreatePipelineLayout(device); + CreateVertexShader(device); + CreateRenderPass(device, frame_format); + CreatePipelines(device); } WindowAdaptPass::~WindowAdaptPass() = default; -void WindowAdaptPass::Draw(RasterizerVulkan& rasterizer, Scheduler& scheduler, size_t image_index, +void WindowAdaptPass::Draw(const Device& device, RasterizerVulkan& rasterizer, Scheduler& scheduler, size_t image_index, std::list& layers, std::span configs, const Layout::FramebufferLayout& layout, Frame* dst) { @@ -60,7 +64,7 @@ void WindowAdaptPass::Draw(RasterizerVulkan& rasterizer, Scheduler& scheduler, s break; } - layer_it->ConfigureDraw(&push_constants[i], &descriptor_sets[i], rasterizer, *sampler, + layer_it->ConfigureDraw(device, &push_constants[i], &descriptor_sets[i], rasterizer, *sampler, image_index, configs[i], layout); layer_it++; } @@ -111,12 +115,12 @@ VkRenderPass WindowAdaptPass::GetRenderPass() { return *render_pass; } -void WindowAdaptPass::CreateDescriptorSetLayout() { +void WindowAdaptPass::CreateDescriptorSetLayout(const Device& device) { descriptor_set_layout = CreateWrappedDescriptorSetLayout(device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}); } -void WindowAdaptPass::CreatePipelineLayout() { +void WindowAdaptPass::CreatePipelineLayout(const Device& device) { const VkPushConstantRange range{ .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, .offset = 0, @@ -134,15 +138,15 @@ void WindowAdaptPass::CreatePipelineLayout() { }); } -void WindowAdaptPass::CreateVertexShader() { +void WindowAdaptPass::CreateVertexShader(const Device& device) { vertex_shader = BuildShader(device, VULKAN_PRESENT_VERT_SPV); } -void WindowAdaptPass::CreateRenderPass(VkFormat frame_format) { +void WindowAdaptPass::CreateRenderPass(const Device& device, VkFormat frame_format) { render_pass = CreateWrappedRenderPass(device, frame_format, VK_IMAGE_LAYOUT_UNDEFINED); } -void WindowAdaptPass::CreatePipelines() { +void WindowAdaptPass::CreatePipelines(const Device& device) { opaque_pipeline = CreateWrappedPipeline(device, render_pass, pipeline_layout, std::tie(vertex_shader, fragment_shader)); premultiplied_pipeline = CreateWrappedPremultipliedBlendingPipeline( diff --git a/src/video_core/renderer_vulkan/present/window_adapt_pass.h b/src/video_core/renderer_vulkan/present/window_adapt_pass.h index cf667a4fc6..58d6fc01c0 100644 --- a/src/video_core/renderer_vulkan/present/window_adapt_pass.h +++ b/src/video_core/renderer_vulkan/present/window_adapt_pass.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -30,7 +33,7 @@ public: vk::ShaderModule&& fragment_shader); ~WindowAdaptPass(); - void Draw(RasterizerVulkan& rasterizer, Scheduler& scheduler, size_t image_index, + void Draw(const Device& device, RasterizerVulkan& rasterizer, Scheduler& scheduler, size_t image_index, std::list& layers, std::span configs, const Layout::FramebufferLayout& layout, Frame* dst); @@ -38,14 +41,12 @@ public: VkRenderPass GetRenderPass(); private: - void CreateDescriptorSetLayout(); - void CreatePipelineLayout(); - void CreateVertexShader(); - void CreateRenderPass(VkFormat frame_format); - void CreatePipelines(); + void CreateDescriptorSetLayout(const Device& device); + void CreatePipelineLayout(const Device& device); + void CreateVertexShader(const Device& device); + void CreateRenderPass(const Device& device, VkFormat frame_format); + void CreatePipelines(const Device& device); -private: - const Device& device; vk::DescriptorSetLayout descriptor_set_layout; vk::PipelineLayout pipeline_layout; vk::Sampler sampler; diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 2deec13ace..b0a7d0c579 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #include @@ -90,11 +89,10 @@ std::string BuildCommaSeparatedExtensions( } // Anonymous namespace -Device CreateDevice(const vk::Instance& instance, const vk::InstanceDispatch& dld, - VkSurfaceKHR surface) { +Device CreateDevice(const vk::Instance& instance, const vk::InstanceDispatch& dld, VkSurfaceKHR surface) { const std::vector devices = instance.EnumeratePhysicalDevices(); - const s32 device_index = Settings::values.vulkan_device.GetValue(); - if (device_index < 0 || device_index >= static_cast(devices.size())) { + const u32 device_index = Settings::values.vulkan_device.GetValue(); + if (device_index >= u32(devices.size())) { LOG_ERROR(Render_Vulkan, "Invalid device index {}!", device_index); throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); } @@ -172,11 +170,7 @@ try RendererVulkan::~RendererVulkan() { scheduler.RegisterOnSubmit([] {}); - scheduler.Finish(); - { - std::scoped_lock lock{scheduler.submit_mutex}; - void(device.GetLogical().WaitIdle()); - } + void(device.GetLogical().WaitIdle()); } void RendererVulkan::Composite(std::span framebuffers) { @@ -194,7 +188,7 @@ void RendererVulkan::Composite(std::span framebu Frame* frame = present_manager.GetRenderFrame(); scheduler.RequestOutsideRenderPassOperationContext(); - blit_swapchain.DrawToFrame(rasterizer, frame, framebuffers, + blit_swapchain.DrawToFrame(device, rasterizer, frame, framebuffers, render_window.GetFramebufferLayout(), swapchain.GetImageCount(), swapchain.GetImageViewFormat()); scheduler.Flush(*frame->render_ready); @@ -232,12 +226,12 @@ vk::Buffer RendererVulkan::RenderToBuffer(std::span -#include #include "video_core/framebuffer_config.h" #include "video_core/present.h" #include "video_core/renderer_vulkan/present/filters.h" @@ -19,26 +18,26 @@ namespace Vulkan { -BlitScreen::BlitScreen(Tegra::MaxwellDeviceMemoryManager& device_memory_, const Device& device_, - MemoryAllocator& memory_allocator_, PresentManager& present_manager_, - Scheduler& scheduler_, const PresentFilters& filters_) - : device_memory{device_memory_}, device{device_}, memory_allocator{memory_allocator_}, - present_manager{present_manager_}, scheduler{scheduler_}, filters{filters_}, - image_count{1}, image_index{0}, - swapchain_view_format{VK_FORMAT_B8G8R8A8_UNORM} {} +BlitScreen::BlitScreen(Tegra::MaxwellDeviceMemoryManager& device_memory_, const Device& device_, MemoryAllocator& memory_allocator_, PresentManager& present_manager_, Scheduler& scheduler_, const PresentFilters& filters_) + : device_memory{device_memory_} + , memory_allocator{memory_allocator_} + , present_manager{present_manager_} + , scheduler{scheduler_} + , filters{filters_} + , image_count{1} + , image_index{0} + , swapchain_view_format{VK_FORMAT_B8G8R8A8_UNORM} +{} BlitScreen::~BlitScreen() = default; -void BlitScreen::WaitIdle() { +void BlitScreen::WaitIdle(const Device& device) { present_manager.WaitPresent(); scheduler.Finish(); - { - std::scoped_lock lock{scheduler.submit_mutex}; - device.GetLogical().WaitIdle(); - } + device.GetLogical().WaitIdle(); } -void BlitScreen::SetWindowAdaptPass() { +void BlitScreen::SetWindowAdaptPass(const Device& device) { layers.clear(); scaling_filter = filters.get_scaling_filter(); @@ -77,6 +76,8 @@ void BlitScreen::SetWindowAdaptPass() { window_adapt = MakeMmpx(device, swapchain_view_format); break; case Settings::ScalingFilter::Fsr: + case Settings::ScalingFilter::Sgsr: + case Settings::ScalingFilter::SgsrEdge: case Settings::ScalingFilter::Bilinear: default: window_adapt = MakeBilinear(device, swapchain_view_format); @@ -84,7 +85,7 @@ void BlitScreen::SetWindowAdaptPass() { } } -void BlitScreen::DrawToFrame(RasterizerVulkan& rasterizer, Frame* frame, +void BlitScreen::DrawToFrame(const Device& device, RasterizerVulkan& rasterizer, Frame* frame, std::span framebuffers, const Layout::FramebufferLayout& layout, size_t current_swapchain_image_count, @@ -109,8 +110,8 @@ void BlitScreen::DrawToFrame(RasterizerVulkan& rasterizer, Frame* frame, } if (resource_update_required) { - WaitIdle(); - SetWindowAdaptPass(); + WaitIdle(device); + SetWindowAdaptPass(device); if (presentation_recreate_required) { present_manager.RecreateFrame(frame, layout.width, layout.height, swapchain_view_format, @@ -133,22 +134,22 @@ void BlitScreen::DrawToFrame(RasterizerVulkan& rasterizer, Frame* frame, } } - window_adapt->Draw(rasterizer, scheduler, image_index, layers, framebuffers, layout, frame); + window_adapt->Draw(device, rasterizer, scheduler, image_index, layers, framebuffers, layout, frame); if (++image_index >= image_count) { image_index = 0; } } -vk::Framebuffer BlitScreen::CreateFramebuffer(const Layout::FramebufferLayout& layout, +vk::Framebuffer BlitScreen::CreateFramebuffer(const Device& device, const Layout::FramebufferLayout& layout, VkImageView image_view, VkFormat current_view_format) { bool format_updated = swapchain_view_format != current_view_format; swapchain_view_format = current_view_format; if (!window_adapt || scaling_filter != filters.get_scaling_filter() || format_updated) { - WaitIdle(); - SetWindowAdaptPass(); + WaitIdle(device); + SetWindowAdaptPass(device); image_index = 0; } @@ -157,10 +158,10 @@ vk::Framebuffer BlitScreen::CreateFramebuffer(const Layout::FramebufferLayout& l .height = layout.height, }; - return CreateFramebuffer(image_view, extent, window_adapt->GetRenderPass()); + return CreateFramebuffer(device, image_view, extent, window_adapt->GetRenderPass()); } -vk::Framebuffer BlitScreen::CreateFramebuffer(const VkImageView& image_view, VkExtent2D extent, +vk::Framebuffer BlitScreen::CreateFramebuffer(const Device& device, const VkImageView& image_view, VkExtent2D extent, VkRenderPass render_pass) { return device.GetLogical().CreateFramebuffer(VkFramebufferCreateInfo{ .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.h b/src/video_core/renderer_vulkan/vk_blit_screen.h index 531c57fc5c..e50f0bcf33 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.h +++ b/src/video_core/renderer_vulkan/vk_blit_screen.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -52,23 +55,22 @@ public: Scheduler& scheduler, const PresentFilters& filters); ~BlitScreen(); - void DrawToFrame(RasterizerVulkan& rasterizer, Frame* frame, + void DrawToFrame(const Device& device, RasterizerVulkan& rasterizer, Frame* frame, std::span framebuffers, const Layout::FramebufferLayout& layout, size_t current_swapchain_image_count, VkFormat current_swapchain_view_format); - [[nodiscard]] vk::Framebuffer CreateFramebuffer(const Layout::FramebufferLayout& layout, + [[nodiscard]] vk::Framebuffer CreateFramebuffer(const Device& device, const Layout::FramebufferLayout& layout, VkImageView image_view, VkFormat current_view_format); private: - void WaitIdle(); - void SetWindowAdaptPass(); - vk::Framebuffer CreateFramebuffer(const VkImageView& image_view, VkExtent2D extent, + void WaitIdle(const Device& device); + void SetWindowAdaptPass(const Device& device); + vk::Framebuffer CreateFramebuffer(const Device& device, const VkImageView& image_view, VkExtent2D extent, VkRenderPass render_pass); Tegra::MaxwellDeviceMemoryManager& device_memory; - const Device& device; MemoryAllocator& memory_allocator; PresentManager& present_manager; Scheduler& scheduler; diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index a359502046..7bd0a260b3 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp @@ -437,13 +437,13 @@ void BufferCacheRuntime::CopyBuffer(VkBuffer dst_buffer, VkBuffer src_buffer, scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([src_buffer, dst_buffer, vk_copies, barrier](vk::CommandBuffer cmdbuf) { if (barrier) { - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, READ_BARRIER); } cmdbuf.CopyBuffer(src_buffer, dst_buffer, VideoCommon::FixSmallVectorADL(vk_copies)); if (barrier) { cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, WRITE_BARRIER); + vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, 0, WRITE_BARRIER); } }); } @@ -457,7 +457,7 @@ void BufferCacheRuntime::PreCopyBarrier() { }; scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([](vk::CommandBuffer cmdbuf) { - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, + cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, READ_BARRIER); }); } @@ -471,7 +471,7 @@ void BufferCacheRuntime::PostCopyBarrier() { }; scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([](vk::CommandBuffer cmdbuf) { - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, 0, WRITE_BARRIER); }); } @@ -495,10 +495,10 @@ void BufferCacheRuntime::ClearBuffer(VkBuffer dest_buffer, u32 offset, size_t si scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([dest_buffer, offset, size, value](vk::CommandBuffer cmdbuf) { - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, + cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, READ_BARRIER); cmdbuf.FillBuffer(dest_buffer, offset, size, value); - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, 0, WRITE_BARRIER); }); } diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp index f198b65d69..c24b7a5757 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp @@ -234,7 +234,7 @@ struct ConditionalRenderingResolvePushConstants { }; } // Anonymous namespace -ComputePass::ComputePass(const Device& device_, DescriptorPool& descriptor_pool, +ComputePass::ComputePass(const Device& device_, Scheduler& scheduler, DescriptorPool& descriptor_pool, vk::Span bindings, vk::Span templates, const DescriptorBankInfo& bank_info, @@ -270,7 +270,7 @@ ComputePass::ComputePass(const Device& device_, DescriptorPool& descriptor_pool, .pipelineLayout = *layout, .set = 0, }); - descriptor_allocator = descriptor_pool.Allocator(*descriptor_set_layout, bank_info); + descriptor_allocator = descriptor_pool.Allocator(device, scheduler, *descriptor_set_layout, bank_info); } if (code.empty()) { return; @@ -313,7 +313,7 @@ ComputePass::~ComputePass() = default; Uint8Pass::Uint8Pass(const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool, StagingBufferPool& staging_buffer_pool_, ComputePassDescriptorQueue& compute_pass_descriptor_queue_) - : ComputePass(device_, descriptor_pool, INPUT_OUTPUT_DESCRIPTOR_SET_BINDINGS, + : ComputePass(device_, scheduler_, descriptor_pool, INPUT_OUTPUT_DESCRIPTOR_SET_BINDINGS, INPUT_OUTPUT_DESCRIPTOR_UPDATE_TEMPLATE, INPUT_OUTPUT_BANK_INFO, {}, VULKAN_UINT8_COMP_SPV), scheduler{scheduler_}, staging_buffer_pool{staging_buffer_pool_}, @@ -355,7 +355,7 @@ QuadIndexedPass::QuadIndexedPass(const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_, StagingBufferPool& staging_buffer_pool_, ComputePassDescriptorQueue& compute_pass_descriptor_queue_) - : ComputePass(device_, descriptor_pool_, INPUT_OUTPUT_DESCRIPTOR_SET_BINDINGS, + : ComputePass(device_, scheduler_, descriptor_pool_, INPUT_OUTPUT_DESCRIPTOR_SET_BINDINGS, INPUT_OUTPUT_DESCRIPTOR_UPDATE_TEMPLATE, INPUT_OUTPUT_BANK_INFO, COMPUTE_PUSH_CONSTANT_RANGE, VULKAN_QUAD_INDEXED_COMP_SPV), scheduler{scheduler_}, staging_buffer_pool{staging_buffer_pool_}, @@ -416,7 +416,7 @@ std::pair QuadIndexedPass::Assemble( ConditionalRenderingResolvePass::ConditionalRenderingResolvePass( const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_, ComputePassDescriptorQueue& compute_pass_descriptor_queue_) - : ComputePass(device_, descriptor_pool_, INPUT_OUTPUT_DESCRIPTOR_SET_BINDINGS, + : ComputePass(device_, scheduler_, descriptor_pool_, INPUT_OUTPUT_DESCRIPTOR_SET_BINDINGS, INPUT_OUTPUT_DESCRIPTOR_UPDATE_TEMPLATE, INPUT_OUTPUT_BANK_INFO, COMPUTE_PUSH_CONSTANT_RANGE, RESOLVE_CONDITIONAL_RENDER_COMP_SPV), @@ -454,8 +454,8 @@ void ConditionalRenderingResolvePass::Resolve(VkBuffer dst_buffer, VkBuffer src_ const VkDescriptorSet set = descriptor_allocator.Commit(); device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data); - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, read_barrier); + cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, read_barrier); cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline); cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {}); cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, uniforms); @@ -470,7 +470,7 @@ QueriesPrefixScanPass::QueriesPrefixScanPass( const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_, ComputePassDescriptorQueue& compute_pass_descriptor_queue_) : ComputePass( - device_, descriptor_pool_, QUERIES_SCAN_DESCRIPTOR_SET_BINDINGS, + device_, scheduler_, descriptor_pool_, QUERIES_SCAN_DESCRIPTOR_SET_BINDINGS, QUERIES_SCAN_DESCRIPTOR_UPDATE_TEMPLATE, QUERIES_SCAN_BANK_INFO, COMPUTE_PUSH_CONSTANT_RANGE, device_.IsSubgroupFeatureSupported(VK_SUBGROUP_FEATURE_BASIC_BIT) && @@ -537,7 +537,7 @@ void QueriesPrefixScanPass::Run(VkBuffer accumulation_buffer, VkBuffer dst_buffe cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, uniforms); cmdbuf.Dispatch(1, 1, 1); cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, - VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, write_barrier); + vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, 0, write_barrier); }); } } @@ -547,7 +547,7 @@ ASTCDecoderPass::ASTCDecoderPass(const Device& device_, Scheduler& scheduler_, StagingBufferPool& staging_buffer_pool_, ComputePassDescriptorQueue& compute_pass_descriptor_queue_, MemoryAllocator& memory_allocator_) - : ComputePass(device_, descriptor_pool_, ASTC_DESCRIPTOR_SET_BINDINGS, + : ComputePass(device_, scheduler_, descriptor_pool_, ASTC_DESCRIPTOR_SET_BINDINGS, ASTC_PASS_DESCRIPTOR_UPDATE_TEMPLATE_ENTRY, ASTC_BANK_INFO, COMPUTE_PUSH_CONSTANT_RANGE, ASTC_DECODER_COMP_SPV), scheduler{scheduler_}, staging_buffer_pool{staging_buffer_pool_}, @@ -589,9 +589,9 @@ void ASTCDecoderPass::Assemble(Image& image, const StagingBufferRef& map, .layerCount = VK_REMAINING_ARRAY_LAYERS, }, }; - cmdbuf.PipelineBarrier(is_initialized ? VK_PIPELINE_STAGE_ALL_COMMANDS_BIT - : VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, image_barrier); + cmdbuf.PipelineBarrier(is_initialized ? vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER + : VkPipelineStageFlags(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT), + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, image_barrier); cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, vk_pipeline); }); for (const VideoCommon::SwizzleParameters& swizzle : swizzles) { @@ -648,7 +648,7 @@ void ASTCDecoderPass::Assemble(Image& image, const StagingBufferRef& map, }, }; cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, - VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, image_barrier); + vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, 0, image_barrier); }); scheduler.Finish(); } @@ -746,7 +746,7 @@ BlockLinearUnswizzle3DPass::BlockLinearUnswizzle3DPass( StagingBufferPool& staging_buffer_pool_, ComputePassDescriptorQueue& compute_pass_descriptor_queue_) : ComputePass( - device_, descriptor_pool_, + device_, scheduler_, descriptor_pool_, BL3D_DESCRIPTOR_SET_BINDINGS, BL3D_DESCRIPTOR_UPDATE_TEMPLATE_ENTRY, BL3D_BANK_INFO, @@ -941,7 +941,7 @@ MSAACopyPass::MSAACopyPass(const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_, StagingBufferPool& staging_buffer_pool_, ComputePassDescriptorQueue& compute_pass_descriptor_queue_) - : ComputePass(device_, descriptor_pool_, MSAA_DESCRIPTOR_SET_BINDINGS, + : ComputePass(device_, scheduler_, descriptor_pool_, MSAA_DESCRIPTOR_SET_BINDINGS, MSAA_DESCRIPTOR_UPDATE_TEMPLATE, MSAA_BANK_INFO, {}, CONVERT_NON_MSAA_TO_MSAA_COMP_SPV), scheduler{scheduler_}, staging_buffer_pool{staging_buffer_pool_}, diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.h b/src/video_core/renderer_vulkan/vk_compute_pass.h index 0e5badce01..cb213dae7e 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.h +++ b/src/video_core/renderer_vulkan/vk_compute_pass.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project @@ -35,7 +35,7 @@ struct StagingBufferRef; class ComputePass { public: - explicit ComputePass(const Device& device, DescriptorPool& descriptor_pool, + explicit ComputePass(const Device& device, Scheduler& scheduler, DescriptorPool& descriptor_pool, vk::Span bindings, vk::Span templates, const DescriptorBankInfo& bank_info, diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp index 490dd7acfe..46a3b4bcb8 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp @@ -30,7 +30,7 @@ using Shader::ImageBufferDescriptor; using Shader::Backend::SPIRV::RESCALING_LAYOUT_WORDS_OFFSET; using Tegra::Texture::TexturePair; -ComputePipeline::ComputePipeline(const Device& device_, vk::PipelineCache& pipeline_cache_, +ComputePipeline::ComputePipeline(const Device& device_, Scheduler& scheduler, vk::PipelineCache& pipeline_cache_, DescriptorPool& descriptor_pool, GuestDescriptorQueue& guest_descriptor_queue_, Common::ThreadWorker* thread_worker, @@ -46,7 +46,7 @@ ComputePipeline::ComputePipeline(const Device& device_, vk::PipelineCache& pipel std::copy_n(info.constant_buffer_used_sizes.begin(), uniform_buffer_sizes.size(), uniform_buffer_sizes.begin()); - auto func{[this, &descriptor_pool, shader_notify, pipeline_statistics] { + auto func{[this, &scheduler, &descriptor_pool, shader_notify, pipeline_statistics] { DescriptorLayoutBuilder builder{device}; builder.Add(info, VK_SHADER_STAGE_COMPUTE_BIT); @@ -56,7 +56,7 @@ ComputePipeline::ComputePipeline(const Device& device_, vk::PipelineCache& pipel descriptor_update_template = builder.CreateTemplate(*descriptor_set_layout, *pipeline_layout, uses_push_descriptor); if (!uses_push_descriptor) { - descriptor_allocator = descriptor_pool.Allocator(*descriptor_set_layout, info); + descriptor_allocator = descriptor_pool.Allocator(device, scheduler, *descriptor_set_layout, info); } const VkPipelineShaderStageRequiredSubgroupSizeCreateInfoEXT subgroup_size_ci{ .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_REQUIRED_SUBGROUP_SIZE_CREATE_INFO_EXT, @@ -87,14 +87,14 @@ ComputePipeline::ComputePipeline(const Device& device_, vk::PipelineCache& pipel }, *pipeline_cache); // Log compute pipeline creation - if (Settings::values.gpu_logging_enabled.GetValue()) { + if (GPU::Logging::IsActive()) { GPU::Logging::GPULogger::GetInstance().LogPipelineStateChange( "ComputePipeline created" ); } if (pipeline_statistics) { - pipeline_statistics->Collect(*pipeline); + pipeline_statistics->Collect(device, *pipeline); } std::scoped_lock lock{build_mutex}; is_built = true; @@ -125,7 +125,7 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute, ++ssbo_index; } - texture_cache.SynchronizeComputeDescriptors(); + texture_cache.SynchronizeDescriptors(true); boost::container::small_vector views; boost::container::small_vector samplers; @@ -173,14 +173,14 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute, const auto handle{read_handle(desc, index)}; views.push_back({handle.first}); - VideoCommon::SamplerId sampler = texture_cache.GetComputeSamplerId(handle.second); + VideoCommon::SamplerId sampler = texture_cache.GetSamplerId(handle.second, true); samplers.push_back(sampler); } } for (const auto& desc : info.image_descriptors) { add_image(desc, desc.is_written); } - texture_cache.FillComputeImageViews(std::span(views.data(), views.size())); + texture_cache.FillImageViews(std::span(views.data(), views.size()), true); buffer_cache.UnbindComputeTextureBuffers(); size_t index{}; @@ -223,7 +223,7 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute, } // Log compute pipeline binding - if (Settings::values.gpu_logging_enabled.GetValue() && + if (GPU::Logging::IsActive() && Settings::values.gpu_log_vulkan_calls.GetValue()) { GPU::Logging::GPULogger::GetInstance().LogPipelineBind(true, "compute pipeline"); } diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.h b/src/video_core/renderer_vulkan/vk_compute_pipeline.h index aa84c00e12..f3abe4c931 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.h @@ -31,7 +31,7 @@ class Scheduler; class ComputePipeline { public: - explicit ComputePipeline(const Device& device, vk::PipelineCache& pipeline_cache, + explicit ComputePipeline(const Device& device, Scheduler& scheduler, vk::PipelineCache& pipeline_cache, DescriptorPool& descriptor_pool, GuestDescriptorQueue& guest_descriptor_queue, Common::ThreadWorker* thread_worker, diff --git a/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp b/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp index 3af9758a31..b020d99b93 100644 --- a/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp +++ b/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project @@ -125,27 +125,22 @@ vk::DescriptorSets DescriptorAllocator::AllocateDescriptors(size_t count) { throw vk::Exception(VK_ERROR_OUT_OF_POOL_MEMORY); } -DescriptorPool::DescriptorPool(const Device& device_, Scheduler& scheduler) - : device{device_}, master_semaphore{scheduler.GetMasterSemaphore()} {} - +DescriptorPool::DescriptorPool(const Device& device_, Scheduler& scheduler) {} DescriptorPool::~DescriptorPool() = default; -DescriptorAllocator DescriptorPool::Allocator(VkDescriptorSetLayout layout, - std::span infos) { - return Allocator(layout, MakeBankInfo(infos)); +DescriptorAllocator DescriptorPool::Allocator(const Device& device, Scheduler& scheduler, VkDescriptorSetLayout layout, std::span infos) { + return Allocator(device, scheduler, layout, MakeBankInfo(infos)); } -DescriptorAllocator DescriptorPool::Allocator(VkDescriptorSetLayout layout, - const Shader::Info& info) { - return Allocator(layout, MakeBankInfo(std::array{info})); +DescriptorAllocator DescriptorPool::Allocator(const Device& device, Scheduler& scheduler, VkDescriptorSetLayout layout, const Shader::Info& info) { + return Allocator(device, scheduler, layout, MakeBankInfo(std::array{info})); } -DescriptorAllocator DescriptorPool::Allocator(VkDescriptorSetLayout layout, - const DescriptorBankInfo& info) { - return DescriptorAllocator(device, master_semaphore, Bank(info), layout); +DescriptorAllocator DescriptorPool::Allocator(const Device& device, Scheduler& scheduler, VkDescriptorSetLayout layout, const DescriptorBankInfo& info) { + return DescriptorAllocator(device, scheduler.GetMasterSemaphore(), Bank(device, info), layout); } -DescriptorBank& DescriptorPool::Bank(const DescriptorBankInfo& reqs) { +DescriptorBank& DescriptorPool::Bank(const Device& device, const DescriptorBankInfo& reqs) { std::shared_lock read_lock{banks_mutex}; const auto it = std::ranges::find_if(bank_infos, [&reqs](const DescriptorBankInfo& bank) { return std::abs(bank.score - reqs.score) < SCORE_THRESHOLD && bank.IsSuperset(reqs); diff --git a/src/video_core/renderer_vulkan/vk_descriptor_pool.h b/src/video_core/renderer_vulkan/vk_descriptor_pool.h index 4aada5a006..4efeb60373 100644 --- a/src/video_core/renderer_vulkan/vk_descriptor_pool.h +++ b/src/video_core/renderer_vulkan/vk_descriptor_pool.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -68,17 +71,12 @@ public: DescriptorPool& operator=(const DescriptorPool&) = delete; DescriptorPool(const DescriptorPool&) = delete; - DescriptorAllocator Allocator(VkDescriptorSetLayout layout, - std::span infos); - DescriptorAllocator Allocator(VkDescriptorSetLayout layout, const Shader::Info& info); - DescriptorAllocator Allocator(VkDescriptorSetLayout layout, const DescriptorBankInfo& info); + DescriptorAllocator Allocator(const Device& device, Scheduler& scheduler, VkDescriptorSetLayout layout, std::span infos); + DescriptorAllocator Allocator(const Device& device, Scheduler& scheduler, VkDescriptorSetLayout layout, const Shader::Info& info); + DescriptorAllocator Allocator(const Device& device, Scheduler& scheduler, VkDescriptorSetLayout layout, const DescriptorBankInfo& info); private: - DescriptorBank& Bank(const DescriptorBankInfo& reqs); - - const Device& device; - MasterSemaphore& master_semaphore; - + DescriptorBank& Bank(const Device& device, const DescriptorBankInfo& reqs); std::shared_mutex banks_mutex; std::vector bank_infos; std::vector> banks; diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 43fbefe425..01495b4515 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -49,7 +49,6 @@ using VideoCore::Surface::PixelFormatFromDepthFormat; using VideoCore::Surface::PixelFormatFromRenderTargetFormat; constexpr size_t NUM_STAGES = Maxwell::MaxShaderStage; -constexpr size_t INLINE_IMAGE_ELEMENTS = 64; DescriptorLayoutBuilder MakeBuilder(const Device& device, std::span infos) { DescriptorLayoutBuilder builder{device}; @@ -277,7 +276,7 @@ GraphicsPipeline::GraphicsPipeline( descriptor_set_layout = builder.CreateDescriptorSetLayout(uses_push_descriptor); if (!uses_push_descriptor) { - descriptor_allocator = descriptor_pool.Allocator(*descriptor_set_layout, stage_infos); + descriptor_allocator = descriptor_pool.Allocator(device, scheduler, *descriptor_set_layout, stage_infos); } const VkDescriptorSetLayout set_layout{*descriptor_set_layout}; @@ -289,7 +288,7 @@ GraphicsPipeline::GraphicsPipeline( Validate(); MakePipeline(render_pass); if (pipeline_statistics) { - pipeline_statistics->Collect(*pipeline); + pipeline_statistics->Collect(device, *pipeline); } std::scoped_lock lock{build_mutex}; @@ -314,12 +313,12 @@ void GraphicsPipeline::AddTransition(GraphicsPipeline* transition) { template bool GraphicsPipeline::ConfigureImpl(bool is_indexed) { - small_vector views; - small_vector samplers; + boost::container::small_vector views; + boost::container::small_vector samplers; views.reserve(num_image_elements); samplers.reserve(num_textures); - texture_cache.SynchronizeGraphicsDescriptors(); + texture_cache.SynchronizeDescriptors(false); buffer_cache.SetUniformBuffersState(enabled_uniform_buffer_masks, &uniform_buffer_sizes); @@ -384,7 +383,7 @@ bool GraphicsPipeline::ConfigureImpl(bool is_indexed) { const auto handle{read_handle(desc, index)}; views.push_back({handle.first}); - VideoCommon::SamplerId sampler{texture_cache.GetGraphicsSamplerId(handle.second)}; + VideoCommon::SamplerId sampler{texture_cache.GetSamplerId(handle.second, false)}; samplers.push_back(sampler); } } @@ -413,7 +412,7 @@ bool GraphicsPipeline::ConfigureImpl(bool is_indexed) { } ASSERT(views.size() == num_image_elements); ASSERT(samplers.size() == num_textures); - texture_cache.FillGraphicsImageViews(std::span(views.data(), views.size())); + texture_cache.FillImageViews(std::span(views.data(), views.size()), false, Spec::has_images); VideoCommon::ImageViewInOut* texture_buffer_it{views.data()}; const auto bind_stage_info{[&](size_t stage) LAMBDA_FORCEINLINE { @@ -533,7 +532,7 @@ void GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling, const bool bind_pipeline{scheduler.UpdateGraphicsPipeline(this)}; // Log graphics pipeline binding - if (bind_pipeline && Settings::values.gpu_logging_enabled.GetValue() && + if (bind_pipeline && GPU::Logging::IsActive() && Settings::values.gpu_log_vulkan_calls.GetValue()) { const std::string pipeline_info = fmt::format("hash=0x{:016x}", key.Hash()); GPU::Logging::GPULogger::GetInstance().LogPipelineBind(false, pipeline_info); @@ -987,7 +986,7 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { }, *pipeline_cache); // Log graphics pipeline creation - if (Settings::values.gpu_logging_enabled.GetValue()) { + if (GPU::Logging::IsActive()) { const std::string pipeline_info = fmt::format( "GraphicsPipeline created: stages={}, attachments={}", shader_stages.size(), diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index d0f98091c1..ad7bfd0718 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -45,7 +45,7 @@ #include "video_core/vulkan_common/vulkan_wrapper.h" #include "video_core/gpu_logging/gpu_logging.h" -#ifdef ANDROID +#ifdef __ANDROID__ #include "../../android/app/src/main/jni/android_settings.h" #endif @@ -328,7 +328,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span program size_t GetTotalPipelineWorkers() { const size_t max_core_threads = std::max(static_cast(std::thread::hardware_concurrency()), 2ULL) - 1ULL; -#ifdef ANDROID +#ifdef __ANDROID__ const int configured = AndroidSettings::values.pipeline_worker_count.GetValue(); const int clamped = std::clamp(configured, 4, 8); const size_t desired = static_cast(clamped); @@ -493,10 +493,13 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, dynamic_features.has_extended_dynamic_state_3_enables = device.IsExtExtendedDynamicState3EnablesSupported(); dynamic_features.has_dynamic_state3_depth_clamp_enable = + dynamic_features.has_extended_dynamic_state_3_enables && device.SupportsDynamicState3DepthClampEnable(); dynamic_features.has_dynamic_state3_logic_op_enable = + dynamic_features.has_extended_dynamic_state_3_enables && device.SupportsDynamicState3LogicOpEnable(); dynamic_features.has_dynamic_state3_line_stipple_enable = + dynamic_features.has_extended_dynamic_state_3_enables && device.SupportsDynamicState3LineStippleEnable(); // VIDS: Independent toggle (not affected by dyna_state levels) @@ -752,7 +755,7 @@ std::unique_ptr PipelineCache::CreateGraphicsPipeline( programs[index] = MergeDualVertexPrograms(program_va, program_vb, env); } - if (Settings::values.dump_shaders) { + if (Settings::values.dump_guest_shaders) { env.Dump(hash, key.unique_hashes[index]); } @@ -784,14 +787,22 @@ std::unique_ptr PipelineCache::CreateGraphicsPipeline( device.SaveShader(code); modules[stage_index] = BuildShader(device, code); - // Log shader compilation to GPU logger (with SPIR-V binary dump if enabled) - if (Settings::values.gpu_logging_enabled.GetValue()) { + // Text log + .spv dump. Text log is gated by gpu_log_level != Off; .spv dump + // is independent and gated only by gpu_log_shader_dumps. + const bool should_log = GPU::Logging::IsActive(); + const bool should_dump = Settings::values.gpu_log_shader_dumps.GetValue(); + if (should_log || should_dump) { static constexpr std::array stage_names{"vertex", "tess_control", "tess_eval", "geometry", "fragment"}; const std::string shader_name = fmt::format("shader_{:016x}_{}", key.unique_hashes[index], stage_names[stage_index]); - const std::string shader_info = fmt::format("SPIR-V size: {} bytes, hash: {:016x}", - code.size() * sizeof(u32), key.unique_hashes[index]); - GPU::Logging::GPULogger::GetInstance().LogShaderCompilation(shader_name, shader_info, - std::span(code.data(), code.size())); + if (should_log) { + const std::string shader_info = fmt::format("SPIR-V size: {} bytes, hash: {:016x}", + code.size() * sizeof(u32), key.unique_hashes[index]); + GPU::Logging::GPULogger::GetInstance().LogShaderCompilation(shader_name, shader_info); + } + if (should_dump) { + GPU::Logging::DumpSpirvShader(key.unique_hashes[index], + std::span(code.data(), code.size())); + } } if (device.HasDebuggingToolAttached()) { @@ -880,7 +891,7 @@ std::unique_ptr PipelineCache::CreateComputePipeline( Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()}; // Dump it before error. - if (Settings::values.dump_shaders) { + if (Settings::values.dump_guest_shaders) { env.Dump(hash, key.unique_hash); } @@ -902,13 +913,20 @@ std::unique_ptr PipelineCache::CreateComputePipeline( device.SaveShader(code); vk::ShaderModule spv_module{BuildShader(device, code)}; - // Log compute shader compilation to GPU logger (with SPIR-V binary dump if enabled) - if (Settings::values.gpu_logging_enabled.GetValue()) { + // Text log + .spv dump. Same split as the graphics path. + const bool should_log = GPU::Logging::IsActive(); + const bool should_dump = Settings::values.gpu_log_shader_dumps.GetValue(); + if (should_log || should_dump) { const std::string shader_name = fmt::format("shader_{:016x}_compute", key.unique_hash); - const std::string shader_info = fmt::format("SPIR-V size: {} bytes, hash: {:016x}", - code.size() * sizeof(u32), key.unique_hash); - GPU::Logging::GPULogger::GetInstance().LogShaderCompilation(shader_name, shader_info, - std::span(code.data(), code.size())); + if (should_log) { + const std::string shader_info = fmt::format("SPIR-V size: {} bytes, hash: {:016x}", + code.size() * sizeof(u32), key.unique_hash); + GPU::Logging::GPULogger::GetInstance().LogShaderCompilation(shader_name, shader_info); + } + if (should_dump) { + GPU::Logging::DumpSpirvShader(key.unique_hash, + std::span(code.data(), code.size())); + } } if (device.HasDebuggingToolAttached()) { @@ -916,7 +934,7 @@ std::unique_ptr PipelineCache::CreateComputePipeline( spv_module.SetObjectNameEXT(name.c_str()); } Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr}; - return std::make_unique(device, vulkan_pipeline_cache, descriptor_pool, + return std::make_unique(device, scheduler, vulkan_pipeline_cache, descriptor_pool, guest_descriptor_queue, thread_worker, statistics, &shader_notify, program.info, std::move(spv_module)); diff --git a/src/video_core/renderer_vulkan/vk_present_manager.cpp b/src/video_core/renderer_vulkan/vk_present_manager.cpp index de854554c7..53c3f6759f 100644 --- a/src/video_core/renderer_vulkan/vk_present_manager.cpp +++ b/src/video_core/renderer_vulkan/vk_present_manager.cpp @@ -309,7 +309,7 @@ void PresentManager::CopyToSwapchain(Frame* frame) { try { // Recreate surface and swapchain if needed. if (requires_recreation) { -#ifdef ANDROID +#ifdef __ANDROID__ surface = CreateSurface(instance, render_window.GetWindowInfo()); #endif RecreateSwapchain(frame); @@ -429,7 +429,7 @@ void PresentManager::CopyToSwapchainImpl(Frame* frame) { }, }; - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, {}, + cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, VK_PIPELINE_STAGE_TRANSFER_BIT, {}, {}, {}, pre_barriers); if (blit_supported) { diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp index 98062a78dc..52f52bd577 100644 --- a/src/video_core/renderer_vulkan/vk_query_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp @@ -126,16 +126,14 @@ public: current_query = nullptr; amend_value = 0; accumulation_value = 0; - queries_prefix_scan_pass = std::make_unique( - device, scheduler, descriptor_pool, compute_pass_descriptor_queue); + queries_prefix_scan_pass.emplace(device, scheduler, descriptor_pool, compute_pass_descriptor_queue); const VkBufferCreateInfo buffer_ci = { .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .pNext = nullptr, .flags = 0, .size = 8, - .usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | - VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, + .usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, .sharingMode = VK_SHARING_MODE_EXCLUSIVE, .queueFamilyIndexCount = 0, .pQueueFamilyIndices = nullptr, @@ -157,9 +155,8 @@ public: ReserveHostQuery(); scheduler.Record([query_pool = current_query_pool, - query_index = current_bank_slot](vk::CommandBuffer cmdbuf) { + query_index = current_bank_slot](vk::CommandBuffer cmdbuf) { const bool use_precise = Settings::IsGPULevelHigh(); - cmdbuf.ResetQueryPool(query_pool, static_cast(query_index), 1); cmdbuf.BeginQuery(query_pool, static_cast(query_index), use_precise ? VK_QUERY_CONTROL_PRECISE_BIT : 0); }); @@ -592,8 +589,7 @@ private: VideoCommon::HostQueryBase* current_query; bool has_started{}; std::mutex flush_guard; - - std::unique_ptr queries_prefix_scan_pass; + std::optional queries_prefix_scan_pass; }; // Transform feedback queries @@ -846,7 +842,7 @@ public: scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([](vk::CommandBuffer cmdbuf) { cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, WRITE_BARRIER); + vk::PIPELINE_STAGE_HOST, 0, WRITE_BARRIER); }); std::scoped_lock lk(flush_guard); @@ -1266,41 +1262,23 @@ private: } // namespace struct QueryCacheRuntimeImpl { - QueryCacheRuntimeImpl(QueryCacheRuntime& runtime, VideoCore::RasterizerInterface* rasterizer_, - Tegra::MaxwellDeviceMemoryManager& device_memory_, - Vulkan::BufferCache& buffer_cache_, const Device& device_, - const MemoryAllocator& memory_allocator_, Scheduler& scheduler_, - StagingBufferPool& staging_pool_, - ComputePassDescriptorQueue& compute_pass_descriptor_queue, - DescriptorPool& descriptor_pool, TextureCache& texture_cache_) - : rasterizer{rasterizer_}, device_memory{device_memory_}, buffer_cache{buffer_cache_}, - device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_}, - staging_pool{staging_pool_}, guest_streamer(0, runtime), - sample_streamer(static_cast(QueryType::ZPassPixelCount64), runtime, rasterizer, - texture_cache_, device, scheduler, memory_allocator, - compute_pass_descriptor_queue, descriptor_pool), - tfb_streamer(static_cast(QueryType::StreamingByteCount), runtime, device, - scheduler, memory_allocator, staging_pool), - primitives_succeeded_streamer( - static_cast(QueryType::StreamingPrimitivesSucceeded), runtime, tfb_streamer, - device_memory_), - primitives_needed_minus_succeeded_streamer( - static_cast(QueryType::StreamingPrimitivesNeededMinusSucceeded), runtime, 0u), - hcr_setup{}, hcr_is_set{}, is_hcr_running{}, maxwell3d{} { + QueryCacheRuntimeImpl(QueryCacheRuntime& runtime, VideoCore::RasterizerInterface* rasterizer_, Tegra::MaxwellDeviceMemoryManager& device_memory_, Vulkan::BufferCache& buffer_cache_, const Device& device_, const MemoryAllocator& memory_allocator_, Scheduler& scheduler_, StagingBufferPool& staging_pool_, ComputePassDescriptorQueue& compute_pass_descriptor_queue, DescriptorPool& descriptor_pool, TextureCache& texture_cache_) + : rasterizer{rasterizer_}, device_memory{device_memory_}, buffer_cache{buffer_cache_} + , device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_} + , staging_pool{staging_pool_}, guest_streamer(0, runtime) + , sample_streamer(size_t(QueryType::ZPassPixelCount64), runtime, rasterizer, texture_cache_, device, scheduler, memory_allocator, compute_pass_descriptor_queue, descriptor_pool) + , tfb_streamer(size_t(QueryType::StreamingByteCount), runtime, device, scheduler, memory_allocator, staging_pool) + , primitives_succeeded_streamer(size_t(QueryType::StreamingPrimitivesSucceeded), runtime, tfb_streamer, device_memory_) + , primitives_needed_minus_succeeded_streamer(size_t(QueryType::StreamingPrimitivesNeededMinusSucceeded), runtime, 0u) + , hcr_setup{}, hcr_is_set{}, is_hcr_running{}, maxwell3d{} { hcr_setup.sType = VK_STRUCTURE_TYPE_CONDITIONAL_RENDERING_BEGIN_INFO_EXT; hcr_setup.pNext = nullptr; hcr_setup.flags = 0; - const bool has_conditional_rendering = device.IsExtConditionalRendering(); - if (has_conditional_rendering) { - conditional_resolve_pass = std::make_unique( - device, scheduler, descriptor_pool, compute_pass_descriptor_queue); - } - - VkBufferUsageFlags hcr_buffer_usage = - VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; - if (has_conditional_rendering) { + VkBufferUsageFlags hcr_buffer_usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; + if (device.IsExtConditionalRendering()) { + conditional_resolve_pass.emplace(device, scheduler, descriptor_pool, compute_pass_descriptor_queue); hcr_buffer_usage |= VK_BUFFER_USAGE_CONDITIONAL_RENDERING_BIT_EXT; } @@ -1339,7 +1317,7 @@ struct QueryCacheRuntimeImpl { std::vector> copies_setup; // Host conditional rendering data - std::unique_ptr conditional_resolve_pass; + std::optional conditional_resolve_pass; vk::Buffer hcr_resolve_buffer; VkConditionalRenderingBeginInfoEXT hcr_setup; VkBuffer hcr_buffer; @@ -1351,13 +1329,7 @@ struct QueryCacheRuntimeImpl { Maxwell3D* maxwell3d; }; -QueryCacheRuntime::QueryCacheRuntime(VideoCore::RasterizerInterface* rasterizer, - Tegra::MaxwellDeviceMemoryManager& device_memory_, - Vulkan::BufferCache& buffer_cache_, const Device& device_, - const MemoryAllocator& memory_allocator_, - Scheduler& scheduler_, StagingBufferPool& staging_pool_, - ComputePassDescriptorQueue& compute_pass_descriptor_queue, - DescriptorPool& descriptor_pool, TextureCache& texture_cache_) { +QueryCacheRuntime::QueryCacheRuntime(VideoCore::RasterizerInterface* rasterizer, Tegra::MaxwellDeviceMemoryManager& device_memory_, Vulkan::BufferCache& buffer_cache_, const Device& device_, const MemoryAllocator& memory_allocator_, Scheduler& scheduler_, StagingBufferPool& staging_pool_, ComputePassDescriptorQueue& compute_pass_descriptor_queue, DescriptorPool& descriptor_pool, TextureCache& texture_cache_) { impl = std::make_unique( *this, rasterizer, device_memory_, buffer_cache_, device_, memory_allocator_, scheduler_, staging_pool_, compute_pass_descriptor_queue, descriptor_pool, texture_cache_); @@ -1587,13 +1559,13 @@ void QueryCacheRuntime::Barriers(bool is_prebarrier) { impl->scheduler.RequestOutsideRenderPassOperationContext(); if (is_prebarrier) { impl->scheduler.Record([](vk::CommandBuffer cmdbuf) { - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, READ_BARRIER); }); } else { impl->scheduler.Record([](vk::CommandBuffer cmdbuf) { cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, WRITE_BARRIER); + vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER_HOST, 0, WRITE_BARRIER); }); } } @@ -1686,8 +1658,7 @@ void QueryCacheRuntime::SyncValues(std::span values, VkBuffer ba } impl->scheduler.RequestOutsideRenderPassOperationContext(); - impl->scheduler.Record([src_buffer, dst_buffers = std::move(impl->buffers_to_upload_to), - vk_copies = std::move(impl->copies_setup)](vk::CommandBuffer cmdbuf) { + impl->scheduler.Record([src_buffer, dst_buffers = std::move(impl->buffers_to_upload_to), vk_copies = std::move(impl->copies_setup)](vk::CommandBuffer cmdbuf) { size_t size = dst_buffers.size(); for (size_t i = 0; i < size; i++) { cmdbuf.CopyBuffer(src_buffer, dst_buffers[i].first, vk_copies[i]); diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 3c3367cfd8..dc84f5c0ad 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -270,7 +270,7 @@ void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) { }); // Log draw call - if (Settings::values.gpu_logging_enabled.GetValue() && + if (GPU::Logging::IsActive() && Settings::values.gpu_log_vulkan_calls.GetValue()) { const std::string params = is_indexed ? fmt::format("vertices={}, instances={}, firstIndex={}, baseVertex={}, baseInstance={}", @@ -331,7 +331,7 @@ void RasterizerVulkan::DrawIndirect() { }); // Log indirect draw call - if (Settings::values.gpu_logging_enabled.GetValue() && + if (GPU::Logging::IsActive() && Settings::values.gpu_log_vulkan_calls.GetValue()) { const std::string log_params = fmt::format("drawCount={}, stride={}", params.max_draw_counts, params.stride); @@ -351,7 +351,7 @@ void RasterizerVulkan::DrawTexture() { FlushWork(); std::scoped_lock l{texture_cache.mutex}; - texture_cache.SynchronizeGraphicsDescriptors(); + texture_cache.SynchronizeDescriptors(false); texture_cache.UpdateRenderTargets(false); UpdateDynamicStates(); @@ -359,7 +359,7 @@ void RasterizerVulkan::DrawTexture() { query_cache.NotifySegment(true); query_cache.CounterEnable(VideoCommon::QueryType::ZPassPixelCount64, maxwell3d->regs.zpass_pixel_count_enable); const auto& draw_texture_state = maxwell3d->draw_manager.draw_texture_state; - const auto& sampler = texture_cache.GetGraphicsSampler(draw_texture_state.src_sampler); + const auto& sampler = texture_cache.GetSampler(draw_texture_state.src_sampler, false); const auto& texture = texture_cache.GetImageView(draw_texture_state.src_texture); const auto* framebuffer = texture_cache.GetFramebuffer(); @@ -580,12 +580,12 @@ void RasterizerVulkan::DispatchCompute() { .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT, }; - scheduler.Record([](vk::CommandBuffer cmdbuf) { cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + scheduler.Record([](vk::CommandBuffer cmdbuf) { cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, READ_BARRIER); }); scheduler.Record([dim](vk::CommandBuffer cmdbuf) { cmdbuf.Dispatch(dim[0], dim[1], dim[2]); }); // Log compute dispatch - if (Settings::values.gpu_logging_enabled.GetValue() && + if (GPU::Logging::IsActive() && Settings::values.gpu_log_vulkan_calls.GetValue()) { const std::string params = fmt::format("groupCountX={}, groupCountY={}, groupCountZ={}", dim[0], dim[1], dim[2]); @@ -928,13 +928,13 @@ void RasterizerVulkan::LoadDiskResources(u64 title_id, std::stop_token stop_load } void RasterizerVulkan::FlushWork() { -#ifdef ANDROID +#ifdef __ANDROID__ static constexpr u32 DRAWS_TO_DISPATCH = 512; static constexpr u32 CHECK_MASK = 3; #else static constexpr u32 DRAWS_TO_DISPATCH = 4096; static constexpr u32 CHECK_MASK = 7; -#endif // ANDROID +#endif // __ANDROID__ static_assert(DRAWS_TO_DISPATCH % (CHECK_MASK + 1) == 0); if ((++draw_counter & CHECK_MASK) != CHECK_MASK) { @@ -1110,7 +1110,7 @@ void RasterizerVulkan::HandleTransformFeedback() { regs.transform_feedback_enabled); if (regs.transform_feedback_enabled != 0) { // Log extension usage for transform feedback - if (Settings::values.gpu_logging_enabled.GetValue()) { + if (GPU::Logging::IsActive()) { GPU::Logging::GPULogger::GetInstance().LogExtensionUsage( "VK_EXT_transform_feedback", "HandleTransformFeedback"); } diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index fdaf9baacc..d1e064c34a 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -27,6 +27,7 @@ namespace Vulkan { +constexpr u64 MAX_PENDING_FLUSHES = 5; void Scheduler::CommandChunk::ExecuteAll(vk::CommandBuffer cmdbuf, vk::CommandBuffer upload_cmdbuf) { @@ -59,13 +60,74 @@ Scheduler::Scheduler(const Device& device_, StateTracker& state_tracker_) AcquireNewChunk(); AllocateWorkerCommandBuffer(); - worker_thread = std::jthread([this](std::stop_token token) { WorkerThread(token); }); + worker_thread = std::jthread([this](std::stop_token stop_token) { + Common::SetCurrentThreadName("VulkanWorker"); + const auto TryPopQueue{[this](auto& work) -> bool { + if (work_queue.empty()) { + return false; + } + + work = std::move(work_queue.front()); + work_queue.pop(); + event_cv.notify_all(); + return true; + }}; + + while (!stop_token.stop_requested()) { + std::unique_ptr work; + + { + std::unique_lock lk{queue_mutex}; + + // Wait for work. + event_cv.wait(lk, stop_token, [&] { return TryPopQueue(work); }); + + // If we've been asked to stop, we're done. + if (stop_token.stop_requested()) { + return; + } + + // Exchange lock ownership so that we take the execution lock before + // the queue lock goes out of scope. This allows us to force execution + // to complete in the next step. + std::exchange(lk, std::unique_lock{execution_mutex}); + + // Perform the work, tracking whether the chunk was a submission + // before executing. + const bool has_submit = work->HasSubmit(); + work->ExecuteAll(current_cmdbuf, current_upload_cmdbuf); + + // If the chunk was a submission, reallocate the command buffer. + if (has_submit) { + AllocateWorkerCommandBuffer(); + } + } + + { + std::scoped_lock rl{reserve_mutex}; + + // Recycle the chunk back to the reserve. + chunk_reserve.emplace_back(std::move(work)); + } + } + }); } Scheduler::~Scheduler() = default; u64 Scheduler::Flush(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) { - // When flushing, we only send data to the worker thread; no waiting is necessary. + // Prevent the CPU from getting too far ahead of the GPU by limiting pending flushes. + const bool should_throttle = Settings::IsGPULevelHigh(); + if (should_throttle) { + const u64 current_tick = master_semaphore->CurrentTick(); + const u64 gap = current_tick > last_submitted_tick ? current_tick - last_submitted_tick : 0; + const u64 step = (std::min)(MAX_PENDING_FLUSHES, gap); + const u64 new_tick = last_submitted_tick + step; + if (new_tick < current_tick) { + last_submitted_tick = new_tick; + master_semaphore->Wait(last_submitted_tick); + } + } const u64 signal_value = SubmitExecution(signal_semaphore, wait_semaphore); AllocateNewContext(); return signal_value; @@ -119,7 +181,7 @@ void Scheduler::RequestRenderpass(const Framebuffer* framebuffer) { state.render_area = render_area; // Log render pass begin - if (Settings::values.gpu_logging_enabled.GetValue() && + if (GPU::Logging::IsActive() && Settings::values.gpu_log_vulkan_calls.GetValue()) { const std::string render_pass_info = fmt::format( "renderArea={}x{}, numImages={}", @@ -187,59 +249,6 @@ bool Scheduler::UpdateRescaling(bool is_rescaling) { return true; } -void Scheduler::WorkerThread(std::stop_token stop_token) { - Common::SetCurrentThreadName("VulkanWorker"); - - const auto TryPopQueue{[this](auto& work) -> bool { - if (work_queue.empty()) { - return false; - } - - work = std::move(work_queue.front()); - work_queue.pop(); - event_cv.notify_all(); - return true; - }}; - - while (!stop_token.stop_requested()) { - std::unique_ptr work; - - { - std::unique_lock lk{queue_mutex}; - - // Wait for work. - event_cv.wait(lk, stop_token, [&] { return TryPopQueue(work); }); - - // If we've been asked to stop, we're done. - if (stop_token.stop_requested()) { - return; - } - - // Exchange lock ownership so that we take the execution lock before - // the queue lock goes out of scope. This allows us to force execution - // to complete in the next step. - std::exchange(lk, std::unique_lock{execution_mutex}); - - // Perform the work, tracking whether the chunk was a submission - // before executing. - const bool has_submit = work->HasSubmit(); - work->ExecuteAll(current_cmdbuf, current_upload_cmdbuf); - - // If the chunk was a submission, reallocate the command buffer. - if (has_submit) { - AllocateWorkerCommandBuffer(); - } - } - - { - std::scoped_lock rl{reserve_mutex}; - - // Recycle the chunk back to the reserve. - chunk_reserve.emplace_back(std::move(work)); - } - } -} - void Scheduler::AllocateWorkerCommandBuffer() { current_cmdbuf = vk::CommandBuffer(command_pool->Commit(), device.GetDispatchLoader()); current_cmdbuf.Begin({ @@ -270,8 +279,7 @@ u64 Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_se .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, }; - upload_cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, WRITE_BARRIER); + upload_cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, WRITE_BARRIER); upload_cmdbuf.End(); cmdbuf.End(); @@ -284,7 +292,7 @@ u64 Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_se cmdbuf, upload_cmdbuf, signal_semaphore, wait_semaphore, signal_value)) { case VK_SUCCESS: // Log successful queue submission - if (Settings::values.gpu_logging_enabled.GetValue() && + if (GPU::Logging::IsActive() && Settings::values.gpu_log_vulkan_calls.GetValue()) { GPU::Logging::GPULogger::GetInstance().LogVulkanCall( "vkQueueSubmit", "", VK_SUCCESS); @@ -327,7 +335,7 @@ void Scheduler::EndRenderPass() query_cache->CounterClose(VideoCommon::QueryType::StreamingByteCount); // Log render pass end - if (Settings::values.gpu_logging_enabled.GetValue() && + if (GPU::Logging::IsActive() && Settings::values.gpu_log_vulkan_calls.GetValue()) { GPU::Logging::GPULogger::GetInstance().LogRenderPassEnd(); } @@ -375,7 +383,7 @@ void Scheduler::EndRenderPass() } cmdbuf.EndRenderPass(); cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, 0, nullptr, nullptr, vk::Span(barriers.data(), num_images)); }); diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h index 0709c3a370..6f471f6247 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_scheduler.h @@ -251,8 +251,6 @@ private: bool needs_state_enable_refresh = false; }; - void WorkerThread(std::stop_token stop_token); - void AllocateWorkerCommandBuffer(); u64 SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore); @@ -298,6 +296,8 @@ private: double last_target_fps{}; u64 max_frame_count{}; u64 frame_counter{}; + + u64 last_submitted_tick = 0; }; } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp index 08513d1534..e3ec62c9fd 100644 --- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp +++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project @@ -27,25 +27,41 @@ using namespace Common::Literals; // Maximum potential alignment of a Vulkan buffer constexpr VkDeviceSize MAX_ALIGNMENT = 256; + // Stream buffer size in bytes +// *NIX drivers are more sensitive to increased buffers for streaming. +// Windows ones however, can intake bigger buffers and generally do not OOM. +// - GTX 960 on Windows will not OOM with 256mib +// - GT 1030 on ^NIX will OOM with 256mib +#ifdef _WIN32 +constexpr VkDeviceSize MAX_STREAM_BUFFER_SIZE = 256_MiB; +#else constexpr VkDeviceSize MAX_STREAM_BUFFER_SIZE = 128_MiB; +#endif size_t GetStreamBufferSize(const Device& device) { + if (!device.HasDebuggingToolAttached()) { + return MAX_STREAM_BUFFER_SIZE; + } + VkDeviceSize size{0}; - if (device.HasDebuggingToolAttached()) { - ForEachDeviceLocalHostVisibleHeap(device, [&size](size_t index, VkMemoryHeap& heap) { - size = (std::max)(size, heap.size); - }); + bool has_device_local_host_visible_heap{}; + ForEachDeviceLocalHostVisibleHeap(device, [&size, &has_device_local_host_visible_heap]( + size_t index, VkMemoryHeap& heap) { + has_device_local_host_visible_heap = true; + size = (std::max)(size, heap.size); + }); + if (has_device_local_host_visible_heap) { // If rebar is not supported, cut the max heap size to 40%. This will allow 2 captures to be // loaded at the same time in RenderDoc. If rebar is supported, this shouldn't be an issue // as the heap will be much larger. - if (size <= 256_MiB) { + if (size <= MAX_STREAM_BUFFER_SIZE) { size = size * 40 / 100; } } else { size = MAX_STREAM_BUFFER_SIZE; } - return (std::min)(Common::AlignUp(size, MAX_ALIGNMENT), MAX_STREAM_BUFFER_SIZE); + return Common::AlignUp(size, MAX_ALIGNMENT); } } // Anonymous namespace diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp index 3f4dd89c7e..0b132c97d6 100644 --- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp +++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp @@ -24,11 +24,8 @@ using namespace Dirty; using namespace VideoCommon::Dirty; using Tegra::Engines::Maxwell3D; using Regs = Maxwell3D::Regs; -using Tables = Maxwell3D::DirtyState::Tables; -using Table = Maxwell3D::DirtyState::Table; -using Flags = Maxwell3D::DirtyState::Flags; -Flags MakeInvalidationFlags() { +Maxwell3D::DirtyState::Flags MakeInvalidationFlags() { static constexpr int INVALIDATION_FLAGS[]{ Viewports, Scissors, @@ -68,7 +65,7 @@ Flags MakeInvalidationFlags() { LineStippleEnable, LineStippleParams, }; - Flags flags{}; + Maxwell3D::DirtyState::Flags flags{}; for (const int flag : INVALIDATION_FLAGS) { flags[flag] = true; } @@ -84,7 +81,7 @@ Flags MakeInvalidationFlags() { return flags; } -void SetupDirtyViewports(Tables& tables) { +void SetupDirtyViewports(Maxwell3D::DirtyState::Tables& tables) { FillBlock(tables[0], OFF(viewport_transform), NUM(viewport_transform), Viewports); FillBlock(tables[0], OFF(viewports), NUM(viewports), Viewports); FillBlock(tables[1], OFF(surface_clip), NUM(surface_clip), Viewports); @@ -92,26 +89,26 @@ void SetupDirtyViewports(Tables& tables) { tables[1][OFF(window_origin)] = Viewports; } -void SetupDirtyScissors(Tables& tables) { +void SetupDirtyScissors(Maxwell3D::DirtyState::Tables& tables) { FillBlock(tables[0], OFF(scissor_test), NUM(scissor_test), Scissors); } -void SetupDirtyDepthBias(Tables& tables) { +void SetupDirtyDepthBias(Maxwell3D::DirtyState::Tables& tables) { auto& table = tables[0]; table[OFF(depth_bias)] = DepthBias; table[OFF(depth_bias_clamp)] = DepthBias; table[OFF(slope_scale_depth_bias)] = DepthBias; } -void SetupDirtyBlendConstants(Tables& tables) { +void SetupDirtyBlendConstants(Maxwell3D::DirtyState::Tables& tables) { FillBlock(tables[0], OFF(blend_color), NUM(blend_color), BlendConstants); } -void SetupDirtyDepthBounds(Tables& tables) { +void SetupDirtyDepthBounds(Maxwell3D::DirtyState::Tables& tables) { FillBlock(tables[0], OFF(depth_bounds), NUM(depth_bounds), DepthBounds); } -void SetupDirtyStencilProperties(Tables& tables) { +void SetupDirtyStencilProperties(Maxwell3D::DirtyState::Tables& tables) { const auto setup = [&](size_t position, u8 flag) { tables[0][position] = flag; tables[1][position] = StencilProperties; @@ -125,18 +122,18 @@ void SetupDirtyStencilProperties(Tables& tables) { setup(OFF(stencil_back_func_mask), StencilCompare); } -void SetupDirtyLineWidth(Tables& tables) { +void SetupDirtyLineWidth(Maxwell3D::DirtyState::Tables& tables) { tables[0][OFF(line_width_smooth)] = LineWidth; tables[0][OFF(line_width_aliased)] = LineWidth; } -void SetupDirtyCullMode(Tables& tables) { +void SetupDirtyCullMode(Maxwell3D::DirtyState::Tables& tables) { auto& table = tables[0]; table[OFF(gl_cull_face)] = CullMode; table[OFF(gl_cull_test_enabled)] = CullMode; } -void SetupDirtyStateEnable(Tables& tables) { +void SetupDirtyStateEnable(Maxwell3D::DirtyState::Tables& tables) { const auto setup = [&](size_t position, u8 flag) { tables[0][position] = flag; tables[1][position] = StateEnable; @@ -157,17 +154,17 @@ void SetupDirtyStateEnable(Tables& tables) { setup(OFF(anti_alias_alpha_control.alpha_to_one), AlphaToOneEnable); } -void SetupDirtyDepthCompareOp(Tables& tables) { +void SetupDirtyDepthCompareOp(Maxwell3D::DirtyState::Tables& tables) { tables[0][OFF(depth_test_func)] = DepthCompareOp; } -void SetupDirtyFrontFace(Tables& tables) { +void SetupDirtyFrontFace(Maxwell3D::DirtyState::Tables& tables) { auto& table = tables[0]; table[OFF(gl_front_face)] = FrontFace; table[OFF(window_origin)] = FrontFace; } -void SetupDirtyStencilOp(Tables& tables) { +void SetupDirtyStencilOp(Maxwell3D::DirtyState::Tables& tables) { auto& table = tables[0]; table[OFF(stencil_front_op.fail)] = StencilOp; table[OFF(stencil_front_op.zfail)] = StencilOp; @@ -182,7 +179,7 @@ void SetupDirtyStencilOp(Tables& tables) { tables[1][OFF(stencil_two_side_enable)] = StencilOp; } -void SetupDirtyBlending(Tables& tables) { +void SetupDirtyBlending(Maxwell3D::DirtyState::Tables& tables) { tables[0][OFF(color_mask_common)] = Blending; tables[1][OFF(color_mask_common)] = ColorMask; tables[0][OFF(blend_per_target_enabled)] = Blending; @@ -196,11 +193,11 @@ void SetupDirtyBlending(Tables& tables) { FillBlock(tables[1], OFF(blend_per_target), NUM(blend_per_target), BlendEquations); } -void SetupDirtySpecialOps(Tables& tables) { +void SetupDirtySpecialOps(Maxwell3D::DirtyState::Tables& tables) { tables[0][OFF(logic_op.op)] = LogicOp; } -void SetupDirtyViewportSwizzles(Tables& tables) { +void SetupDirtyViewportSwizzles(Maxwell3D::DirtyState::Tables& tables) { static constexpr size_t swizzle_offset = 6; for (size_t index = 0; index < Regs::NumViewports; ++index) { tables[1][OFF(viewport_transform) + index * NUM(viewport_transform[0]) + swizzle_offset] = @@ -208,7 +205,7 @@ void SetupDirtyViewportSwizzles(Tables& tables) { } } -void SetupDirtyVertexAttributes(Tables& tables) { +void SetupDirtyVertexAttributes(Maxwell3D::DirtyState::Tables& tables) { for (size_t i = 0; i < Regs::NumVertexAttributes; ++i) { const size_t offset = OFF(vertex_attrib_format) + i * NUM(vertex_attrib_format[0]); FillBlock(tables[0], offset, NUM(vertex_attrib_format[0]), VertexAttribute0 + i); @@ -216,7 +213,7 @@ void SetupDirtyVertexAttributes(Tables& tables) { FillBlock(tables[1], OFF(vertex_attrib_format), Regs::NumVertexAttributes, VertexInput); } -void SetupDirtyVertexBindings(Tables& tables) { +void SetupDirtyVertexBindings(Maxwell3D::DirtyState::Tables& tables) { // Do NOT include stride here, it's implicit in VertexBuffer static constexpr size_t divisor_offset = 3; for (size_t i = 0; i < Regs::NumVertexArrays; ++i) { @@ -228,7 +225,7 @@ void SetupDirtyVertexBindings(Tables& tables) { } } -void SetupRasterModes(Tables &tables) { +void SetupRasterModes(Maxwell3D::DirtyState::Tables &tables) { auto& table = tables[0]; table[OFF(line_stipple_params)] = LineStippleParams; @@ -238,7 +235,7 @@ void SetupRasterModes(Tables &tables) { } // Anonymous namespace void StateTracker::SetupTables(Tegra::Control::ChannelState& channel_state) { - auto& tables{channel_state.maxwell_3d->dirty.tables}; + auto& tables{channel_state.payload->maxwell_3d.dirty.tables}; SetupDirtyFlags(tables); SetupDirtyViewports(tables); SetupDirtyScissors(tables); @@ -261,7 +258,7 @@ void StateTracker::SetupTables(Tegra::Control::ChannelState& channel_state) { } void StateTracker::ChangeChannel(Tegra::Control::ChannelState& channel_state) { - flags = &channel_state.maxwell_3d->dirty.flags; + flags = &channel_state.payload->maxwell_3d.dirty.flags; } void StateTracker::InvalidateState() { diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp index 7e31f9cf67..fdbad16721 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.cpp +++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp @@ -288,7 +288,7 @@ void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities) { .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE, .queueFamilyIndexCount = 0, .pQueueFamilyIndices = nullptr, -#ifdef ANDROID +#ifdef __ANDROID__ // On Android, do not allow surface rotation to deviate from the frontend. .preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR, #else @@ -313,7 +313,7 @@ void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities) { swapchain_ci.imageFormat, // Base format MUST be first VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_B8G8R8A8_SRGB, -#ifdef ANDROID +#ifdef __ANDROID__ VK_FORMAT_R8G8B8A8_UNORM, // Android may use RGBA VK_FORMAT_R8G8B8A8_SRGB, #endif @@ -338,7 +338,7 @@ void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities) { images = swapchain.GetImages(); image_count = static_cast(images.size()); -#ifdef ANDROID +#ifdef __ANDROID__ // Android is already ordered the same as Switch. image_view_format = VK_FORMAT_R8G8B8A8_UNORM; #else diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index f099db74cb..567fd560af 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -860,12 +860,12 @@ void BlitScale(Scheduler& scheduler, VkImage src_image, VkImage dst_image, const .subresourceRange = subresource_range, }, }; - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, - 0, nullptr, nullptr, read_barriers); + cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, nullptr, nullptr, read_barriers); cmdbuf.BlitImage(src_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, regions, vk_filter); - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, - 0, nullptr, nullptr, write_barriers); + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, + 0, nullptr, nullptr, write_barriers); }); } } // Anonymous namespace @@ -1123,19 +1123,19 @@ void TextureCacheRuntime::ReinterpretImage(Image& dst, Image& src, .subresourceRange = dst_range.SubresourceRange(dst_aspect_mask), }, }; - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, - 0, {}, {}, pre_barriers); + cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, {}, {}, pre_barriers); cmdbuf.CopyImageToBuffer(src_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, copy_buffer, vk_in_copies); - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, - 0, WRITE_BARRIER, nullptr, middle_in_barrier); + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, + 0, WRITE_BARRIER, nullptr, middle_in_barrier); - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, - 0, READ_BARRIER, {}, middle_out_barrier); + cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, READ_BARRIER, {}, middle_out_barrier); cmdbuf.CopyBufferToImage(copy_buffer, dst_image, VK_IMAGE_LAYOUT_GENERAL, vk_out_copies); - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, - 0, {}, {}, post_barriers); + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, + 0, {}, {}, post_barriers); }); } @@ -1261,8 +1261,8 @@ void TextureCacheRuntime::BlitImage(Framebuffer* dst_framebuffer, ImageView& dst .layerCount = VK_REMAINING_ARRAY_LAYERS, }, }; - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, - 0, nullptr, nullptr, read_barriers); + cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, nullptr, nullptr, read_barriers); if (is_resolve) { cmdbuf.ResolveImage(src_image, VK_IMAGE_LAYOUT_GENERAL, dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, @@ -1274,8 +1274,8 @@ void TextureCacheRuntime::BlitImage(Framebuffer* dst_framebuffer, ImageView& dst src_image, VK_IMAGE_LAYOUT_GENERAL, dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, MakeImageBlit(dst_region, src_region, dst_layers, src_layers), vk_filter); } - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, - 0, write_barrier); + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, + 0, write_barrier); }); } @@ -1852,7 +1852,7 @@ void Image::DownloadMemory(std::span buffers_span, std::span o .layerCount = VK_REMAINING_ARRAY_LAYERS, }, }; - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, + cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, read_barrier); for (size_t index = 0; index < buffers.size(); index++) { @@ -1884,7 +1884,7 @@ void Image::DownloadMemory(std::span buffers_span, std::span o .layerCount = VK_REMAINING_ARRAY_LAYERS, }, }; - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, 0, memory_write_barrier, nullptr, image_write_barrier); }); return; @@ -1919,7 +1919,7 @@ void Image::DownloadMemory(std::span buffers_span, std::span o .layerCount = VK_REMAINING_ARRAY_LAYERS, }, }; - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, + cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, read_barrier); for (size_t index = 0; index < buffers.size(); index++) { @@ -1951,7 +1951,7 @@ void Image::DownloadMemory(std::span buffers_span, std::span o .layerCount = VK_REMAINING_ARRAY_LAYERS, }, }; - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, 0, memory_write_barrier, nullptr, image_write_barrier); }); } @@ -2332,7 +2332,7 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t if (has_custom_border_colors) { pnext = &border_ci; // Log extension usage for custom border color - if (Settings::values.gpu_logging_enabled.GetValue()) { + if (GPU::Logging::IsActive()) { GPU::Logging::GPULogger::GetInstance().LogExtensionUsage( "VK_EXT_custom_border_color", "Sampler::Sampler"); } @@ -2524,8 +2524,8 @@ void TextureCacheRuntime::TransitionImageLayout(Image& image) { }; scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([barrier](vk::CommandBuffer cmdbuf) { - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, - VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, barrier); + cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, + vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, 0, barrier); }); } } diff --git a/src/video_core/renderer_vulkan/vk_turbo_mode.cpp b/src/video_core/renderer_vulkan/vk_turbo_mode.cpp index 54183be12c..095908b6cf 100644 --- a/src/video_core/renderer_vulkan/vk_turbo_mode.cpp +++ b/src/video_core/renderer_vulkan/vk_turbo_mode.cpp @@ -1,10 +1,10 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#if defined(ANDROID) && defined(ARCHITECTURE_arm64) +#if defined(__ANDROID__) && defined(ARCHITECTURE_arm64) #include #endif @@ -20,7 +20,7 @@ namespace Vulkan { using namespace Common::Literals; TurboMode::TurboMode(const vk::Instance& instance, const vk::InstanceDispatch& dld) -#ifndef ANDROID +#ifndef __ANDROID__ : m_device{CreateDevice(instance, dld, VK_NULL_HANDLE)}, m_allocator{m_device} #endif { @@ -40,7 +40,7 @@ void TurboMode::QueueSubmitted() { } void TurboMode::Run(std::stop_token stop_token) { -#ifndef ANDROID +#ifndef __ANDROID__ auto& dld = m_device.GetLogical(); // Allocate buffer. 2MiB should be sufficient. @@ -154,7 +154,7 @@ void TurboMode::Run(std::stop_token stop_token) { #endif while (!stop_token.stop_requested()) { -#ifdef ANDROID +#ifdef __ANDROID__ #ifdef ARCHITECTURE_arm64 adrenotools_set_turbo(true); #endif @@ -232,7 +232,7 @@ void TurboMode::Run(std::stop_token stop_token) { std::chrono::milliseconds{100}; }); } -#if defined(ANDROID) && defined(ARCHITECTURE_arm64) +#if defined(__ANDROID__) && defined(ARCHITECTURE_arm64) adrenotools_set_turbo(false); #endif } diff --git a/src/video_core/renderer_vulkan/vk_turbo_mode.h b/src/video_core/renderer_vulkan/vk_turbo_mode.h index 9341c98671..6f95cf5e74 100644 --- a/src/video_core/renderer_vulkan/vk_turbo_mode.h +++ b/src/video_core/renderer_vulkan/vk_turbo_mode.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -23,7 +26,7 @@ public: private: void Run(std::stop_token stop_token); -#ifndef ANDROID +#ifndef __ANDROID__ Device m_device; MemoryAllocator m_allocator; #endif diff --git a/src/video_core/renderer_vulkan/vk_update_descriptor.h b/src/video_core/renderer_vulkan/vk_update_descriptor.h index 82fce298da..1497108b16 100644 --- a/src/video_core/renderer_vulkan/vk_update_descriptor.h +++ b/src/video_core/renderer_vulkan/vk_update_descriptor.h @@ -1,10 +1,13 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include - +#include #include "video_core/vulkan_common/vulkan_wrapper.h" namespace Vulkan { @@ -12,20 +15,15 @@ namespace Vulkan { class Device; class Scheduler; -struct DescriptorUpdateEntry { - struct Empty {}; - +union DescriptorUpdateEntry { DescriptorUpdateEntry() = default; DescriptorUpdateEntry(VkDescriptorImageInfo image_) : image{image_} {} DescriptorUpdateEntry(VkDescriptorBufferInfo buffer_) : buffer{buffer_} {} DescriptorUpdateEntry(VkBufferView texel_buffer_) : texel_buffer{texel_buffer_} {} - - union { - Empty empty{}; - VkDescriptorImageInfo image; - VkDescriptorBufferInfo buffer; - VkBufferView texel_buffer; - }; + std::monostate empty{}; + VkDescriptorImageInfo image; + VkDescriptorBufferInfo buffer; + VkBufferView texel_buffer; }; class UpdateDescriptorQueue final { diff --git a/src/video_core/shader_environment.cpp b/src/video_core/shader_environment.cpp index 5f5633d4d1..cf9c80fba6 100644 --- a/src/video_core/shader_environment.cpp +++ b/src/video_core/shader_environment.cpp @@ -18,6 +18,7 @@ #include "common/fs/fs.h" #include "common/fs/path_util.h" #include "common/logging.h" +#include "common/settings.h" #include #include "shader_recompiler/environment.h" #include "video_core/engines/kepler_compute.h" @@ -73,36 +74,36 @@ static Shader::TexturePixelFormat ConvertTexturePixelFormat(const Tegra::Texture static std::string_view StageToPrefix(Shader::Stage stage) { switch (stage) { case Shader::Stage::VertexB: - return "VB"; + return "vs"; case Shader::Stage::TessellationControl: - return "TC"; + return "tc"; case Shader::Stage::TessellationEval: - return "TE"; + return "te"; case Shader::Stage::Geometry: - return "GS"; + return "gs"; case Shader::Stage::Fragment: - return "FS"; + return "fs"; case Shader::Stage::Compute: - return "CS"; + return "cs"; case Shader::Stage::VertexA: - return "VA"; + return "va"; default: - return "UK"; + return "uk"; } } -static void DumpImpl(u64 pipeline_hash, u64 shader_hash, std::span code, +static void DumpImpl(u64 /*pipeline_hash*/, u64 shader_hash, std::span code, [[maybe_unused]] u32 read_highest, [[maybe_unused]] u32 read_lowest, u32 initial_offset, Shader::Stage stage) { - const auto shader_dir{Common::FS::GetEdenPath(Common::FS::EdenPath::DumpDir)}; - const auto base_dir{shader_dir / "shaders"}; - if (!Common::FS::CreateDir(shader_dir) || !Common::FS::CreateDir(base_dir)) { - LOG_ERROR(Common_Filesystem, "Failed to create shader dump directories"); + const auto dump_dir{Common::FS::GetEdenPath(Common::FS::EdenPath::DumpDir)}; + if (!Common::FS::CreateDir(dump_dir)) { + LOG_ERROR(Common_Filesystem, "Failed to create dump directory"); return; } const auto prefix = StageToPrefix(stage); - const auto name{base_dir / - fmt::format("{:016x}_{}_{:016x}.ash", pipeline_hash, prefix, shader_hash)}; + const auto name{dump_dir / + fmt::format("{:016x}_{:016x}_{}.ash", + Settings::GetCurrentProgramID(), shader_hash, prefix)}; std::fstream shader_file(name, std::ios::out | std::ios::binary); ASSERT(initial_offset % sizeof(u64) == 0); const size_t jump_index = initial_offset / sizeof(u64); @@ -254,11 +255,15 @@ std::optional GenericEnvironment::TryFindSize() { static constexpr u64 SELF_BRANCH_A = 0xE2400FFFFF87000FULL; static constexpr u64 SELF_BRANCH_B = 0xE2400FFFFF07000FULL; + static constexpr u64 MESA_EXIT_MASK = 0xFFF00000000F001FULL; + static constexpr u64 MESA_EXIT_VALUE = (0xE30ULL << 52) | (0x7ULL << 16) | 0xFULL; + + code.resize(MAXIMUM_SIZE / INST_SIZE); + GPUVAddr guest_addr{program_base + start_address}; size_t offset{0}; size_t size{BLOCK_SIZE}; while (size <= MAXIMUM_SIZE) { - code.resize(size / INST_SIZE); u64* const data = code.data() + offset / INST_SIZE; gpu_memory->ReadBlock(guest_addr, data, BLOCK_SIZE); for (size_t index = 0; index < BLOCK_SIZE; index += INST_SIZE) { @@ -266,6 +271,9 @@ std::optional GenericEnvironment::TryFindSize() { if (inst == SELF_BRANCH_A || inst == SELF_BRANCH_B) { return offset + index; } + if ((inst & MESA_EXIT_MASK) == MESA_EXIT_VALUE) { + return offset + index + INST_SIZE; + } } guest_addr += BLOCK_SIZE; size += BLOCK_SIZE; diff --git a/src/video_core/smaa_area_tex.h b/src/video_core/smaa_area_tex.h index 40d0941c26..aa4000d315 100644 --- a/src/video_core/smaa_area_tex.h +++ b/src/video_core/smaa_area_tex.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2013 Jorge Jimenez (jorge@iryoku.com) // SPDX-FileCopyrightText: 2013 Jose I. Echevarria (joseignacioechevarria@gmail.com) // SPDX-FileCopyrightText: 2013 Belen Masia (bmasia@unizar.es) @@ -5,8 +8,7 @@ // SPDX-FileCopyrightText: 2013 Diego Gutierrez (diegog@unizar.es) // SPDX-License-Identifier: MIT -#ifndef AREATEX_H -#define AREATEX_H +#pragma once #define AREATEX_WIDTH 160 #define AREATEX_HEIGHT 560 @@ -11219,5 +11221,3 @@ static const unsigned char areaTexBytes[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - -#endif diff --git a/src/video_core/smaa_search_tex.h b/src/video_core/smaa_search_tex.h index 61939e2046..3a74ab0a32 100644 --- a/src/video_core/smaa_search_tex.h +++ b/src/video_core/smaa_search_tex.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2013 Jorge Jimenez (jorge@iryoku.com) // SPDX-FileCopyrightText: 2013 Jose I. Echevarria (joseignacioechevarria@gmail.com) // SPDX-FileCopyrightText: 2013 Belen Masia (bmasia@unizar.es) @@ -5,8 +8,7 @@ // SPDX-FileCopyrightText: 2013 Diego Gutierrez (diegog@unizar.es) // SPDX-License-Identifier: MIT -#ifndef SEARCHTEX_H -#define SEARCHTEX_H +#pragma once #define SEARCHTEX_WIDTH 64 #define SEARCHTEX_HEIGHT 16 @@ -84,5 +86,3 @@ static const unsigned char searchTexBytes[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; - -#endif diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp index 1998849e84..69c1720943 100644 --- a/src/video_core/surface.cpp +++ b/src/video_core/surface.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: 2014 Citra Emulator Project @@ -336,6 +336,20 @@ bool IsPixelFormatBCn(PixelFormat format) { } } +bool IsPixelFormatETC2(PixelFormat format) { + switch (format) { + case PixelFormat::ETC2_RGB_UNORM: + case PixelFormat::ETC2_RGBA_UNORM: + case PixelFormat::ETC2_RGB_PTA_UNORM: + case PixelFormat::ETC2_RGB_SRGB: + case PixelFormat::ETC2_RGBA_SRGB: + case PixelFormat::ETC2_RGB_PTA_SRGB: + return true; + default: + return false; + } +} + bool IsPixelFormatSRGB(PixelFormat format) { switch (format) { case PixelFormat::A8B8G8R8_SRGB: @@ -344,6 +358,9 @@ bool IsPixelFormatSRGB(PixelFormat format) { case PixelFormat::BC2_SRGB: case PixelFormat::BC3_SRGB: case PixelFormat::BC7_SRGB: + case PixelFormat::ETC2_RGB_SRGB: + case PixelFormat::ETC2_RGBA_SRGB: + case PixelFormat::ETC2_RGB_PTA_SRGB: case PixelFormat::ASTC_2D_4X4_SRGB: case PixelFormat::ASTC_2D_8X8_SRGB: case PixelFormat::ASTC_2D_8X5_SRGB: diff --git a/src/video_core/surface.h b/src/video_core/surface.h index a97bcb39b5..5611dbdffa 100644 --- a/src/video_core/surface.h +++ b/src/video_core/surface.h @@ -5,6 +5,8 @@ #pragma once +#undef PIXEL_FORMAT_LIST + #include #include #include "common/assert.h" @@ -111,6 +113,12 @@ namespace VideoCore::Surface { PIXEL_FORMAT_ELEM(ASTC_2D_6X5_UNORM, 6, 5, 128) \ PIXEL_FORMAT_ELEM(ASTC_2D_6X5_SRGB, 6, 5, 128) \ PIXEL_FORMAT_ELEM(E5B9G9R9_FLOAT, 1, 1, 32) \ + PIXEL_FORMAT_ELEM(ETC2_RGB_UNORM, 4, 4, 64) \ + PIXEL_FORMAT_ELEM(ETC2_RGBA_UNORM, 4, 4, 128) \ + PIXEL_FORMAT_ELEM(ETC2_RGB_PTA_UNORM, 4, 4, 64) \ + PIXEL_FORMAT_ELEM(ETC2_RGB_SRGB, 4, 4, 64) \ + PIXEL_FORMAT_ELEM(ETC2_RGBA_SRGB, 4, 4, 128) \ + PIXEL_FORMAT_ELEM(ETC2_RGB_PTA_SRGB, 4, 4, 64) \ /* Depth formats */ \ PIXEL_FORMAT_ELEM(D32_FLOAT, 1, 1, 32) \ PIXEL_FORMAT_ELEM(D16_UNORM, 1, 1, 16) \ @@ -122,7 +130,7 @@ namespace VideoCore::Surface { PIXEL_FORMAT_ELEM(S8_UINT_D24_UNORM, 1, 1, 32) \ PIXEL_FORMAT_ELEM(D32_FLOAT_S8_UINT, 1, 1, 64) -enum class PixelFormat { +enum class PixelFormat : u32 { #define PIXEL_FORMAT_ELEM(name, ...) name, PIXEL_FORMAT_LIST #undef PIXEL_FORMAT_ELEM @@ -181,13 +189,18 @@ constexpr u32 BitsPerBlock(PixelFormat format) noexcept { } } -#undef PIXEL_FORMAT_LIST - /// Returns the sizer in bytes of the specified pixel format constexpr u32 BytesPerBlock(PixelFormat pixel_format) { return BitsPerBlock(pixel_format) / CHAR_BIT; } +} + +#include "video_core/gpu.h" +#include "video_core/textures/texture.h" + +namespace VideoCore::Surface { + SurfaceTarget SurfaceTargetFromTextureType(Tegra::Texture::TextureType texture_type); bool SurfaceTargetIsLayered(SurfaceTarget target); bool SurfaceTargetIsArray(SurfaceTarget target); @@ -198,6 +211,7 @@ SurfaceType GetFormatType(PixelFormat pixel_format); bool HasAlpha(PixelFormat pixel_format); bool IsPixelFormatASTC(PixelFormat format); bool IsPixelFormatBCn(PixelFormat format); +bool IsPixelFormatETC2(PixelFormat format); bool IsPixelFormatSRGB(PixelFormat format); bool IsPixelFormatInteger(PixelFormat format); bool IsPixelFormatSignedInteger(PixelFormat format); diff --git a/src/video_core/texture_cache/descriptor_table.h b/src/video_core/texture_cache/descriptor_table.h index 1bad83fb4a..e40c128ab5 100644 --- a/src/video_core/texture_cache/descriptor_table.h +++ b/src/video_core/texture_cache/descriptor_table.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -6,37 +9,39 @@ #include #include +#include "common/alignment.h" #include "common/common_types.h" #include "common/div_ceil.h" +#include "common/assert.h" #include "video_core/memory_manager.h" #include "video_core/rasterizer_interface.h" namespace VideoCommon { -template +template class DescriptorTable { public: - explicit DescriptorTable(Tegra::MemoryManager& gpu_memory_) : gpu_memory{gpu_memory_} {} - - [[nodiscard]] bool Synchronize(GPUVAddr gpu_addr, u32 limit) { - [[likely]] if (current_gpu_addr == gpu_addr && current_limit == limit) { return false; } - Refresh(gpu_addr, limit); - return true; + [[nodiscard]] bool Synchronize(GPUVAddr gpu_addr, u32 limit) noexcept { + bool ret = !(current_gpu_addr == gpu_addr && current_limit == limit); + if (ret) { + Refresh(gpu_addr, limit); + } + return ret; } void Invalidate() noexcept { std::ranges::fill(read_descriptors, 0); } - [[nodiscard]] std::pair Read(u32 index) { + [[nodiscard]] std::pair Read(Tegra::MemoryManager const& gpu_memory, u32 index) noexcept { DEBUG_ASSERT(index <= current_limit); - const GPUVAddr gpu_addr = current_gpu_addr + index * sizeof(Descriptor); - std::pair result; - gpu_memory.ReadBlockUnsafe(gpu_addr, &result.first, sizeof(Descriptor)); - if (IsDescriptorRead(index)) { + const GPUVAddr gpu_addr = current_gpu_addr + index * sizeof(T); + std::pair result; + gpu_memory.ReadBlockUnsafe(gpu_addr, std::addressof(result.first), sizeof(T)); + if ((read_descriptors[index / 64] & (1ULL << (index % 64))) != 0) { result.second = result.first != descriptors[index]; } else { - MarkDescriptorAsRead(index); + read_descriptors[index / 64] |= 1ULL << (index % 64); result.second = true; } if (result.second) { @@ -45,34 +50,24 @@ public: return result; } - [[nodiscard]] u32 Limit() const noexcept { - return current_limit; - } - -private: - void Refresh(GPUVAddr gpu_addr, u32 limit) { + void Refresh(GPUVAddr gpu_addr, u32 limit) noexcept { current_gpu_addr = gpu_addr; current_limit = limit; - - const size_t num_descriptors = static_cast(limit) + 1; - read_descriptors.clear(); - read_descriptors.resize(Common::DivCeil(num_descriptors, 64U), 0); + // Mario Brothership reallocates a lot of times, so use aggressive pre-alloc sizes + // std::vector by default uses quadratic growth, but that isn't even enough to satisfy brothership + const size_t num_descriptors = ((limit + 0x80000) & (~0x7ffff)) + 1; + size_t old_size = read_descriptors.size(); + read_descriptors.resize(Common::DivCeil(num_descriptors, 64U)); + old_size = (std::min)(old_size, read_descriptors.size()); + std::fill(read_descriptors.begin(), read_descriptors.begin() + old_size, 0); + // descriptors.resize(num_descriptors); } - void MarkDescriptorAsRead(u32 index) noexcept { - read_descriptors[index / 64] |= 1ULL << (index % 64); - } - - [[nodiscard]] bool IsDescriptorRead(u32 index) const noexcept { - return (read_descriptors[index / 64] & (1ULL << (index % 64))) != 0; - } - - Tegra::MemoryManager& gpu_memory; + std::vector read_descriptors; + std::vector descriptors; GPUVAddr current_gpu_addr{}; u32 current_limit{}; - std::vector read_descriptors; - std::vector descriptors; }; } // namespace VideoCommon diff --git a/src/video_core/texture_cache/format_lookup_table.cpp b/src/video_core/texture_cache/format_lookup_table.cpp index a9e03b375d..ea68a1c063 100644 --- a/src/video_core/texture_cache/format_lookup_table.cpp +++ b/src/video_core/texture_cache/format_lookup_table.cpp @@ -191,6 +191,20 @@ PixelFormat PixelFormatFromTextureInfo(TextureFormat format, ComponentType red, return PixelFormat::BC6H_SFLOAT; case Hash(TextureFormat::BC6H_U16, FLOAT): return PixelFormat::BC6H_UFLOAT; + /* ETC2 */ + case Hash(TextureFormat::ETC2_RGB, UNORM, LINEAR): + return PixelFormat::ETC2_RGB_UNORM; + case Hash(TextureFormat::ETC2_RGB_PTA, UNORM, LINEAR): + return PixelFormat::ETC2_RGB_PTA_UNORM; + case Hash(TextureFormat::ETC2_RGBA, UNORM, LINEAR): + return PixelFormat::ETC2_RGBA_UNORM; + case Hash(TextureFormat::ETC2_RGB, UNORM, SRGB): + return PixelFormat::ETC2_RGB_SRGB; + case Hash(TextureFormat::ETC2_RGB_PTA, UNORM, SRGB): + return PixelFormat::ETC2_RGB_PTA_SRGB; + case Hash(TextureFormat::ETC2_RGBA, UNORM, SRGB): + return PixelFormat::ETC2_RGBA_SRGB; + /* ASTC */ case Hash(TextureFormat::ASTC_2D_4X4, UNORM, LINEAR): return PixelFormat::ASTC_2D_4X4_UNORM; case Hash(TextureFormat::ASTC_2D_4X4, UNORM, SRGB): diff --git a/src/video_core/texture_cache/formatter.h b/src/video_core/texture_cache/formatter.h index 33c32645a2..3e2e749274 100644 --- a/src/video_core/texture_cache/formatter.h +++ b/src/video_core/texture_cache/formatter.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -17,210 +20,9 @@ struct fmt::formatter : fmt::formatter; diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 5a1c680830..2cfdb94359 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -14,6 +14,7 @@ #include "common/alignment.h" #include "common/settings.h" +#include "common/slot_vector.h" #include "video_core/control/channel_state.h" #include "video_core/dirty_flags.h" #include "video_core/engines/kepler_compute.h" @@ -128,6 +129,7 @@ void TextureCache

::RunGarbageCollector() { if (num_iterations == 0) { return true; } + --num_iterations; auto& image = slot_images[image_id]; if (True(image.flags & ImageFlagBits::IsDecoding)) { return false; @@ -136,7 +138,6 @@ void TextureCache

::RunGarbageCollector() { if ((!aggressive_mode && True(image.flags & ImageFlagBits::CostlyLoad)) || (!high_priority_mode && must_download)) { return false; } - --num_iterations; if (must_download) { auto map = runtime.DownloadStagingBuffer(image.unswizzled_size_bytes); const auto copies = FixSmallVectorADL(FullDownloadCopies(image.info)); @@ -204,8 +205,8 @@ typename P::ImageView& TextureCache

::GetImageView(ImageViewId id) noexcept { template typename P::ImageView& TextureCache

::GetImageView(u32 index) noexcept { - const auto image_view_id = VisitImageView(channel_state->graphics_image_table, - channel_state->graphics_image_view_ids, index); + // Not compute! + const auto image_view_id = VisitImageView(index, false); return slot_image_views[image_view_id]; } @@ -215,16 +216,25 @@ void TextureCache

::MarkModification(ImageId id) noexcept { } template -template -void TextureCache

::FillGraphicsImageViews(std::span views) { - FillImageViews(channel_state->graphics_image_table, - channel_state->graphics_image_view_ids, views); -} - -template -void TextureCache

::FillComputeImageViews(std::span views) { - FillImageViews(channel_state->compute_image_table, channel_state->compute_image_view_ids, - views); +void TextureCache

::FillImageViews(std::span views, bool compute, bool blacklist) { + bool has_blacklisted = false; + do { + has_deleted_images = false; + if (blacklist) { + has_blacklisted = false; + } + for (ImageViewInOut& view : views) { + view.id = VisitImageView(view.index, compute); + if (blacklist) { + if (view.blacklist && view.id != NULL_IMAGE_VIEW_ID) { + const ImageViewBase& image_view = slot_image_views[view.id]; + auto& image = slot_images[image_view.image_id]; + has_blacklisted |= ScaleDown(image); + image.scale_rating = 0; + } + } + } + } while (has_deleted_images || (blacklist && has_blacklisted)); } template @@ -292,41 +302,25 @@ void TextureCache

::CheckFeedbackLoop(std::span views) { } template -typename P::Sampler* TextureCache

::GetGraphicsSampler(u32 index) { - return &slot_samplers[GetGraphicsSamplerId(index)]; +typename P::Sampler* TextureCache

::GetSampler(u32 index, bool compute) { + return &slot_samplers[GetSamplerId(index, compute)]; } template -typename P::Sampler* TextureCache

::GetComputeSampler(u32 index) { - return &slot_samplers[GetComputeSamplerId(index)]; -} - -template -SamplerId TextureCache

::GetGraphicsSamplerId(u32 index) { - if (index > channel_state->graphics_sampler_table.Limit()) { +SamplerId TextureCache

::GetSamplerId(u32 index, bool compute) { + auto& table = compute ? channel_state->compute_sampler_table : channel_state->graphics_sampler_table; + if (index > table.current_limit) { LOG_DEBUG(HW_GPU, "Invalid sampler index={}", index); return NULL_SAMPLER_ID; } - const auto [descriptor, is_new] = channel_state->graphics_sampler_table.Read(index); - SamplerId& id = channel_state->graphics_sampler_ids[index]; + auto const map_index = index | (compute ? Common::SlotId::TAGGED_VALUE : 0); + auto const [descriptor, is_new] = table.Read(*gpu_memory, index); if (is_new) { - id = FindSampler(descriptor); + auto const id = FindSampler(descriptor, compute); + channel_state->sampler_ids.insert_or_assign(map_index, id); + return id; } - return id; -} - -template -SamplerId TextureCache

::GetComputeSamplerId(u32 index) { - if (index > channel_state->compute_sampler_table.Limit()) { - LOG_DEBUG(HW_GPU, "Invalid sampler index={}", index); - return NULL_SAMPLER_ID; - } - const auto [descriptor, is_new] = channel_state->compute_sampler_table.Read(index); - SamplerId& id = channel_state->compute_sampler_ids[index]; - if (is_new) { - id = FindSampler(descriptor); - } - return id; + return channel_state->sampler_ids.find(map_index)->second; } template @@ -340,45 +334,31 @@ typename P::Sampler& TextureCache

::GetSampler(SamplerId id) noexcept { } template -void TextureCache

::SynchronizeGraphicsDescriptors() { - using SamplerBinding = Tegra::Engines::Maxwell3D::Regs::SamplerBinding; - const bool linked_tsc = maxwell3d->regs.sampler_binding == SamplerBinding::ViaHeaderBinding; - const u32 tic_limit = maxwell3d->regs.tex_header.limit; - const u32 tsc_limit = linked_tsc ? tic_limit : maxwell3d->regs.tex_sampler.limit; - bool bindings_changed = false; - if (channel_state->graphics_sampler_table.Synchronize(maxwell3d->regs.tex_sampler.Address(), - tsc_limit)) { - channel_state->graphics_sampler_ids.resize(tsc_limit + 1, CORRUPT_ID); - bindings_changed = true; - } - if (channel_state->graphics_image_table.Synchronize(maxwell3d->regs.tex_header.Address(), - tic_limit)) { - channel_state->graphics_image_view_ids.resize(tic_limit + 1, CORRUPT_ID); - bindings_changed = true; - } - if (bindings_changed) { - ++texture_bindings_serial; - } -} - -template -void TextureCache

::SynchronizeComputeDescriptors() { - const bool linked_tsc = kepler_compute->launch_description.linked_tsc; - const u32 tic_limit = kepler_compute->regs.tic.limit; - const u32 tsc_limit = linked_tsc ? tic_limit : kepler_compute->regs.tsc.limit; - const GPUVAddr tsc_gpu_addr = kepler_compute->regs.tsc.Address(); - bool bindings_changed = false; - if (channel_state->compute_sampler_table.Synchronize(tsc_gpu_addr, tsc_limit)) { - channel_state->compute_sampler_ids.resize(tsc_limit + 1, CORRUPT_ID); - bindings_changed = true; - } - if (channel_state->compute_image_table.Synchronize(kepler_compute->regs.tic.Address(), - tic_limit)) { - channel_state->compute_image_view_ids.resize(tic_limit + 1, CORRUPT_ID); - bindings_changed = true; - } - if (bindings_changed) { - ++texture_bindings_serial; +void TextureCache

::SynchronizeDescriptors(bool compute) { + if (compute) { + const bool linked_tsc = kepler_compute->launch_description.linked_tsc; + const u32 tic_limit = kepler_compute->regs.tic.limit; + const u32 tsc_limit = linked_tsc ? tic_limit : kepler_compute->regs.tsc.limit; + bool bindings_changed = false; + if (channel_state->compute_sampler_table.Synchronize(kepler_compute->regs.tsc.Address(), tsc_limit)) + bindings_changed = true; + if (channel_state->compute_image_table.Synchronize(kepler_compute->regs.tic.Address(), tic_limit)) + bindings_changed = true; + if (bindings_changed) { + ++texture_bindings_serial; + } + } else { + const bool linked_tsc = maxwell3d->regs.sampler_binding == Tegra::Engines::Maxwell3D::Regs::SamplerBinding::ViaHeaderBinding; + const u32 tic_limit = maxwell3d->regs.tex_header.limit; + const u32 tsc_limit = linked_tsc ? tic_limit : maxwell3d->regs.tex_sampler.limit; + bool bindings_changed = false; + if (channel_state->graphics_sampler_table.Synchronize(maxwell3d->regs.tex_sampler.Address(), tsc_limit)) + bindings_changed = true; + if (channel_state->graphics_image_table.Synchronize(maxwell3d->regs.tex_header.Address(), tic_limit)) + bindings_changed = true; + if (bindings_changed) { + ++texture_bindings_serial; + } } } @@ -557,47 +537,32 @@ typename P::Framebuffer* TextureCache

::GetFramebuffer() { } template -template -void TextureCache

::FillImageViews(DescriptorTable& table, - std::span cached_image_view_ids, - std::span views) { - bool has_blacklisted = false; - do { - has_deleted_images = false; - if constexpr (has_blacklists) { - has_blacklisted = false; - } - for (ImageViewInOut& view : views) { - view.id = VisitImageView(table, cached_image_view_ids, view.index); - if constexpr (has_blacklists) { - if (view.blacklist && view.id != NULL_IMAGE_VIEW_ID) { - const ImageViewBase& image_view{slot_image_views[view.id]}; - auto& image = slot_images[image_view.image_id]; - has_blacklisted |= ScaleDown(image); - image.scale_rating = 0; - } - } - } - } while (has_deleted_images || (has_blacklists && has_blacklisted)); -} - -template -ImageViewId TextureCache

::VisitImageView(DescriptorTable& table, - std::span cached_image_view_ids, - u32 index) { - if (index > table.Limit()) { +ImageViewId TextureCache

::VisitImageView(u32 index, bool compute) { + auto& table = compute ? channel_state->compute_image_table : channel_state->graphics_image_table; + if (index > table.current_limit) { LOG_DEBUG(HW_GPU, "Invalid image view index={}", index); return NULL_IMAGE_VIEW_ID; } - const auto [descriptor, is_new] = table.Read(index); - ImageViewId& image_view_id = cached_image_view_ids[index]; + auto const map_index = index | (compute ? Common::SlotId::TAGGED_VALUE : 0); + // Is new (on the tegra engine side)? + auto const [descriptor, is_new] = table.Read(*gpu_memory, index); if (is_new) { - image_view_id = FindImageView(descriptor); + if (IsValidEntry(*gpu_memory, descriptor)) { + // Is new (registered view) on the texture cache side? + const auto [pair, is_new_tc] = channel_state->image_views.try_emplace(descriptor); + if (is_new_tc) + pair->second = CreateImageView(descriptor); + PrepareImageView(pair->second, false, false); + channel_state->image_view_ids.insert_or_assign(map_index, pair->second); + return pair->second; + } + channel_state->image_view_ids.insert_or_assign(map_index, NULL_IMAGE_VIEW_ID); + return NULL_IMAGE_VIEW_ID; } - if (image_view_id != NULL_IMAGE_VIEW_ID) { - PrepareImageView(image_view_id, false, false); - } - return image_view_id; + auto const it = channel_state->image_view_ids.find(map_index); + if (it->second != NULL_IMAGE_VIEW_ID) + PrepareImageView(it->second, false, false); + return it->second; } template @@ -1196,19 +1161,6 @@ void TextureCache

::UploadImageContents(Image& image, StagingBuffer& staging) } } -template -ImageViewId TextureCache

::FindImageView(const TICEntry& config) { - if (!IsValidEntry(*gpu_memory, config)) { - return NULL_IMAGE_VIEW_ID; - } - const auto [pair, is_new] = channel_state->image_views.try_emplace(config); - ImageViewId& image_view_id = pair->second; - if (is_new) { - image_view_id = CreateImageView(config); - } - return image_view_id; -} - template ImageViewId TextureCache

::CreateImageView(const TICEntry& config) { const ImageInfo info(config); @@ -1350,10 +1302,10 @@ void TextureCache

::InvalidateScale(Image& image) { image.image_view_infos.clear(); for (size_t c : active_channel_ids) { auto& channel_info = channel_storage[c]; - if constexpr (ENABLE_VALIDATION) { - std::ranges::fill(channel_info.graphics_image_view_ids, CORRUPT_ID); - std::ranges::fill(channel_info.compute_image_view_ids, CORRUPT_ID); - } + + if constexpr (ENABLE_VALIDATION) + for (auto& e : channel_info.image_view_ids) + e.second = CORRUPT_ID; channel_info.graphics_image_table.Invalidate(); channel_info.compute_image_table.Invalidate(); } @@ -1918,7 +1870,7 @@ std::pair TextureCache

::PrepareDmaImage(ImageId dst_id, GPUVAddr ba } template -SamplerId TextureCache

::FindSampler(const TSCEntry& config) { +SamplerId TextureCache

::FindSampler(const TSCEntry& config, bool compute) { if (std::ranges::all_of(config.raw, [](u64 value) { return value == 0; })) { return NULL_SAMPLER_ID; } @@ -1941,69 +1893,48 @@ std::optional TextureCache

::QuerySamplerBudget() const { template void TextureCache

::EnforceSamplerBudget() { - const auto budget = QuerySamplerBudget(); - if (!budget) { - return; + if (auto const budget = QuerySamplerBudget(); budget) { + if (slot_samplers.size() < *budget) { + return; + } + if (!channel_state) { + return; + } + if (last_sampler_gc_frame == frame_tick) { + return; + } + last_sampler_gc_frame = frame_tick; + TrimInactiveSamplers(*budget); } - if (slot_samplers.size() < *budget) { - return; - } - if (!channel_state) { - return; - } - if (last_sampler_gc_frame == frame_tick) { - return; - } - last_sampler_gc_frame = frame_tick; - TrimInactiveSamplers(*budget); } template void TextureCache

::TrimInactiveSamplers(size_t budget) { - if (channel_state->samplers.empty()) { - return; - } - constexpr size_t SAMPLER_GC_SLACK = 1024; - auto mark_active = [](auto& set, SamplerId id) { - if (!id || id == CORRUPT_ID || id == NULL_SAMPLER_ID) { - return; + if (channel_state->samplers.size() > 0) { + constexpr size_t SAMPLER_GC_SLACK = 1024; + ankerl::unordered_dense::set active_sampler_ids; + for (auto const& e : channel_state->sampler_ids) + active_sampler_ids.insert(e.second); + // Elements in the map must be necesarily valid + size_t removed = 0; + for (auto it = channel_state->samplers.begin(); it != channel_state->samplers.end();) { + const SamplerId sampler_id = it->second; + if (!sampler_id || sampler_id == CORRUPT_ID) { + it = channel_state->samplers.erase(it); + } else if (std::ranges::find(active_sampler_ids, sampler_id) != active_sampler_ids.end()) { + ++it; + } else { + slot_samplers.erase(sampler_id); + it = channel_state->samplers.erase(it); + ++removed; + if (slot_samplers.size() + SAMPLER_GC_SLACK <= budget) { + break; + } + } } - set.insert(id); - }; - ankerl::unordered_dense::set active; - active.reserve(channel_state->graphics_sampler_ids.size() + - channel_state->compute_sampler_ids.size()); - for (const SamplerId id : channel_state->graphics_sampler_ids) { - mark_active(active, id); - } - for (const SamplerId id : channel_state->compute_sampler_ids) { - mark_active(active, id); - } - - size_t removed = 0; - auto& sampler_map = channel_state->samplers; - for (auto it = sampler_map.begin(); it != sampler_map.end();) { - const SamplerId sampler_id = it->second; - if (!sampler_id || sampler_id == CORRUPT_ID) { - it = sampler_map.erase(it); - continue; + if (removed != 0) { + LOG_WARNING(HW_GPU, "Sampler cache exceeded {} entries on this driver; reclaimed {} inactive samplers", budget, removed); } - if (active.find(sampler_id) != active.end()) { - ++it; - continue; - } - slot_samplers.erase(sampler_id); - it = sampler_map.erase(it); - ++removed; - if (slot_samplers.size() + SAMPLER_GC_SLACK <= budget) { - break; - } - } - - if (removed != 0) { - LOG_WARNING(HW_GPU, - "Sampler cache exceeded {} entries on this driver; reclaimed {} inactive samplers", - budget, removed); } } @@ -2243,8 +2174,7 @@ ImageViewId TextureCache

::FindOrEmplaceImageView(ImageId image_id, const Imag if (const ImageViewId image_view_id = image.FindView(info); image_view_id) { return image_view_id; } - const ImageViewId image_view_id = - slot_image_views.insert(runtime, info, image_id, image, slot_images); + const ImageViewId image_view_id = slot_image_views.insert(runtime, info, image_id, image, slot_images); image.InsertView(info, image_view_id); return image_view_id; } @@ -2504,10 +2434,9 @@ void TextureCache

::DeleteImage(ImageId image_id, bool immediate_delete) { } for (size_t c : active_channel_ids) { auto& channel_info = channel_storage[c]; - if constexpr (ENABLE_VALIDATION) { - std::ranges::fill(channel_info.graphics_image_view_ids, CORRUPT_ID); - std::ranges::fill(channel_info.compute_image_view_ids, CORRUPT_ID); - } + if constexpr (ENABLE_VALIDATION) + for (auto& e : channel_info.image_view_ids) + e.second = CORRUPT_ID; channel_info.graphics_image_table.Invalidate(); channel_info.compute_image_table.Invalidate(); } diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h index 47f52c5c99..fbc2bb4cf7 100644 --- a/src/video_core/texture_cache/texture_cache_base.h +++ b/src/video_core/texture_cache/texture_cache_base.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include "common/common_types.h" @@ -76,22 +77,20 @@ public: TextureCacheChannelInfo(const TextureCacheChannelInfo& state) = delete; TextureCacheChannelInfo& operator=(const TextureCacheChannelInfo&) = delete; - DescriptorTable graphics_image_table{gpu_memory}; - DescriptorTable graphics_sampler_table{gpu_memory}; - std::vector graphics_sampler_ids; - std::vector graphics_image_view_ids; - - DescriptorTable compute_image_table{gpu_memory}; - DescriptorTable compute_sampler_table{gpu_memory}; - std::vector compute_sampler_ids; - std::vector compute_image_view_ids; + DescriptorTable graphics_image_table; + DescriptorTable graphics_sampler_table; + DescriptorTable compute_image_table; + DescriptorTable compute_sampler_table; // TODO: still relies on bad iterators :( std::unordered_map image_views; std::unordered_map samplers; - TextureCacheGPUMap* gpu_page_table; - TextureCacheGPUMap* sparse_page_table; + ankerl::unordered_dense::map sampler_ids; + ankerl::unordered_dense::map image_view_ids; + + TextureCacheGPUMap* gpu_page_table = nullptr; + TextureCacheGPUMap* sparse_page_table = nullptr; }; template @@ -167,27 +166,17 @@ public: /// Mark an image as modified from the GPU void MarkModification(ImageId id) noexcept; - /// Fill image_view_ids with the graphics images in indices - template - void FillGraphicsImageViews(std::span views); - - /// Fill image_view_ids with the compute images in indices - void FillComputeImageViews(std::span views); + /// Fill image_view_ids with the graphics/compute images in indices + void FillImageViews(std::span views, bool compute, bool blacklist = true); /// Handle feedback loops during draws. void CheckFeedbackLoop(std::span views); - /// Get the sampler from the graphics descriptor table in the specified index - Sampler* GetGraphicsSampler(u32 index); + /// Get the sampler from the graphics/compute descriptor table in the specified index + Sampler* GetSampler(u32 index, bool compute); - /// Get the sampler from the compute descriptor table in the specified index - Sampler* GetComputeSampler(u32 index); - - /// Get the sampler id from the graphics descriptor table in the specified index - SamplerId GetGraphicsSamplerId(u32 index); - - /// Get the sampler id from the compute descriptor table in the specified index - SamplerId GetComputeSamplerId(u32 index); + /// Get the sampler id from the graphics/compute descriptor table in the specified index + SamplerId GetSamplerId(u32 index, bool compute); /// Return a constant reference to the given sampler id [[nodiscard]] const Sampler& GetSampler(SamplerId id) const noexcept; @@ -195,11 +184,8 @@ public: /// Return a reference to the given sampler id [[nodiscard]] Sampler& GetSampler(SamplerId id) noexcept; - /// Refresh the state for graphics image view and sampler descriptors - void SynchronizeGraphicsDescriptors(); - - /// Refresh the state for compute image view and sampler descriptors - void SynchronizeComputeDescriptors(); + /// Refresh the state for graphics/compute image view and sampler descriptors + void SynchronizeDescriptors(bool compute); /// Updates the Render Targets if they can be rescaled /// @retval True if the Render Targets have been rescaled. @@ -310,15 +296,8 @@ private: /// Runs the Garbage Collector. void RunGarbageCollector(); - /// Fills image_view_ids in the image views in indices - template - void FillImageViews(DescriptorTable& table, - std::span cached_image_view_ids, - std::span views); - /// Find or create an image view in the guest descriptor table - ImageViewId VisitImageView(DescriptorTable& table, - std::span cached_image_view_ids, u32 index); + ImageViewId VisitImageView(u32 index, bool compute); /// Find or create a framebuffer with the given render target parameters FramebufferId GetFramebufferId(const RenderTargets& key); @@ -330,9 +309,6 @@ private: template void UploadImageContents(Image& image, StagingBuffer& staging_buffer); - /// Find or create an image view from a guest descriptor - [[nodiscard]] ImageViewId FindImageView(const TICEntry& config); - /// Create a new image view from a guest descriptor [[nodiscard]] ImageViewId CreateImageView(const TICEntry& config); @@ -360,7 +336,7 @@ private: const Tegra::Engines::Fermi2D::Config& copy); /// Find or create a sampler from a guest descriptor sampler - [[nodiscard]] SamplerId FindSampler(const TSCEntry& config); + [[nodiscard]] SamplerId FindSampler(const TSCEntry& config, bool compute); /// Find or create an image view for the given color buffer index [[nodiscard]] ImageViewId FindColorBuffer(size_t index); diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp index 1ac7a0bc35..54fb8f6239 100644 --- a/src/video_core/video_core.cpp +++ b/src/video_core/video_core.cpp @@ -45,23 +45,23 @@ std::unique_ptr CreateRenderer(Core::System& system, Co namespace VideoCore { -std::unique_ptr CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system) { +/// @brief Creates an emulated GPU instance using the given system context. +void CreateGPU(std::optional& gpu, Core::Frontend::EmuWindow& emu_window, Core::System& system) { Settings::UpdateRescalingInfo(); const auto nvdec_value = Settings::values.nvdec_emulation.GetValue(); const bool use_nvdec = nvdec_value != Settings::NvdecEmulation::Off; const bool use_async = Settings::values.use_asynchronous_gpu_emulation.GetValue(); - auto gpu = std::make_unique(system, use_async, use_nvdec); + gpu.emplace(system, use_async, use_nvdec); auto context = emu_window.CreateSharedContext(); auto scope = context->Acquire(); try { auto renderer = CreateRenderer(system, emu_window, *gpu, std::move(context)); gpu->BindRenderer(std::move(renderer)); - return gpu; } catch (const std::runtime_error& exception) { scope.Cancel(); LOG_ERROR(HW_GPU, "Failed to initialize GPU: {}", exception.what()); - return nullptr; + gpu.reset(); } } diff --git a/src/video_core/video_core.h b/src/video_core/video_core.h index f8e2444f33..1a56dd99b6 100644 --- a/src/video_core/video_core.h +++ b/src/video_core/video_core.h @@ -1,9 +1,13 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2014 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include +#include namespace Core { class System; @@ -20,8 +24,6 @@ class GPU; namespace VideoCore { class RendererBase; - -/// Creates an emulated GPU instance using the given system context. -std::unique_ptr CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system); +void CreateGPU(std::optional& gpu, Core::Frontend::EmuWindow& emu_window, Core::System& system); } // namespace VideoCore diff --git a/src/video_core/vulkan_common/vulkan_debug_callback.cpp b/src/video_core/vulkan_common/vulkan_debug_callback.cpp index d1bd009c8f..0f9aaa9a0b 100644 --- a/src/video_core/vulkan_common/vulkan_debug_callback.cpp +++ b/src/video_core/vulkan_common/vulkan_debug_callback.cpp @@ -27,7 +27,7 @@ VkBool32 DebugUtilCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity, [[maybe_unused]] void* user_data) { // Skip logging known false-positive validation errors switch (static_cast(data->messageIdNumber)) { -#ifdef ANDROID +#ifdef __ANDROID__ case 0xbf9cf353u: // VUID-vkCmdBindVertexBuffers2-pBuffers-04111 // The below are due to incorrect reporting of extendedDynamicState case 0x1093bebbu: // VUID-vkCmdSetCullMode-None-03384 @@ -76,7 +76,7 @@ VkBool32 DebugUtilCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity, } // Route to GPU logger for tracking Vulkan validation messages - if (Settings::values.gpu_logging_enabled.GetValue() && + if (GPU::Logging::IsActive() && Settings::values.gpu_log_vulkan_calls.GetValue()) { // Convert severity to result code for logging (negative = error) int result_code = 0; diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 6e55306079..7bf73dcead 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -26,7 +26,7 @@ #include "video_core/vulkan_common/vulkan_wrapper.h" #include "video_core/gpu_logging/gpu_logging.h" -#if defined(ANDROID) && defined(ARCHITECTURE_arm64) +#if defined(__ANDROID__) && defined(ARCHITECTURE_arm64) #include #include #endif @@ -286,6 +286,12 @@ ankerl::unordered_dense::map GetFormatProperties(v VK_FORMAT_R8_UNORM, VK_FORMAT_R8_USCALED, VK_FORMAT_S8_UINT, + VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK, + VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK, + VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK, + VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK, + VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK, + VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK, }; ankerl::unordered_dense::map format_properties; for (const auto format : formats) { @@ -294,7 +300,7 @@ ankerl::unordered_dense::map GetFormatProperties(v return format_properties; } -#if defined(ANDROID) && defined(ARCHITECTURE_arm64) +#if defined(__ANDROID__) && defined(ARCHITECTURE_arm64) void OverrideBcnFormats(ankerl::unordered_dense::map& format_properties) { // These properties are extracted from Adreno driver 512.687.0 constexpr VkFormatFeatureFlags tiling_features{VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | @@ -486,11 +492,15 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR CollectToolingInfo(); if (is_qualcomm) { - // Qualcomm Adreno GPUs doesn't handle scaled vertex attributes; keep emulation enabled + LOG_WARNING(Render_Vulkan, + "Qualcomm drivers require scaled vertex format emulation"); must_emulate_scaled_formats = true; LOG_WARNING(Render_Vulkan, - "Qualcomm drivers require scaled vertex format emulation; forcing fallback"); - + "Qualcomm drivers have broken provoking vertex"); + RemoveExtension(extensions.provoking_vertex, VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME); + LOG_WARNING(Render_Vulkan, + "Qualcomm drivers have slow push descriptor implementation"); + RemoveExtension(extensions.push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME); LOG_WARNING(Render_Vulkan, "Disabling shader float controls and 64-bit integer features on Qualcomm proprietary drivers"); RemoveExtension(extensions.shader_float_controls, VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME); @@ -500,7 +510,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR features.shader_atomic_int64.shaderSharedInt64Atomics = false; features.features.shaderInt64 = false; -#if defined(ANDROID) && defined(ARCHITECTURE_arm64) +#if defined(__ANDROID__) && defined(ARCHITECTURE_arm64) // BCn patching only safe on Android 9+ (API 28+). Older versions crash on driver load. const auto major = (properties.properties.driverVersion >> 24) << 2; const auto minor = (properties.properties.driverVersion >> 12) & 0xFFFU; @@ -1477,6 +1487,12 @@ void Device::CollectToolingInfo() { has_nsight_graphics = has_nsight_graphics || name == "NVIDIA Nsight Graphics"; has_radeon_gpu_profiler = has_radeon_gpu_profiler || name == "Radeon GPU Profiler"; } +#ifdef _WIN32 + if (has_renderdoc) { + LOG_INFO(Render_Vulkan, + "Windows default RenderDoc output folder: %LOCALAPPDATA%\\Temp\\RenderDoc"); + } +#endif } std::vector Device::GetDeviceQueueCreateInfos() const { @@ -1502,7 +1518,10 @@ std::vector Device::GetDeviceQueueCreateInfos() const { } void Device::InitializeGPULogging() { - if (!Settings::values.gpu_logging_enabled.GetValue()) { + // Get log level from settings — Off is the disable. + const auto log_level = static_cast( + static_cast(Settings::values.gpu_log_level.GetValue())); + if (log_level == GPU::Logging::LogLevel::Off) { return; } @@ -1516,18 +1535,12 @@ void Device::InitializeGPULogging() { detected_driver = GPU::Logging::DriverType::Qualcomm; } - // Get log level from settings - const auto log_level = static_cast( - static_cast(Settings::values.gpu_log_level.GetValue())); - // Initialize GPU logger GPU::Logging::GPULogger::GetInstance().Initialize(log_level, detected_driver); // Configure feature flags GPU::Logging::GPULogger::GetInstance().EnableVulkanCallTracking( Settings::values.gpu_log_vulkan_calls.GetValue()); - GPU::Logging::GPULogger::GetInstance().EnableShaderDumps( - Settings::values.gpu_log_shader_dumps.GetValue()); GPU::Logging::GPULogger::GetInstance().EnableMemoryTracking( Settings::values.gpu_log_memory_tracking.GetValue()); GPU::Logging::GPULogger::GetInstance().EnableDriverDebugInfo( diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index 13fe1b371f..7f2c29519f 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -363,6 +363,11 @@ public: return features.features.textureCompressionBC; } + /// Returns true if ETC2 is natively supported. + bool IsOptimalEtc2Supported() const { + return features.features.textureCompressionETC2; + } + /// Returns true if descriptor aliasing is natively supported. bool IsDescriptorAliasingSupported() const { return GetDriverID() != VK_DRIVER_ID_QUALCOMM_PROPRIETARY; diff --git a/src/video_core/vulkan_common/vulkan_instance.cpp b/src/video_core/vulkan_common/vulkan_instance.cpp index 398342296e..7f3d8ed41f 100644 --- a/src/video_core/vulkan_common/vulkan_instance.cpp +++ b/src/video_core/vulkan_common/vulkan_instance.cpp @@ -36,8 +36,8 @@ namespace { } [[nodiscard]] std::vector RequiredExtensions( - const vk::InstanceDispatch& dld, Core::Frontend::WindowSystemType window_type, - bool enable_validation) { + const vk::InstanceDispatch& dld, std::vector const& properties, + Core::Frontend::WindowSystemType window_type, bool enable_validation) { std::vector extensions; extensions.reserve(6); switch (window_type) { @@ -74,14 +74,14 @@ namespace { if (window_type != Core::Frontend::WindowSystemType::Headless) { extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME); } - if (auto const properties = vk::EnumerateInstanceExtensionProperties(dld); properties) { + // Probe optional extensions against the same snapshot the caller verifies against, so the + // check here and the verification in CreateInstance can never disagree (see TOCTOU note below). #ifdef __APPLE__ - if (AreExtensionsSupported(dld, *properties, std::array{VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME})) - extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); + if (AreExtensionsSupported(dld, properties, std::array{VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME})) + extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); #endif - if (enable_validation && AreExtensionsSupported(dld, *properties, std::array{VK_EXT_DEBUG_UTILS_EXTENSION_NAME})) - extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); - } + if (enable_validation && AreExtensionsSupported(dld, properties, std::array{VK_EXT_DEBUG_UTILS_EXTENSION_NAME})) + extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); return extensions; } @@ -128,9 +128,19 @@ vk::Instance CreateInstance(const Common::DynamicLibrary& library, vk::InstanceD LOG_ERROR(Render_Vulkan, "Failed to load Vulkan function pointers"); throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); } - std::vector const extensions = RequiredExtensions(dld, window_type, enable_validation); + // Enumerate instance extensions exactly once. RequiredExtensions() used to enumerate a second + // time internally; if the driver returned a different set between the two calls (a real TOCTOU + // seen on some AMD iGPU drivers), an extension added to the list could be missing from the + // verification snapshot, throwing EXTENSION_NOT_PRESENT on an otherwise valid launch. Sharing + // one snapshot for both the optional-extension probe and the final check removes that window. auto const properties = vk::EnumerateInstanceExtensionProperties(dld); - if (!properties || !AreExtensionsSupported(dld, *properties, extensions)) + if (!properties) { + LOG_ERROR(Render_Vulkan, "Failed to query instance extension properties"); + throw vk::Exception(VK_ERROR_EXTENSION_NOT_PRESENT); + } + std::vector const extensions = + RequiredExtensions(dld, *properties, window_type, enable_validation); + if (!AreExtensionsSupported(dld, *properties, extensions)) throw vk::Exception(VK_ERROR_EXTENSION_NOT_PRESENT); std::vector layers = Layers(enable_validation); RemoveUnavailableLayers(dld, layers); diff --git a/src/video_core/vulkan_common/vulkan_library.cpp b/src/video_core/vulkan_common/vulkan_library.cpp index 271b622e65..d174a9d3c8 100644 --- a/src/video_core/vulkan_common/vulkan_library.cpp +++ b/src/video_core/vulkan_common/vulkan_library.cpp @@ -16,7 +16,7 @@ namespace Vulkan { std::shared_ptr OpenLibrary( [[maybe_unused]] Core::Frontend::GraphicsContext* context) { LOG_DEBUG(Render_Vulkan, "Looking for a Vulkan library"); -#if defined(ANDROID) && defined(ARCHITECTURE_arm64) +#if defined(__ANDROID__) && defined(ARCHITECTURE_arm64) // Android manages its Vulkan driver from the frontend. return context->GetDriverLibrary(); #else diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp index 9c7cd8a61f..e57864ede8 100644 --- a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp +++ b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp @@ -111,7 +111,7 @@ namespace Vulkan { : allocator{alloc}, allocation{a}, memory{info.deviceMemory}, offset{info.offset}, size{info.size}, mapped_ptr{info.pMappedData} { // Log GPU memory allocation - if (Settings::values.gpu_logging_enabled.GetValue() && + if (GPU::Logging::IsActive() && Settings::values.gpu_log_memory_tracking.GetValue()) { GPU::Logging::GPULogger::GetInstance().LogMemoryAllocation( reinterpret_cast(memory), @@ -179,7 +179,7 @@ namespace Vulkan { void MemoryCommit::Release() { if (allocation && allocator) { // Log GPU memory deallocation - if (Settings::values.gpu_logging_enabled.GetValue() && + if (GPU::Logging::IsActive() && Settings::values.gpu_log_memory_tracking.GetValue() && memory != VK_NULL_HANDLE) { GPU::Logging::GPULogger::GetInstance().LogMemoryDeallocation( @@ -243,7 +243,7 @@ namespace Vulkan { vk::Check(vmaCreateImage(allocator, &ci, &alloc_ci, &handle, &allocation, &alloc_info)); // Log GPU memory allocation for images - if (Settings::values.gpu_logging_enabled.GetValue() && + if (GPU::Logging::IsActive() && Settings::values.gpu_log_memory_tracking.GetValue()) { GPU::Logging::GPULogger::GetInstance().LogMemoryAllocation( reinterpret_cast(alloc_info.deviceMemory), @@ -256,18 +256,20 @@ namespace Vulkan { device.GetDispatchLoader()); } - vk::Buffer - MemoryAllocator::CreateBuffer(const VkBufferCreateInfo &ci, MemoryUsage usage) const - { + vk::Buffer MemoryAllocator::CreateBuffer(const VkBufferCreateInfo &ci, MemoryUsage usage) const { + // MESA will do memcpy() if not marked as host cached, so just force mark it for most buffers + auto const anv_flags = (usage == MemoryUsage::Stream + && device.GetDriverID() == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA) + ? VK_MEMORY_PROPERTY_HOST_CACHED_BIT : 0; const VmaAllocationCreateInfo alloc_ci = { - .flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT | MemoryUsageVmaFlags(usage), - .usage = MemoryUsageVma(usage), - .requiredFlags = 0, - .preferredFlags = MemoryUsagePreferredVmaFlags(usage), - .memoryTypeBits = usage == MemoryUsage::Stream ? 0u : valid_memory_types, - .pool = VK_NULL_HANDLE, - .pUserData = nullptr, - .priority = 0.f, + .flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT | MemoryUsageVmaFlags(usage), + .usage = MemoryUsageVma(usage), + .requiredFlags = 0, + .preferredFlags = MemoryUsagePreferredVmaFlags(usage) | anv_flags, + .memoryTypeBits = usage == MemoryUsage::Stream ? 0u : valid_memory_types, + .pool = VK_NULL_HANDLE, + .pUserData = nullptr, + .priority = 0.f, }; VkBuffer handle{}; @@ -279,7 +281,7 @@ namespace Vulkan { vmaGetAllocationMemoryProperties(allocator, allocation, &property_flags); // Log GPU memory allocation for buffers - if (Settings::values.gpu_logging_enabled.GetValue() && + if (GPU::Logging::IsActive() && Settings::values.gpu_log_memory_tracking.GetValue()) { GPU::Logging::GPULogger::GetInstance().LogMemoryAllocation( reinterpret_cast(alloc_info.deviceMemory), diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp index 871ce52678..3d082c8360 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.cpp +++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp @@ -123,7 +123,6 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { X(vkCmdEndDebugUtilsLabelEXT); X(vkCmdFillBuffer); X(vkCmdPipelineBarrier); - X(vkCmdResetQueryPool); X(vkCmdPushConstants); X(vkCmdPushDescriptorSetWithTemplateKHR); X(vkCmdSetBlendConstants); @@ -726,10 +725,15 @@ PipelineLayout Device::CreatePipelineLayout(const VkPipelineLayoutCreateInfo& ci return PipelineLayout(object, handle, *dld); } -Pipeline Device::CreateGraphicsPipeline(const VkGraphicsPipelineCreateInfo& ci, - VkPipelineCache cache) const { - VkPipeline object; - Check(dld->vkCreateGraphicsPipelines(handle, cache, 1, &ci, nullptr, &object)); +Pipeline Device::CreateGraphicsPipeline(const VkGraphicsPipelineCreateInfo& ci, VkPipelineCache cache) const { + VkPipeline object = VK_NULL_HANDLE; + auto const result = dld->vkCreateGraphicsPipelines(handle, cache, 1, &ci, nullptr, &object); + // Adreno 5xx drivers do not properly return when a graphics pipeline fails to be created + // Some (unkown) Mali drivers also do not properly return + // This result code is out of spec, but should be handled as "kinda working" + if (result == VK_INCOMPLETE) + return Pipeline(object, handle, *dld); + Check(result); return Pipeline(object, handle, *dld); } diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h index 4a3baad2c4..87870893c5 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.h +++ b/src/video_core/vulkan_common/vulkan_wrapper.h @@ -146,6 +146,18 @@ inline VkResult Filter(VkResult result) { return result; } +inline constexpr VkPipelineStageFlags PIPELINE_STAGE_GRAPHICS_COMPUTE = + VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + +inline constexpr VkPipelineStageFlags PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER = + PIPELINE_STAGE_GRAPHICS_COMPUTE | VK_PIPELINE_STAGE_TRANSFER_BIT; + +inline constexpr VkPipelineStageFlags PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER_HOST = + PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER | VK_PIPELINE_STAGE_HOST_BIT; + +inline constexpr VkPipelineStageFlags PIPELINE_STAGE_HOST = VK_PIPELINE_STAGE_HOST_BIT; + + /// Table holding Vulkan instance function pointers. struct InstanceDispatch { PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr{}; @@ -225,7 +237,6 @@ struct DeviceDispatch : InstanceDispatch { PFN_vkCmdEndTransformFeedbackEXT vkCmdEndTransformFeedbackEXT{}; PFN_vkCmdFillBuffer vkCmdFillBuffer{}; PFN_vkCmdPipelineBarrier vkCmdPipelineBarrier{}; - PFN_vkCmdResetQueryPool vkCmdResetQueryPool{}; PFN_vkCmdPushConstants vkCmdPushConstants{}; PFN_vkCmdPushDescriptorSetWithTemplateKHR vkCmdPushDescriptorSetWithTemplateKHR{}; PFN_vkCmdResolveImage vkCmdResolveImage{}; @@ -442,7 +453,7 @@ public: return handle != Type{}; } -#ifndef ANDROID +#ifndef __ANDROID__ /** * Releases ownership of the managed handle. * The caller is responsible for managing the lifetime of the returned handle. @@ -524,7 +535,7 @@ public: return handle != Type{}; } -#ifndef ANDROID +#ifndef __ANDROID__ /** * Releases ownership of the managed handle. * The caller is responsible for managing the lifetime of the returned handle. @@ -1169,10 +1180,6 @@ public: dld->vkCmdEndQuery(handle, query_pool, query); } - void ResetQueryPool(VkQueryPool query_pool, u32 first_query, u32 query_count) const noexcept { - dld->vkCmdResetQueryPool(handle, query_pool, first_query, query_count); - } - void BindDescriptorSets(VkPipelineBindPoint bind_point, VkPipelineLayout layout, u32 first, Span sets, Span dynamic_offsets) const noexcept { dld->vkCmdBindDescriptorSets(handle, bind_point, layout, first, sets.size(), sets.data(), diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index ff0312660b..f806ebf515 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -156,13 +156,12 @@ add_executable(yuzu debugger/controller.cpp debugger/controller.h - game/game_list.cpp - game/game_list.h - game/game_list_p.h - game/game_list_worker.cpp - game/game_list_worker.h - game/game_card.h - game/game_card.cpp + game/game_list.cpp game/game_list.h + game/game_grid.cpp game/game_grid.h + + game/game_tree.h game/game_tree.cpp + game/game_card.h game/game_card.cpp + game/search_field.h game/search_field.cpp hotkeys.cpp hotkeys.h @@ -217,22 +216,18 @@ add_executable(yuzu util/url_request_interceptor.h util/util.cpp util/util.h - vk_device_info.cpp - vk_device_info.h compatdb.cpp compatdb.h - user_data_migration.cpp - user_data_migration.h + + user_data_migration.h user_data_migration.cpp yuzu.qrc yuzu.rc migration_dialog.h migration_dialog.cpp - migration_worker.h - migration_worker.cpp + migration_worker.h migration_worker.cpp + libqt_common.h libqt_common.cpp - deps_dialog.cpp - deps_dialog.h - deps_dialog.ui + deps_dialog.cpp deps_dialog.h deps_dialog.ui data_dialog.h data_dialog.cpp data_dialog.ui data_widget.ui @@ -244,9 +239,9 @@ add_executable(yuzu configuration/addon/mod_select_dialog.h configuration/addon/mod_select_dialog.cpp configuration/addon/mod_select_dialog.ui render/performance_overlay.h render/performance_overlay.cpp render/performance_overlay.ui - libqt_common.h libqt_common.cpp updater/update_dialog.h updater/update_dialog.cpp updater/update_dialog.ui - + game/common.h + game/carousel.h game/carousel.cpp ) set_target_properties(yuzu PROPERTIES OUTPUT_NAME "eden") @@ -466,6 +461,6 @@ if (NOT MSVC AND (APPLE OR NOT YUZU_STATIC_BUILD)) endif() # Remember that the linker is incredibly stupid. -target_link_libraries(yuzu PRIVATE OpenSSL::SSL OpenSSL::Crypto SDL2::SDL2) +target_link_libraries(yuzu PRIVATE OpenSSL::SSL OpenSSL::Crypto SDL3::SDL3) create_target_directory_groups(yuzu) diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index dd55034e4e..248e302aa5 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -6,8 +6,6 @@ #include #include #include -#include -#include #ifdef HAS_OPENGL #include @@ -24,7 +22,11 @@ #include #include #include + +#include "input_common/drivers/camera.h" + #endif + #include #include #include @@ -45,16 +47,13 @@ #include #endif -#include "common/polyfill_thread.h" #include "common/scm_rev.h" #include "common/settings.h" #include "common/settings_input.h" -#include "common/thread.h" #include "core/core.h" #include "core/cpu_manager.h" #include "core/frontend/framebuffer_layout.h" #include "core/frontend/graphics_context.h" -#include "input_common/drivers/camera.h" #include "input_common/drivers/keyboard.h" #include "input_common/drivers/mouse.h" #include "input_common/drivers/tas_input.h" @@ -62,189 +61,19 @@ #include "input_common/main.h" #include "qt_common/qt_common.h" #include "video_core/gpu.h" -#include "video_core/rasterizer_interface.h" #include "video_core/renderer_base.h" #include "yuzu/bootmanager.h" #include "yuzu/main_window.h" +#include "qt_common/render/context.h" +#include "qt_common/render/emu_thread.h" + class QObject; class QPaintEngine; class QSurface; constexpr int default_mouse_constrain_timeout = 10; -EmuThread::EmuThread(Core::System& system) : m_system{system} {} - -EmuThread::~EmuThread() = default; - -void EmuThread::run() { - Common::SetCurrentThreadName("EmuControlThread"); - - auto& gpu = m_system.GPU(); - auto stop_token = m_stop_source.get_token(); - - m_system.RegisterHostThread(); - - // Main process has been loaded. Make the context current to this thread and begin GPU and CPU - // execution. - gpu.ObtainContext(); - - emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); - if (Settings::values.use_disk_shader_cache.GetValue()) { - m_system.Renderer().ReadRasterizer()->LoadDiskResources( - m_system.GetApplicationProcessProgramID(), stop_token, - [this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) { - emit LoadProgress(stage, value, total); - }); - } - emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); - - gpu.ReleaseContext(); - gpu.Start(); - - m_system.GetCpuManager().OnGpuReady(); - - if (m_system.DebuggerEnabled()) { - m_system.InitializeDebugger(); - } - - while (!stop_token.stop_requested()) { - std::unique_lock lk{m_should_run_mutex}; - if (m_should_run) { - m_system.Run(); - m_stopped.Reset(); - - m_should_run_cv.wait(lk, stop_token, [&] { return !m_should_run; }); - } else { - m_system.Pause(); - m_stopped.Set(); - - EmulationPaused(lk); - m_should_run_cv.wait(lk, stop_token, [&] { return m_should_run; }); - EmulationResumed(lk); - } - } - - // Shutdown the main emulated process - m_system.DetachDebugger(); - m_system.ShutdownMainProcess(); -} - -// Unlock while emitting signals so that the main thread can -// continue pumping events. - -void EmuThread::EmulationPaused(std::unique_lock& lk) { - lk.unlock(); - emit DebugModeEntered(); - lk.lock(); -} - -void EmuThread::EmulationResumed(std::unique_lock& lk) { - lk.unlock(); - emit DebugModeLeft(); - lk.lock(); -} - -#ifdef HAS_OPENGL -class OpenGLSharedContext : public Core::Frontend::GraphicsContext { -public: - /// Create the original context that should be shared from - explicit OpenGLSharedContext(QSurface* surface_) : surface{surface_} { - QSurfaceFormat format; - format.setVersion(4, 6); - format.setProfile(QSurfaceFormat::CompatibilityProfile); - format.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions); - if (Settings::values.renderer_debug) { - format.setOption(QSurfaceFormat::FormatOption::DebugContext); - } - // TODO: expose a setting for buffer value (ie default/single/double/triple) - format.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior); - format.setSwapInterval(0); - - context = std::make_unique(); - context->setFormat(format); - if (!context->create()) { - LOG_ERROR(Frontend, "Unable to create main openGL context"); - } - } - - /// Create the shared contexts for rendering and presentation - explicit OpenGLSharedContext(QOpenGLContext* share_context, QSurface* main_surface = nullptr) { - - // disable vsync for any shared contexts - auto format = share_context->format(); - const int swap_interval = - Settings::values.vsync_mode.GetValue() == Settings::VSyncMode::Immediate ? 0 : 1; - - format.setSwapInterval(main_surface ? swap_interval : 0); - - context = std::make_unique(); - context->setShareContext(share_context); - context->setFormat(format); - if (!context->create()) { - LOG_ERROR(Frontend, "Unable to create shared openGL context"); - } - - if (!main_surface) { - offscreen_surface = std::make_unique(nullptr); - offscreen_surface->setFormat(format); - offscreen_surface->create(); - surface = offscreen_surface.get(); - } else { - surface = main_surface; - } - } - - ~OpenGLSharedContext() { - DoneCurrent(); - } - - void SwapBuffers() override { - if (auto window = static_cast(surface)) { - if (!window->isExposed()) { - LOG_DEBUG(Frontend, "SwapBuffers ignored: window not exposed"); - return; - } - } - - context->swapBuffers(surface); - } - - void MakeCurrent() override { - // We can't track the current state of the underlying context in this wrapper class because - // Qt may make the underlying context not current for one reason or another. In particular, - // the WebBrowser uses GL, so it seems to conflict if we aren't careful. - // Instead of always just making the context current (which does not have any caching to - // check if the underlying context is already current) we can check for the current context - // in the thread local data by calling `currentContext()` and checking if its ours. - if (QOpenGLContext::currentContext() != context.get()) { - context->makeCurrent(surface); - } - } - - void DoneCurrent() override { - context->doneCurrent(); - } - - QOpenGLContext* GetShareContext() { - return context.get(); - } - - const QOpenGLContext* GetShareContext() const { - return context.get(); - } - -private: - // Avoid using Qt parent system here since we might move the QObjects to new threads - // As a note, this means we should avoid using slots/signals with the objects too - std::unique_ptr context; - std::unique_ptr offscreen_surface{}; - QSurface* surface; -}; -#endif - -class DummyContext : public Core::Frontend::GraphicsContext {}; - class RenderWidget : public QWidget { public: explicit RenderWidget(GRenderWindow* parent) : QWidget(parent) { @@ -285,11 +114,9 @@ struct NullRenderWidget : public RenderWidget { explicit NullRenderWidget(GRenderWindow* parent) : RenderWidget(parent) {} }; -GRenderWindow::GRenderWindow(MainWindow* parent, EmuThread* emu_thread_, - std::shared_ptr input_subsystem_, - Core::System& system_) - : QWidget(parent), emu_thread(emu_thread_), input_subsystem{std::move(input_subsystem_)}, - system{system_} { +GRenderWindow::GRenderWindow(MainWindow* parent, + std::shared_ptr input_subsystem_) + : QWidget(parent), input_subsystem{std::move(input_subsystem_)} { setWindowTitle(QStringLiteral("Eden %1 | %2-%3") .arg(QString::fromUtf8(Common::g_build_name), QString::fromUtf8(Common::g_scm_branch), @@ -708,10 +535,11 @@ void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { } void GRenderWindow::ConstrainMouse() { - if (emu_thread == nullptr || !Settings::values.mouse_panning) { + if (QtCommon::emu_thread == nullptr || !Settings::values.mouse_panning) { mouse_constrain_timer.stop(); return; } + if (!this->isActiveWindow()) { mouse_constrain_timer.stop(); return; @@ -964,7 +792,7 @@ void GRenderWindow::ReleaseRenderTarget() { } void GRenderWindow::CaptureScreenshot(const QString& screenshot_path) { - auto& renderer = system.Renderer(); + auto& renderer = QtCommon::system->Renderer(); if (renderer.IsScreenshotPending()) { LOG_WARNING(Render, @@ -989,7 +817,12 @@ void GRenderWindow::CaptureScreenshot(const QString& screenshot_path) { screenshot_image.bits(), [=, this](bool invert_y) { const std::string std_screenshot_path = screenshot_path.toStdString(); +#if QT_VERSION >= QT_VERSION_CHECK(6, 9, 0) + if (screenshot_image.flipped(invert_y ? Qt::Vertical : (Qt::Orientations)0) + .save(screenshot_path)) { +#else if (screenshot_image.mirrored(false, invert_y).save(screenshot_path)) { +#endif LOG_INFO(Frontend, "Screenshot saved to \"{}\"", std_screenshot_path); } else { LOG_ERROR(Frontend, "Failed to save screenshot to \"{}\"", std_screenshot_path); @@ -1026,7 +859,8 @@ bool GRenderWindow::InitializeOpenGL() { return true; #else - QMessageBox::warning(this, tr("OpenGL not available!"), tr("Eden has not been compiled with OpenGL support.")); + QMessageBox::warning(this, tr("OpenGL not available!"), + tr("Eden has not been compiled with OpenGL support.")); return false; #endif } @@ -1072,8 +906,7 @@ bool GRenderWindow::LoadOpenGL() { tr("Your GPU may not support one or more required OpenGL extensions. Please ensure you " "have the latest graphics driver.

GL Renderer:
%1

Unsupported " "extensions:
%2") - .arg(renderer) - .arg(missing_ext.join(QStringLiteral("
")))); + .arg(renderer, missing_ext.join(QStringLiteral("
")))); // Non fatal } return true; @@ -1101,14 +934,6 @@ QStringList GRenderWindow::GetUnsupportedGLExtensions() const { return missing_ext; } -void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread_) { - emu_thread = emu_thread_; -} - -void GRenderWindow::OnEmulationStopping() { - emu_thread = nullptr; -} - void GRenderWindow::showEvent(QShowEvent* event) { QWidget::showEvent(event); diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index e763cd9868..1ed61a8191 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h @@ -6,12 +6,9 @@ #pragma once -#include #include #include -#include #include -#include #include #include @@ -27,9 +24,6 @@ #include #include "common/common_types.h" -#include "common/logging.h" -#include "common/polyfill_thread.h" -#include "common/thread.h" #include "core/frontend/emu_window.h" class MainWindow; @@ -46,6 +40,7 @@ class QShowEvent; class QTouchEvent; class QWheelEvent; +class EmuThread; namespace Core { class System; } // namespace Core @@ -63,96 +58,12 @@ namespace VideoCore { enum class LoadCallbackStage; } // namespace VideoCore -class EmuThread final : public QThread { - Q_OBJECT - -public: - explicit EmuThread(Core::System& system); - ~EmuThread() override; - - /** - * Start emulation (on new thread) - * @warning Only call when not running! - */ - void run() override; - - /** - * Sets whether the emulation thread should run or not - * @param should_run Boolean value, set the emulation thread to running if true - */ - void SetRunning(bool should_run) { - // TODO: Prevent other threads from modifying the state until we finish. - { - // Notify the running thread to change state. - std::unique_lock run_lk{m_should_run_mutex}; - m_should_run = should_run; - m_should_run_cv.notify_one(); - } - - // Wait until paused, if pausing. - if (!should_run) { - m_stopped.Wait(); - } - } - - /** - * Check if the emulation thread is running or not - * @return True if the emulation thread is running, otherwise false - */ - bool IsRunning() const { - return m_should_run; - } - - /** - * Requests for the emulation thread to immediately stop running - */ - void ForceStop() { - LOG_WARNING(Frontend, "Force stopping EmuThread"); - m_stop_source.request_stop(); - } - -private: - void EmulationPaused(std::unique_lock& lk); - void EmulationResumed(std::unique_lock& lk); - -private: - Core::System& m_system; - - std::stop_source m_stop_source; - std::mutex m_should_run_mutex; - std::condition_variable_any m_should_run_cv; - Common::Event m_stopped; - bool m_should_run{true}; - -signals: - /** - * Emitted when the CPU has halted execution - * - * @warning When connecting to this signal from other threads, make sure to specify either - * Qt::QueuedConnection (invoke slot within the destination object's message thread) or even - * Qt::BlockingQueuedConnection (additionally block source thread until slot returns) - */ - void DebugModeEntered(); - - /** - * Emitted right before the CPU continues execution - * - * @warning When connecting to this signal from other threads, make sure to specify either - * Qt::QueuedConnection (invoke slot within the destination object's message thread) or even - * Qt::BlockingQueuedConnection (additionally block source thread until slot returns) - */ - void DebugModeLeft(); - - void LoadProgress(VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total); -}; - class GRenderWindow : public QWidget, public Core::Frontend::EmuWindow { Q_OBJECT public: - explicit GRenderWindow(MainWindow* parent, EmuThread* emu_thread_, - std::shared_ptr input_subsystem_, - Core::System& system_); + explicit GRenderWindow(MainWindow* parent, + std::shared_ptr input_subsystem_); ~GRenderWindow() override; // EmuWindow implementation. @@ -217,8 +128,6 @@ public: void Exit(); public slots: - void OnEmulationStarting(EmuThread* emu_thread_); - void OnEmulationStopping(); void OnFramebufferSizeChanged(); signals: @@ -247,7 +156,6 @@ private: bool LoadOpenGL(); QStringList GetUnsupportedGLExtensions() const; - EmuThread* emu_thread; std::shared_ptr input_subsystem; // Main context that will be shared with all other contexts that are requested. @@ -277,8 +185,6 @@ private: QTimer mouse_constrain_timer; - Core::System& system; - protected: void showEvent(QShowEvent* event) override; bool eventFilter(QObject* object, QEvent* event) override; diff --git a/src/yuzu/configuration/addon/mod_select_dialog.cpp b/src/yuzu/configuration/addon/mod_select_dialog.cpp index 49f556a66a..f025a3b390 100644 --- a/src/yuzu/configuration/addon/mod_select_dialog.cpp +++ b/src/yuzu/configuration/addon/mod_select_dialog.cpp @@ -33,8 +33,8 @@ ModSelectDialog::ModSelectDialog(const QStringList& mods, QWidget* parent) ui->treeView->expandAll(); int rows = item_model->rowCount(); - int height = - 4 + ui->treeView->contentsMargins().top() * 4 + ui->treeView->contentsMargins().bottom() * 4; + int height = 4 + ui->treeView->contentsMargins().top() * 4 + + ui->treeView->contentsMargins().bottom() * 4; int width = 0; for (int i = 0; i < rows; ++i) { diff --git a/src/yuzu/configuration/configure_cpu.cpp b/src/yuzu/configuration/configure_cpu.cpp index b31ca849a6..0511465ee6 100644 --- a/src/yuzu/configuration/configure_cpu.cpp +++ b/src/yuzu/configuration/configure_cpu.cpp @@ -76,7 +76,8 @@ void ConfigureCpu::Setup(const ConfigurationShared::Builder& builder) { } else if (setting->Id() == Settings::values.cpu_backend.Id()) { backend_layout->addWidget(widget); backend_combobox = widget->combobox; - } else if (setting->Id() == Settings::values.fast_cpu_time.Id() || setting->Id() == Settings::values.cpu_ticks.Id()) { + } else if (setting->Id() == Settings::values.fast_cpu_time.Id() || + setting->Id() == Settings::values.cpu_ticks.Id()) { ui->general_layout->addWidget(widget); } else { // Presently, all other settings here are unsafe checkboxes diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp index 9db48c084b..47abf66036 100644 --- a/src/yuzu/configuration/configure_debug.cpp +++ b/src/yuzu/configuration/configure_debug.cpp @@ -51,8 +51,12 @@ void ConfigureDebug::SetConfiguration() { ui->enable_all_controllers->setChecked(Settings::values.enable_all_controllers.GetValue()); ui->extended_logging->setChecked(Settings::values.extended_logging.GetValue()); ui->perform_vulkan_check->setChecked(Settings::values.perform_vulkan_check.GetValue()); - ui->serial_battery_edit->setText(QString::fromStdString(std::to_string(Settings::values.serial_battery.GetValue()))); - ui->serial_board_edit->setText(QString::fromStdString(std::to_string(Settings::values.serial_unit.GetValue()))); + ui->serial_battery_edit->setText( + QString::number(Settings::values.serial_battery.GetValue()) + ); + ui->serial_board_edit->setText( + QString::number(Settings::values.serial_unit.GetValue()) + ); #ifdef YUZU_USE_QT_WEB_ENGINE ui->disable_web_applet->setChecked(Settings::values.disable_web_applet.GetValue()); #else @@ -77,8 +81,8 @@ void ConfigureDebug::SetConfiguration() { ui->enable_shader_feedback->setChecked(Settings::values.renderer_shader_feedback.GetValue()); ui->enable_nsight_aftermath->setEnabled(runtime_lock); ui->enable_nsight_aftermath->setChecked(Settings::values.enable_nsight_aftermath.GetValue()); - ui->dump_shaders->setEnabled(runtime_lock); - ui->dump_shaders->setChecked(Settings::values.dump_shaders.GetValue()); + ui->dump_guest_shaders->setEnabled(runtime_lock); + ui->dump_guest_shaders->setChecked(Settings::values.dump_guest_shaders.GetValue()); ui->dump_macros->setEnabled(runtime_lock); ui->dump_macros->setChecked(Settings::values.dump_macros.GetValue()); ui->disable_macro_jit->setEnabled(runtime_lock); @@ -90,6 +94,12 @@ void ConfigureDebug::SetConfiguration() { Settings::values.disable_shader_loop_safety_checks.GetValue()); ui->perform_vulkan_check->setChecked(Settings::values.perform_vulkan_check.GetValue()); ui->debug_knobs_spinbox->setValue(Settings::values.debug_knobs.GetValue()); + + ui->gpu_log_level->setEnabled(runtime_lock); + ui->gpu_log_level->setCurrentIndex( + static_cast(Settings::values.gpu_log_level.GetValue())); + ui->gpu_log_shader_dumps->setEnabled(runtime_lock); + ui->gpu_log_shader_dumps->setChecked(Settings::values.gpu_log_shader_dumps.GetValue()); #ifdef YUZU_USE_QT_WEB_ENGINE ui->disable_web_applet->setChecked(Settings::values.disable_web_applet.GetValue()); #else @@ -119,7 +129,7 @@ void ConfigureDebug::ApplyConfiguration() { Settings::values.disable_buffer_reorder = ui->disable_buffer_reorder->isChecked(); Settings::values.renderer_shader_feedback = ui->enable_shader_feedback->isChecked(); Settings::values.enable_nsight_aftermath = ui->enable_nsight_aftermath->isChecked(); - Settings::values.dump_shaders = ui->dump_shaders->isChecked(); + Settings::values.dump_guest_shaders = ui->dump_guest_shaders->isChecked(); Settings::values.dump_macros = ui->dump_macros->isChecked(); Settings::values.disable_shader_loop_safety_checks = ui->disable_loop_safety_checks->isChecked(); @@ -131,6 +141,9 @@ void ConfigureDebug::ApplyConfiguration() { Settings::values.serial_battery = ui->serial_battery_edit->text().toUInt(); Settings::values.serial_unit = ui->serial_board_edit->text().toUInt(); Settings::values.debug_knobs = ui->debug_knobs_spinbox->value(); + Settings::values.gpu_log_level = + static_cast(ui->gpu_log_level->currentIndex()); + Settings::values.gpu_log_shader_dumps = ui->gpu_log_shader_dumps->isChecked(); Debugger::ToggleConsole(); Common::Log::Filter filter; filter.ParseFilterString(Settings::values.log_filter.GetValue()); diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui index f76f3b012d..4e49c55f37 100644 --- a/src/yuzu/configuration/configure_debug.ui +++ b/src/yuzu/configuration/configure_debug.ui @@ -157,7 +157,7 @@ Logging - + @@ -197,7 +197,77 @@ - + + + + + 0 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + GPU Logging/Level + + + + + + + Detail level for GPU logs. Off disables logging entirely. + + + + Off + + + + + Errors + + + + + Standard + + + + + Verbose + + + + + All + + + + + + + + + + + Show Log in Console + + + + true @@ -210,15 +280,20 @@ - - - - Show Log in Console - - - - + + + + 1 + 0 + + + + + 250 + 0 + + Open Log Location @@ -270,7 +345,7 @@ - + true @@ -294,15 +369,15 @@ - + true - When checked, it will dump all the original assembler shaders from the disk shader cache or game as found + When checked, it will dump original Maxwell guest shader bytecode (the input to the recompiler) as .ash files under DumpDir/shaders/. Useful for inspection with nvdisasm. - Dump Game Shaders + Dump Guest (Maxwell) Shaders @@ -313,7 +388,7 @@ - + true @@ -326,7 +401,7 @@ - + true @@ -352,7 +427,7 @@ - + Qt::Orientation::Vertical @@ -388,6 +463,16 @@ + + + + When checked, it will dump the recompiler's output SPIR-V binaries (.spv) under LogDir/shaders/. Inspect via SPIRV-Tools (spirv-dis / spirv-cross / spirv-val) or RenderDoc. + + + Dump SPIR-V Shaders + + + @@ -514,31 +599,65 @@ - - - - 0 - - - 65535 - - - Bitmask for quick development toggles - - - Set debug knobs (bitmask) - - - 16-bit debug knob set for quick development toggles - - - (bitmask) - - - Debug Knobs: - - - + + + + + 0 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + Debug Knobs: + + + + + + + 0 + + + 65535 + + + Bitmask for quick development toggles + + + Set debug knobs (bitmask) + + + 16-bit debug knob set for quick development toggles + + + (bitmask) + + + + + + @@ -579,7 +698,7 @@ - + Qt::Orientation::Vertical diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp index d4a16e9f73..547008262e 100644 --- a/src/yuzu/configuration/configure_dialog.cpp +++ b/src/yuzu/configuration/configure_dialog.cpp @@ -10,8 +10,8 @@ #include "common/settings_enums.h" #include "core/core.h" #include "qt_common/config/uisettings.h" +#include "qt_common/util/vk.h" #include "ui_configure.h" -#include "vk_device_info.h" #include "yuzu/configuration/configure_applets.h" #include "yuzu/configuration/configure_audio.h" #include "yuzu/configuration/configure_cpu.h" diff --git a/src/yuzu/configuration/configure_dialog.h b/src/yuzu/configuration/configure_dialog.h index 5c504c9a16..73db11238e 100644 --- a/src/yuzu/configuration/configure_dialog.h +++ b/src/yuzu/configuration/configure_dialog.h @@ -11,8 +11,8 @@ #include #include "configuration/shared_widget.h" #include "qt_common/config/shared_translation.h" +#include "qt_common/util/vk.h" #include "yuzu/configuration/configuration_shared.h" -#include "yuzu/vk_device_info.h" namespace Core { class System; diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index 54835aea5a..f294403397 100644 --- a/src/yuzu/configuration/configure_graphics.cpp +++ b/src/yuzu/configuration/configure_graphics.cpp @@ -4,14 +4,7 @@ // SPDX-FileCopyrightText: 2016 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include #include -#include -#include -#include -#include -#include -#include #include #include #include @@ -34,52 +27,15 @@ #include #include "common/common_types.h" -#include "common/dynamic_library.h" -#include "common/logging.h" #include "common/settings.h" #include "common/settings_enums.h" #include "core/core.h" #include "qt_common/config/uisettings.h" -#include "qt_common/qt_common.h" +#include "qt_common/util/vk.h" #include "ui_configure_graphics.h" #include "yuzu/configuration/configuration_shared.h" #include "yuzu/configuration/configure_graphics.h" #include "yuzu/configuration/shared_widget.h" -#include "yuzu/vk_device_info.h" - -static const std::vector default_present_modes{VK_PRESENT_MODE_IMMEDIATE_KHR, - VK_PRESENT_MODE_FIFO_KHR}; - -// Converts a setting to a present mode (or vice versa) -static constexpr VkPresentModeKHR VSyncSettingToMode(Settings::VSyncMode mode) { - switch (mode) { - case Settings::VSyncMode::Immediate: - return VK_PRESENT_MODE_IMMEDIATE_KHR; - case Settings::VSyncMode::Mailbox: - return VK_PRESENT_MODE_MAILBOX_KHR; - case Settings::VSyncMode::Fifo: - return VK_PRESENT_MODE_FIFO_KHR; - case Settings::VSyncMode::FifoRelaxed: - return VK_PRESENT_MODE_FIFO_RELAXED_KHR; - default: - return VK_PRESENT_MODE_FIFO_KHR; - } -} - -static constexpr Settings::VSyncMode PresentModeToSetting(VkPresentModeKHR mode) { - switch (mode) { - case VK_PRESENT_MODE_IMMEDIATE_KHR: - return Settings::VSyncMode::Immediate; - case VK_PRESENT_MODE_MAILBOX_KHR: - return Settings::VSyncMode::Mailbox; - case VK_PRESENT_MODE_FIFO_KHR: - return Settings::VSyncMode::Fifo; - case VK_PRESENT_MODE_FIFO_RELAXED_KHR: - return Settings::VSyncMode::FifoRelaxed; - default: - return Settings::VSyncMode::Fifo; - } -} ConfigureGraphics::ConfigureGraphics( const Core::System& system_, std::vector& records_, @@ -265,7 +221,7 @@ void ConfigureGraphics::Setup(const ConfigurationShared::Builder& builder) { // FSR needs a reversed slider and a 0.5 multiplier return builder.BuildWidget( setting, apply_funcs, ConfigurationShared::RequestType::ReverseSlider, true, - 0.5f, nullptr, tr("%", "FSR sharpening percentage (e.g. 50%)")); + 0.5f, nullptr, tr("%", "FSR/SGSR sharpening percentage (e.g. 50%)")); } else { return builder.BuildWidget(setting, apply_funcs); } diff --git a/src/yuzu/configuration/configure_graphics.h b/src/yuzu/configuration/configure_graphics.h index 3009edf4b6..9002af437b 100644 --- a/src/yuzu/configuration/configure_graphics.h +++ b/src/yuzu/configuration/configure_graphics.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: 2016 Citra Emulator Project @@ -19,7 +19,7 @@ #include "common/common_types.h" #include "common/settings_enums.h" #include "qt_common/config/shared_translation.h" -#include "vk_device_info.h" +#include "qt_common/util/vk.h" #include "yuzu/configuration/configuration_shared.h" class QPushButton; diff --git a/src/yuzu/configuration/configure_motion_touch.cpp b/src/yuzu/configuration/configure_motion_touch.cpp index d1bcac12aa..2083315c03 100644 --- a/src/yuzu/configuration/configure_motion_touch.cpp +++ b/src/yuzu/configuration/configure_motion_touch.cpp @@ -94,11 +94,10 @@ void ConfigureMotionTouch::SetConfiguration() { const Common::ParamPackage touch_param(Settings::values.touch_device.GetValue()); touch_from_button_maps = Settings::values.touch_from_button_maps; - for (const auto& touch_map : touch_from_button_maps) { + for (const auto& touch_map : touch_from_button_maps) ui->touch_from_button_map->addItem(QString::fromStdString(touch_map.name)); - } - ui->touch_from_button_map->setCurrentIndex( - Settings::values.touch_from_button_map_index.GetValue()); + if (auto const index = Settings::values.touch_from_button_map_index.GetValue(); int(index) < ui->touch_from_button_map->count()) + ui->touch_from_button_map->setCurrentIndex(index); min_x = touch_param.Get("min_x", 100); min_y = touch_param.Get("min_y", 50); diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp index dcf40d678f..6f530eb0ab 100644 --- a/src/yuzu/configuration/configure_per_game.cpp +++ b/src/yuzu/configuration/configure_per_game.cpp @@ -30,6 +30,8 @@ #include "core/loader/loader.h" #include "frontend_common/config.h" #include "qt_common/config/uisettings.h" +#include "qt_common/qt_common.h" +#include "qt_common/util/vk.h" #include "ui_configure_per_game.h" #include "yuzu/configuration/configuration_shared.h" #include "yuzu/configuration/configure_applets.h" @@ -44,7 +46,6 @@ #include "yuzu/configuration/configure_per_game_addons.h" #include "yuzu/configuration/configure_system.h" #include "yuzu/util/util.h" -#include "yuzu/vk_device_info.h" ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::string& file_name, std::vector& vk_device_records, @@ -205,6 +206,6 @@ void ConfigurePerGame::LoadConfiguration() { ui->display_format->setText( QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))); - const auto valueText = ReadableByteSize(file->GetSize()); + const auto valueText = QtCommon::ReadableByteSize(file->GetSize()); ui->display_size->setText(valueText); } diff --git a/src/yuzu/configuration/configure_per_game.h b/src/yuzu/configuration/configure_per_game.h index ca973db8b0..bc6561235e 100644 --- a/src/yuzu/configuration/configure_per_game.h +++ b/src/yuzu/configuration/configure_per_game.h @@ -18,7 +18,7 @@ #include "frontend_common/config.h" #include "qt_common/config/qt_config.h" #include "qt_common/config/shared_translation.h" -#include "vk_device_info.h" +#include "qt_common/util/vk.h" #include "yuzu/configuration/configuration_shared.h" namespace Core { diff --git a/src/yuzu/data_dialog.cpp b/src/yuzu/data_dialog.cpp index 8d50843eb3..ffdedcf1c2 100644 --- a/src/yuzu/data_dialog.cpp +++ b/src/yuzu/data_dialog.cpp @@ -63,27 +63,31 @@ DataWidget::DataWidget(FrontendCommon::DataManager::DataDir data_dir, void DataWidget::clear() { std::optional user_id = selectProfile(); - if (!user_id) return; + if (!user_id) + return; QtCommon::Content::ClearDataDir(m_dir, user_id.value()); scan(); } void DataWidget::open() { std::optional user_id = selectProfile(); - if (!user_id) return; - QDesktopServices::openUrl(QUrl::fromLocalFile( - QString::fromStdString(FrontendCommon::DataManager::GetDataDirString(m_dir, user_id.value())))); + if (!user_id) + return; + QDesktopServices::openUrl(QUrl::fromLocalFile(QString::fromStdString( + FrontendCommon::DataManager::GetDataDirString(m_dir, user_id.value())))); } void DataWidget::upload() { std::optional user_id = selectProfile(); - if (!user_id) return; + if (!user_id) + return; QtCommon::Content::ExportDataDir(m_dir, user_id.value(), m_exportName); } void DataWidget::download() { std::optional user_id = selectProfile(); - if (!user_id) return; + if (!user_id) + return; QtCommon::Content::ImportDataDir(m_dir, user_id.value(), std::bind(&DataWidget::scan, this)); } @@ -107,7 +111,8 @@ std::optional DataWidget::selectProfile() { std::string user_id{}; if (m_dir == FrontendCommon::DataManager::DataDir::Saves) { user_id = GetProfileIDString(); - if (user_id.empty()) return std::nullopt; + if (user_id.empty()) + return std::nullopt; } return user_id; diff --git a/src/yuzu/data_dialog.h b/src/yuzu/data_dialog.h index 2915f00848..b780debdff 100644 --- a/src/yuzu/data_dialog.h +++ b/src/yuzu/data_dialog.h @@ -1,8 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later -#ifndef DATA_DIALOG_H -#define DATA_DIALOG_H +#pragma once #include #include "frontend_common/data_manager.h" @@ -47,5 +46,3 @@ private: std::optional selectProfile(); }; - -#endif // DATA_DIALOG_H diff --git a/src/yuzu/debugger/console.cpp b/src/yuzu/debugger/console.cpp index 8965c7a08f..3506f1edf3 100644 --- a/src/yuzu/debugger/console.cpp +++ b/src/yuzu/debugger/console.cpp @@ -11,7 +11,6 @@ #endif #include "common/logging.h" -#include "yuzu/debugger/console.h" #include "qt_common/config/uisettings.h" #include "yuzu/debugger/console.h" diff --git a/src/yuzu/game/carousel.cpp b/src/yuzu/game/carousel.cpp new file mode 100644 index 0000000000..ac9c9947b5 --- /dev/null +++ b/src/yuzu/game/carousel.cpp @@ -0,0 +1,107 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "qt_common/config/uisettings.h" +#include "qt_common/game_list/model.h" +#include "yuzu/game/common.h" +#include "yuzu/game/game_card.h" +#include "yuzu/game/carousel.h" + +GameCarousel::GameCarousel(QWidget* parent) : QListView{parent} { + m_gameCard = new GameCard(this); + setItemDelegate(m_gameCard); + + setViewMode(QListView::IconMode); + setMovement(QListView::Static); + setUniformItemSizes(true); + setSelectionMode(QAbstractItemView::SingleSelection); + + setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); + setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); + + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + setEditTriggers(QAbstractItemView::NoEditTriggers); + setContextMenuPolicy(Qt::CustomContextMenu); + + setSpacing(10); + setWordWrap(true); + setTextElideMode(Qt::ElideRight); + setFlow(QListView::LeftToRight); + setWrapping(false); +} + +void GameCarousel::SetModel(GameListModel* model) { + QListView::setModel(model); + UpdateIconSize(); +} + +void GameCarousel::ApplyFilter(const QString& edit_filter_text, GameListModel* model) { + int row_count = model->rowCount(); + + for (int i = 0; i < row_count; ++i) { + QStandardItem* item = model->item(i, 0); + if (!item) + continue; + + if (Yuzu::FilterMatches(edit_filter_text, item)) { + setRowHidden(i, false); + } else { + setRowHidden(i, true); + } + } +} + +void GameCarousel::UpdateIconSize() { + const u32 icon_size = UISettings::values.game_icon_size.GetValue(); + + int heightMargin = 0; + int widthMargin = 80; + + // TODO(crueter): get rid of this nonsense + if (UISettings::values.show_game_name) { + switch (icon_size) { + case 128: + heightMargin = 65; + break; + case 0: + widthMargin = 120; + heightMargin = 120; + break; + case 64: + heightMargin = 77; + break; + case 32: + case 256: + heightMargin = 81; + break; + } + } else { + widthMargin = 24; + heightMargin = 24; + } + + const int min_item_width = icon_size + widthMargin; + const int min_item_height = icon_size + heightMargin; + const int grid_height = std::max(min_item_height, viewport()->height()); + + QSize content_size(min_item_width, min_item_height); + QSize grid_size(min_item_width, grid_height); + if (gridSize() != grid_size) { + setUpdatesEnabled(false); + + setGridSize(grid_size); + m_gameCard->setSize(grid_size, content_size, 0, 0); + + setUpdatesEnabled(true); + } +} + +QModelIndex GameCarousel::indexAt(const QPoint& point) const { + QModelIndex index = QListView::indexAt(point); + if (!index.isValid()) + return {}; + if (m_gameCard && !m_gameCard->hitTest(point, index, this, visualRect(index))) + return {}; + return index; +} diff --git a/src/yuzu/game/carousel.h b/src/yuzu/game/carousel.h new file mode 100644 index 0000000000..c03884c8c2 --- /dev/null +++ b/src/yuzu/game/carousel.h @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include + +class GameCard; +class GameListModel; +class QResizeEvent; + +class GameCarousel : public QListView { + Q_OBJECT + +public: + explicit GameCarousel(QWidget* parent = nullptr); + + void SetModel(GameListModel* model); + void ApplyFilter(const QString& edit_filter_text, GameListModel* model); + void UpdateIconSize(); + + QModelIndex indexAt(const QPoint& point) const override; + +private: + GameCard* m_gameCard = nullptr; +}; diff --git a/src/yuzu/game/common.h b/src/yuzu/game/common.h new file mode 100644 index 0000000000..03505d6313 --- /dev/null +++ b/src/yuzu/game/common.h @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include + +#include "qt_common/game_list/game_list_p.h" + +namespace Yuzu { + +inline bool ContainsAllWords(const QString& haystack, const QString& userinput) { + const QStringList userinput_split = userinput.split(QLatin1Char{' '}, Qt::SkipEmptyParts); + return std::all_of(userinput_split.begin(), userinput_split.end(), + [&haystack](const QString& s) { return haystack.contains(s); }); +} + +inline bool FilterMatches(const QString& filter, const QStandardItem* item) { + if (filter.isEmpty()) + return true; + + const auto program_id = item->data(GameListItemPath::ProgramIdRole).toULongLong(); + + const QString file_path = item->data(GameListItemPath::FullPathRole).toString().toLower(); + const QString file_title = item->data(GameListItemPath::TitleRole).toString().toLower(); + const QString file_program_id = QStringLiteral("%1").arg(program_id, 16, 16, QLatin1Char{'0'}); + + const QString file_name = + file_path.mid(file_path.lastIndexOf(QLatin1Char{'/'}) + 1) + QLatin1Char{' '} + file_title; + + return Yuzu::ContainsAllWords(file_name, filter) || + (file_program_id.size() == 16 && file_program_id.contains(filter)); +} + +} // namespace Yuzu diff --git a/src/yuzu/game/game_card.cpp b/src/yuzu/game/game_card.cpp index 05167fe455..12ad482c88 100644 --- a/src/yuzu/game/game_card.cpp +++ b/src/yuzu/game/game_card.cpp @@ -18,33 +18,16 @@ void GameCard::paint(QPainter* painter, const QStyleOptionViewItem& option, painter->save(); painter->setRenderHint(QPainter::Antialiasing); - // Padding, dimensions, alignment... - const int column = index.row() % m_columns; - const int cell_width = option.rect.width(); - const int fixed_card_width = cell_width - m_padding; - const int margins = 8; + constexpr int cardCornerRadius = 10; - // The gist of it is that this anchors the left and right sides to the edges, - // while maintaining an even gap between each card. - // I just smashed random keys into my keyboard until something worked. - // Don't even bother trying to figure out what the hell this is doing. - const auto total_row_width = m_columns * cell_width; - const auto total_gap_space = total_row_width - (margins * 2) - (m_columns * fixed_card_width); - const auto gap = (m_columns > 1) ? (total_gap_space / (m_columns - 1)) : 0; + const QRect cardRect = getCardRect(option, index); - const auto relative_x = margins + (column * (fixed_card_width + gap)); - const auto x_pos = option.rect.left() - (column * cell_width) + static_cast(relative_x); - - // also, add some additional padding here to prevent card overlap - QRect cardRect(x_pos + 4, option.rect.top() + 4, fixed_card_width - 8, option.rect.height() - margins); - - // colors QPalette palette = option.palette; QColor backgroundColor = palette.window().color(); QColor borderColor = palette.dark().color(); QColor textColor = palette.text().color(); - // if it's selected add a blue background + // highlight on select or hover if (option.state & QStyle::State_Selected) { backgroundColor = palette.highlight().color(); borderColor = palette.highlight().color().lighter(150); @@ -53,63 +36,45 @@ void GameCard::paint(QPainter* painter, const QStyleOptionViewItem& option, backgroundColor = backgroundColor.lighter(120); } - // bg painter->setBrush(backgroundColor); painter->setPen(QPen(borderColor, 1)); - painter->drawRoundedRect(cardRect, 10, 10); + painter->drawRoundedRect(cardRect, cardCornerRadius, cardCornerRadius); - // icon - int _iconsize = UISettings::values.game_icon_size.GetValue(); - QSize iconSize(_iconsize, _iconsize); - QPixmap iconPixmap = index.data(Qt::DecorationRole).value(); + const u32 icon_size = UISettings::values.game_icon_size.GetValue(); + QPixmap icon_pixmap = index.data(Qt::DecorationRole).value(); QRect iconRect; - if (!iconPixmap.isNull()) { - QSize scaledSize = iconPixmap.size(); - scaledSize.scale(iconSize, Qt::KeepAspectRatio); + if (!icon_pixmap.isNull()) { + QSize scaled = icon_pixmap.size(); + scaled.scale(icon_size, icon_size, Qt::KeepAspectRatio); - int x = cardRect.left() + (cardRect.width() - scaledSize.width()) / 2; - int y = cardRect.top() + margins; - - iconRect = QRect(x, y, scaledSize.width(), scaledSize.height()); + iconRect = {cardRect.left() + (cardRect.width() - scaled.width()) / 2, + cardRect.top() + cardMargin - 1, scaled.width(), scaled.height()}; painter->setRenderHint(QPainter::SmoothPixmapTransform, true); - // Put this in a separate thing on the painter stack to prevent clipping the text. painter->save(); - - // round image edges - QPainterPath path; - path.addRoundedRect(iconRect, 10, 10); - painter->setClipPath(path); - - painter->drawPixmap(iconRect, iconPixmap); - + QPainterPath clip_path; + clip_path.addRoundedRect(iconRect, cardCornerRadius, cardCornerRadius); + painter->setClipPath(clip_path); + painter->drawPixmap(iconRect, icon_pixmap); painter->restore(); } else { - // if there is no icon just draw a blank rect - iconRect = QRect(cardRect.left() + margins, cardRect.top() + margins, _iconsize, _iconsize); + iconRect = {cardRect.left() + cardMargin, cardRect.top() + cardMargin, + static_cast(icon_size), static_cast(icon_size)}; } if (UISettings::values.show_game_name.GetValue()) { - // padding + text QRect textRect = cardRect; - textRect.setTop(iconRect.bottom() + margins); - textRect.adjust(margins, 0, -margins, -margins); + textRect.setTop(iconRect.bottom() + cardMargin); + textRect.adjust(cardMargin, 0, -cardMargin, -cardMargin); - // We are already crammed on space, ignore the row 2 - QString title = index.data(Qt::DisplayRole).toString(); - title = title.split(QLatin1Char('\n')).first(); + QString title = index.data(Qt::DisplayRole).toString().split(QLatin1Char('\n')).first(); - // now draw text painter->setPen(textColor); QFont font = option.font; font.setBold(true); - - // TODO(crueter): fix this abysmal scaling - font.setPixelSize(1.5 + std::max(10.0, std::sqrt(_iconsize))); - - // TODO(crueter): elide mode + font.setPixelSize(std::max(11.0, std::sqrt(static_cast(icon_size)))); painter->setFont(font); painter->drawText(textRect, Qt::AlignHCenter | Qt::AlignTop | Qt::TextWordWrap, title); @@ -118,12 +83,62 @@ void GameCard::paint(QPainter* painter, const QStyleOptionViewItem& option, painter->restore(); } +QRect GameCard::getCardRect(const QStyleOptionViewItem& option, const QModelIndex& index) const { + const int cell_width = option.rect.width(); + + const int card_width = cell_width - m_padding; + + int card_left, card_top, card_height; + + if (m_columns >= 1) { + // grid mode + + // center everything in-line, such that the leftmost and rightmost cards + // have ~ equal padding to the edge of the viewport + // spacing between each card is larger, but equal to each other + const int column = index.row() % m_columns; + + const int row_width = m_columns * cell_width; + const int total_gap = row_width - cardMargin * 2 - m_columns * card_width; + const int gap = (m_columns > 1) ? (total_gap / (m_columns - 1)) : 0; + card_left = + option.rect.left() - column * cell_width + cardMargin + column * (card_width + gap) + 4; + + // fill cell vertically + card_top = option.rect.top() + cardMargin; + card_height = option.rect.height() - cardMargin - 1; + } else { + // carousel mode + card_left = option.rect.left() + cardMargin + 4; + + // the delegate itself takes up the full height, but the card itself + // gets centered + const int content_height = m_contentSize.height() - cardMargin; + const int cell_height = option.rect.height(); + + card_height = std::min(content_height, cell_height - cardMargin * 2) - 1; + card_top = option.rect.top() + (cell_height - card_height) / 2; + } + + return QRect(card_left, card_top, card_width - cardMargin, card_height); +} + +bool GameCard::hitTest(const QPoint& point, const QModelIndex& index, const QWidget* widget, + const QRect& cellRect) const { + QStyleOptionViewItem option; + option.initFrom(widget); + option.rect = cellRect; + return getCardRect(option, index).contains(point); +} + QSize GameCard::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const { return m_size; } -void GameCard::setSize(const QSize& newSize, const int padding, const int columns) { +void GameCard::setSize(const QSize& newSize, const QSize& contentSize, const int padding, + const int columns) { m_size = newSize; + m_contentSize = contentSize; m_padding = padding; m_columns = columns; } diff --git a/src/yuzu/game/game_card.h b/src/yuzu/game/game_card.h index f2a3edfddf..1d522c0ed8 100644 --- a/src/yuzu/game/game_card.h +++ b/src/yuzu/game/game_card.h @@ -17,11 +17,19 @@ public: void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; + QRect getCardRect(const QStyleOptionViewItem &option, const QModelIndex &index) const; + + bool hitTest(const QPoint& point, const QModelIndex& index, + const QWidget* widget, const QRect& cellRect) const; + QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override; - void setSize(const QSize& newSize, const int padding, const int columns); + void setSize(const QSize& newSize, const QSize& contentSize, const int padding, const int columns); private: + static constexpr int cardMargin = 8; + QSize m_size; + QSize m_contentSize; int m_padding; int m_columns; }; diff --git a/src/yuzu/game/game_grid.cpp b/src/yuzu/game/game_grid.cpp new file mode 100644 index 0000000000..c5f77cf897 --- /dev/null +++ b/src/yuzu/game/game_grid.cpp @@ -0,0 +1,111 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include +#include + +#include "qt_common/config/uisettings.h" +#include "qt_common/game_list/model.h" +#include "yuzu/game/common.h" +#include "yuzu/game/game_card.h" +#include "yuzu/game/game_grid.h" + +GameGrid::GameGrid(QWidget* parent) : QListView{parent} { + m_gameCard = new GameCard(this); + setItemDelegate(m_gameCard); + + setViewMode(QListView::ListMode); + setResizeMode(QListView::Fixed); + setUniformItemSizes(true); + setSelectionMode(QAbstractItemView::SingleSelection); + setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); + setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); + + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + setEditTriggers(QAbstractItemView::NoEditTriggers); + setContextMenuPolicy(Qt::CustomContextMenu); + + setSpacing(10); + setWordWrap(true); + setTextElideMode(Qt::ElideRight); + setFlow(QListView::LeftToRight); + setWrapping(true); +} + +void GameGrid::SetModel(GameListModel* model) { + QListView::setModel(model); + UpdateIconSize(); +} + +void GameGrid::ApplyFilter(const QString& edit_filter_text, GameListModel* model) { + int row_count = model->rowCount(); + + for (int i = 0; i < row_count; ++i) { + QStandardItem* item = model->item(i, 0); + if (!item) + continue; + + if (Yuzu::FilterMatches(edit_filter_text, item)) { + setRowHidden(i, false); + } else { + setRowHidden(i, true); + } + } +} + +void GameGrid::UpdateIconSize() { + const u32 icon_size = UISettings::values.game_icon_size.GetValue(); + + int heightMargin = 0; + int widthMargin = 80; + + if (UISettings::values.show_game_name) { + switch (icon_size) { + case 128: + heightMargin = 65; + break; + case 0: + widthMargin = 120; + heightMargin = 120; + break; + case 64: + heightMargin = 77; + break; + case 32: + case 256: + heightMargin = 81; + break; + } + } else { + widthMargin = 24; + heightMargin = 24; + } + + const int view_width = viewport()->width(); + + const double spacing = 0.01; + const int min_item_width = icon_size + widthMargin; + + int columns = std::max(1, (view_width - 16) / min_item_width); + int stretched_width = ((view_width) - (spacing * (columns - 1))) / columns; + + QSize grid_size(stretched_width, icon_size + heightMargin); + if (gridSize() != grid_size) { + setUpdatesEnabled(false); + + setGridSize(grid_size); + m_gameCard->setSize(grid_size, grid_size, stretched_width - min_item_width, columns); + + setUpdatesEnabled(true); + } +} + +QModelIndex GameGrid::indexAt(const QPoint& point) const { + QModelIndex index = QListView::indexAt(point); + if (!index.isValid()) + return {}; + if (m_gameCard && !m_gameCard->hitTest(point, index, this, visualRect(index))) + return {}; + return index; +} diff --git a/src/yuzu/game/game_grid.h b/src/yuzu/game/game_grid.h new file mode 100644 index 0000000000..3b5d6da1d4 --- /dev/null +++ b/src/yuzu/game/game_grid.h @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include + +class GameCard; +class GameListModel; + +class GameGrid : public QListView { + Q_OBJECT + +public: + explicit GameGrid(QWidget* parent = nullptr); + + void SetModel(GameListModel* model); + void ApplyFilter(const QString& edit_filter_text, GameListModel* model); + void UpdateIconSize(); + + QModelIndex indexAt(const QPoint& point) const override; + +private: + GameCard* m_gameCard = nullptr; +}; diff --git a/src/yuzu/game/game_list.cpp b/src/yuzu/game/game_list.cpp index 87b5f87474..86fd3c1b4f 100644 --- a/src/yuzu/game/game_list.cpp +++ b/src/yuzu/game/game_list.cpp @@ -1,6 +1,9 @@ // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-FileCopyrightText: 2015 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + #include #include #include @@ -15,431 +18,62 @@ #include #include #include -#include #include #include -#include -#include +#include + #include "common/common_types.h" -#include "common/logging.h" -#include "common/settings.h" #include "core/core.h" #include "core/file_sys/patch_manager.h" #include "core/file_sys/registered_cache.h" -#include "game/game_card.h" #include "qt_common/config/uisettings.h" +#include "qt_common/game_list/game_list_p.h" +#include "qt_common/game_list/model.h" #include "qt_common/qt_common.h" #include "qt_common/util/game.h" #include "yuzu/compatibility_list.h" +#include "yuzu/game/carousel.h" +#include "yuzu/game/game_grid.h" #include "yuzu/game/game_list.h" -#include "yuzu/game/game_list_p.h" -#include "yuzu/game/game_list_worker.h" +#include "yuzu/game/game_tree.h" +#include "yuzu/game/search_field.h" #include "yuzu/main_window.h" #include "yuzu/util/controller_navigation.h" -GameListSearchField::KeyReleaseEater::KeyReleaseEater(GameList* gamelist_, QObject* parent) - : QObject(parent), gamelist{gamelist_} {} - -// EventFilter in order to process systemkeys while editing the searchfield -bool GameListSearchField::KeyReleaseEater::eventFilter(QObject* obj, QEvent* event) { - // If it isn't a KeyRelease event then continue with standard event processing - if (event->type() != QEvent::KeyRelease) - return QObject::eventFilter(obj, event); - - QKeyEvent* keyEvent = static_cast(event); - QString edit_filter_text = gamelist->search_field->edit_filter->text().toLower(); - - // If the searchfield's text hasn't changed special function keys get checked - // If no function key changes the searchfield's text the filter doesn't need to get reloaded - if (edit_filter_text == edit_filter_text_old) { - switch (keyEvent->key()) { - // Escape: Resets the searchfield - case Qt::Key_Escape: { - if (edit_filter_text_old.isEmpty()) { - return QObject::eventFilter(obj, event); - } else { - gamelist->search_field->edit_filter->clear(); - edit_filter_text.clear(); - } - break; - } - // Return and Enter - // If the enter key gets pressed first checks how many and which entry is visible - // If there is only one result launch this game - case Qt::Key_Return: - case Qt::Key_Enter: { - if (gamelist->search_field->visible == 1) { - const QString file_path = gamelist->GetLastFilterResultItem(); - - // To avoid loading error dialog loops while confirming them using enter - // Also users usually want to run a different game after closing one - gamelist->search_field->edit_filter->clear(); - edit_filter_text.clear(); - emit gamelist->GameChosen(file_path); - } else { - return QObject::eventFilter(obj, event); - } - break; - } - default: - return QObject::eventFilter(obj, event); - } - } - edit_filter_text_old = edit_filter_text; - return QObject::eventFilter(obj, event); -} - -void GameListSearchField::setFilterResult(int visible_, int total_) { - visible = visible_; - total = total_; - - label_filter_result->setText(tr("%1 of %n result(s)", "", total).arg(visible)); -} - -QString GameListSearchField::filterText() const { - return edit_filter->text(); -} - -QString GameList::GetLastFilterResultItem() const { - QString file_path; - - for (int i = 1; i < item_model->rowCount() - 1; ++i) { - const QStandardItem* folder = item_model->item(i, 0); - const QModelIndex folder_index = folder->index(); - const int children_count = folder->rowCount(); - - for (int j = 0; j < children_count; ++j) { - if (tree_view->isRowHidden(j, folder_index)) { - continue; - } - - const QStandardItem* child = folder->child(j, 0); - file_path = child->data(GameListItemPath::FullPathRole).toString(); - } - } - - return file_path; -} - -void GameListSearchField::clear() { - edit_filter->clear(); -} - -void GameListSearchField::setFocus() { - if (edit_filter->isVisible()) { - edit_filter->setFocus(); - } -} - -GameListSearchField::GameListSearchField(GameList* parent) : QWidget{parent} { - auto* const key_release_eater = new KeyReleaseEater(parent, this); - layout_filter = new QHBoxLayout; - layout_filter->setContentsMargins(8, 8, 8, 8); - label_filter = new QLabel; - edit_filter = new QLineEdit; - edit_filter->clear(); - edit_filter->installEventFilter(key_release_eater); - edit_filter->setClearButtonEnabled(true); - connect(edit_filter, &QLineEdit::textChanged, parent, &GameList::OnTextChanged); - label_filter_result = new QLabel; - button_filter_close = new QToolButton(this); - button_filter_close->setText(QStringLiteral("X")); - button_filter_close->setCursor(Qt::ArrowCursor); - button_filter_close->setStyleSheet( - QStringLiteral("QToolButton{ border: none; padding: 0px; color: " - "#000000; font-weight: bold; background: #F0F0F0; }" - "QToolButton:hover{ border: none; padding: 0px; color: " - "#EEEEEE; font-weight: bold; background: #E81123}")); - connect(button_filter_close, &QToolButton::clicked, parent, &GameList::OnFilterCloseClicked); - layout_filter->setSpacing(10); - layout_filter->addWidget(label_filter); - layout_filter->addWidget(edit_filter); - layout_filter->addWidget(label_filter_result); - layout_filter->addWidget(button_filter_close); - setLayout(layout_filter); - RetranslateUI(); -} - -/** - * Checks if all words separated by spaces are contained in another string - * This offers a word order insensitive search function - * - * @param haystack String that gets checked if it contains all words of the userinput string - * @param userinput String containing all words getting checked - * @return true if the haystack contains all words of userinput - */ -static bool ContainsAllWords(const QString& haystack, const QString& userinput) { - const QStringList userinput_split = userinput.split(QLatin1Char{' '}, Qt::SkipEmptyParts); - - return std::all_of(userinput_split.begin(), userinput_split.end(), - [&haystack](const QString& s) { return haystack.contains(s); }); -} - -// Syncs the expanded state of Game Directories with settings to persist across sessions -void GameList::OnItemExpanded(const QModelIndex& item) { - const auto type = item.data(GameListItem::TypeRole).value(); - const bool is_dir = type == GameListItemType::CustomDir || type == GameListItemType::SdmcDir || - type == GameListItemType::UserNandDir || - type == GameListItemType::SysNandDir; - const bool is_fave = type == GameListItemType::Favorites; - if (!is_dir && !is_fave) { - return; - } - const bool is_expanded = tree_view->isExpanded(item); - if (is_fave) { - UISettings::values.favorites_expanded = is_expanded; - return; - } - const int item_dir_index = item.data(GameListDir::GameDirRole).toInt(); - UISettings::values.game_dirs[item_dir_index].expanded = is_expanded; -} - -// Event in order to filter the gamelist after editing the searchfield -void GameList::OnTextChanged(const QString& new_text) { - QString edit_filter_text = new_text.toLower(); - QStandardItem* folder; - int children_total = 0; - int result_count = 0; - - auto hide = [this](int row, bool hidden, QModelIndex index = QModelIndex()) { - if (m_isTreeMode) { - tree_view->setRowHidden(row, index, hidden); - } else { - list_view->setRowHidden(row, hidden); - } - }; - - // If the searchfield is empty every item is visible - // Otherwise the filter gets applied - - // TODO(crueter) dedupe - if (!m_isTreeMode) { - int row_count = item_model->rowCount(); - - for (int i = 0; i < row_count; ++i) { - QStandardItem* item = item_model->item(i, 0); - if (!item) - continue; - - children_total++; - - const QString file_path = - item->data(GameListItemPath::FullPathRole).toString().toLower(); - const QString file_title = item->data(GameListItemPath::TitleRole).toString().toLower(); - const QString file_name = file_path.mid(file_path.lastIndexOf(QLatin1Char{'/'}) + 1) + - QLatin1Char{' '} + file_title; - - if (edit_filter_text.isEmpty() || ContainsAllWords(file_name, edit_filter_text)) { - hide(i, false); - result_count++; - } else { - hide(i, true); - } - } - search_field->setFilterResult(result_count, children_total); - } else if (edit_filter_text.isEmpty()) { - hide(0, UISettings::values.favorited_ids.size() == 0, - item_model->invisibleRootItem()->index()); - for (int i = 1; i < item_model->rowCount() - 1; ++i) { - folder = item_model->item(i, 0); - const QModelIndex folder_index = folder->index(); - const int children_count = folder->rowCount(); - for (int j = 0; j < children_count; ++j) { - ++children_total; - hide(j, false, folder_index); - } - } - search_field->setFilterResult(children_total, children_total); - } else { - hide(0, true, item_model->invisibleRootItem()->index()); - for (int i = 1; i < item_model->rowCount() - 1; ++i) { - folder = item_model->item(i, 0); - const QModelIndex folder_index = folder->index(); - const int children_count = folder->rowCount(); - for (int j = 0; j < children_count; ++j) { - ++children_total; - - const QStandardItem* child = folder->child(j, 0); - - const auto program_id = child->data(GameListItemPath::ProgramIdRole).toULongLong(); - - const QString file_path = - child->data(GameListItemPath::FullPathRole).toString().toLower(); - const QString file_title = - child->data(GameListItemPath::TitleRole).toString().toLower(); - const QString file_program_id = - QStringLiteral("%1").arg(program_id, 16, 16, QLatin1Char{'0'}); - - // Only items which filename in combination with its title contains all words - // that are in the searchfield will be visible in the gamelist - // The search is case insensitive because of toLower() - // I decided not to use Qt::CaseInsensitive in containsAllWords to prevent - // multiple conversions of edit_filter_text for each game in the gamelist - const QString file_name = - file_path.mid(file_path.lastIndexOf(QLatin1Char{'/'}) + 1) + QLatin1Char{' '} + - file_title; - if (ContainsAllWords(file_name, edit_filter_text) || - (file_program_id.size() == 16 && file_program_id.contains(edit_filter_text))) { - hide(j, false, folder_index); - ++result_count; - } else { - hide(j, true, folder_index); - } - } - } - search_field->setFilterResult(result_count, children_total); - } -} - -void GameList::OnUpdateThemedIcons() { - for (int i = 0; i < item_model->invisibleRootItem()->rowCount(); i++) { - QStandardItem* child = item_model->invisibleRootItem()->child(i); - - const int icon_size = UISettings::values.folder_icon_size.GetValue(); - - switch (child->data(GameListItem::TypeRole).value()) { - case GameListItemType::SdmcDir: - child->setData( - QIcon::fromTheme(QStringLiteral("sd_card")) - .pixmap(icon_size) - .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), - Qt::DecorationRole); - break; - case GameListItemType::UserNandDir: - child->setData( - QIcon::fromTheme(QStringLiteral("chip")) - .pixmap(icon_size) - .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), - Qt::DecorationRole); - break; - case GameListItemType::SysNandDir: - child->setData( - QIcon::fromTheme(QStringLiteral("chip")) - .pixmap(icon_size) - .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), - Qt::DecorationRole); - break; - case GameListItemType::CustomDir: { - const UISettings::GameDir& game_dir = - UISettings::values.game_dirs[child->data(GameListDir::GameDirRole).toInt()]; - const QString icon_name = QFileInfo::exists(QString::fromStdString(game_dir.path)) - ? QStringLiteral("folder") - : QStringLiteral("bad_folder"); - child->setData( - QIcon::fromTheme(icon_name).pixmap(icon_size).scaled( - icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), - Qt::DecorationRole); - break; - } - case GameListItemType::AddDir: - child->setData( - QIcon::fromTheme(QStringLiteral("list-add")) - .pixmap(icon_size) - .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), - Qt::DecorationRole); - break; - case GameListItemType::Favorites: - child->setData( - QIcon::fromTheme(QStringLiteral("star")) - .pixmap(icon_size) - .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), - Qt::DecorationRole); - break; - default: - break; - } - } -} - -void GameList::OnFilterCloseClicked() { - main_window->filterBarSetChecked(false); -} - GameList::GameList(FileSys::VirtualFilesystem vfs_, FileSys::ManualContentProvider* provider_, PlayTime::PlayTimeManager& play_time_manager_, Core::System& system_, MainWindow* parent) : QWidget{parent}, vfs{std::move(vfs_)}, provider{provider_}, play_time_manager{play_time_manager_}, system{system_} { - watcher = new QFileSystemWatcher(this); - connect(watcher, &QFileSystemWatcher::directoryChanged, this, &GameList::RefreshGameDirectory); - - external_watcher = new QFileSystemWatcher(this); - ResetExternalWatcher(); - connect(external_watcher, &QFileSystemWatcher::directoryChanged, this, - &GameList::RefreshExternalContent); this->main_window = parent; layout = new QVBoxLayout; - tree_view = new QTreeView(this); - list_view = new QListView(this); - m_gameCard = new GameCard(this); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + setLayout(layout); - list_view->setItemDelegate(m_gameCard); + item_model = new GameListModel(vfs, provider, play_time_manager, system, this); + + SetupViews(); + + search_field = new GameListSearchField(this); + layout->addWidget(search_field); controller_navigation = new ControllerNavigation(system.HIDCore(), this); - search_field = new GameListSearchField(this); - item_model = new QStandardItemModel(tree_view); - tree_view->setModel(item_model); - list_view->setModel(item_model); SetupScrollAnimation(); - // tree - tree_view->setAlternatingRowColors(true); - tree_view->setSelectionMode(QHeaderView::SingleSelection); - tree_view->setSelectionBehavior(QHeaderView::SelectRows); - tree_view->setVerticalScrollMode(QHeaderView::ScrollPerPixel); - tree_view->setHorizontalScrollMode(QHeaderView::ScrollPerPixel); - tree_view->setSortingEnabled(true); - tree_view->setEditTriggers(QHeaderView::NoEditTriggers); - tree_view->setContextMenuPolicy(Qt::CustomContextMenu); - tree_view->setAttribute(Qt::WA_AcceptTouchEvents, true); - tree_view->setStyleSheet(QStringLiteral("QTreeView{ border: none; }")); - - // list view setup - list_view->setViewMode(QListView::ListMode); - list_view->setResizeMode(QListView::Fixed); - list_view->setUniformItemSizes(true); - list_view->setSelectionMode(QAbstractItemView::SingleSelection); - list_view->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); - list_view->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); - - // Forcefully disable scroll bar, prevents thing where game list items - // will start clamping prematurely. - list_view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - - list_view->setEditTriggers(QAbstractItemView::NoEditTriggers); - list_view->setContextMenuPolicy(Qt::CustomContextMenu); - list_view->setGridSize(QSize(140, 160)); - m_gameCard->setSize(list_view->gridSize(), 0, 4); - - list_view->setSpacing(10); - list_view->setWordWrap(true); - list_view->setTextElideMode(Qt::ElideRight); - list_view->setFlow(QListView::LeftToRight); - list_view->setWrapping(true); - - item_model->insertColumns(0, COLUMN_COUNT); - RetranslateUI(); - - tree_view->setColumnHidden(COLUMN_ADD_ONS, !UISettings::values.show_add_ons); - tree_view->setColumnHidden(COLUMN_COMPATIBILITY, !UISettings::values.show_compat); - tree_view->setColumnHidden(COLUMN_PLAY_TIME, !UISettings::values.show_play_time); - item_model->setSortRole(GameListItemPath::SortRole); - - connect(main_window, &MainWindow::UpdateThemedIcons, this, &GameList::OnUpdateThemedIcons); - connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry); connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu); - connect(list_view, &QListView::activated, this, &GameList::ValidateEntry); - connect(list_view, &QListView::customContextMenuRequested, this, &GameList::PopupContextMenu); + connect(grid_view, &QListView::activated, this, &GameList::ValidateEntry); + connect(grid_view, &QListView::customContextMenuRequested, this, &GameList::PopupContextMenu); + + connect(carousel_view, &QListView::activated, this, &GameList::ValidateEntry); + connect(carousel_view, &QListView::customContextMenuRequested, this, &GameList::PopupContextMenu); - connect(tree_view, &QTreeView::expanded, this, &GameList::OnItemExpanded); - connect(tree_view, &QTreeView::collapsed, this, &GameList::OnItemExpanded); connect(controller_navigation, &ControllerNavigation::TriggerKeyboardEvent, this, [this](Qt::Key key) { - // Avoid pressing buttons while playing if (system.IsPoweredOn()) { return; } @@ -447,64 +81,148 @@ GameList::GameList(FileSys::VirtualFilesystem vfs_, FileSys::ManualContentProvid return; } QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier); - QCoreApplication::postEvent(m_currentView, event); }); - // We must register all custom types with the Qt Automoc system so that we are able to use - // it with signals/slots. In this case, QList falls under the umbrella of custom types. + connect(item_model, &GameListModel::PopulatingCompleted, this, + &GameList::OnPopulatingCompleted); + + connect(item_model, &GameListModel::ShowList, this, &GameList::ShowList); + connect(item_model, &GameListModel::SaveConfig, this, &GameList::SaveConfig); + connect(item_model, &GameListModel::PopulatingStarted, this, &GameList::OnPopulate); + + // TODO: impl on grid/carousel + connect(tree_view, &GameTree::FilterResultReady, search_field, + [this](int visible, int total) { search_field->setFilterResult(visible, total); }); + qRegisterMetaType>("QList"); - layout->setContentsMargins(0, 0, 0, 0); - layout->setSpacing(0); - - layout->addWidget(tree_view); - layout->addWidget(list_view); - layout->addWidget(search_field); - setLayout(layout); - ResetViewMode(); } +GameList::~GameList() { + UnloadController(); +} + +void GameList::SetupViews() { + tree_view = new GameTree(this); + grid_view = new GameGrid(this); + carousel_view = new GameCarousel(this); + + tree_view->SetModel(item_model); + grid_view->SetModel(item_model); + carousel_view->SetModel(item_model); +} + +QString GameList::GetLastFilterResultItem() const { + return tree_view->GetLastFilterResultItem(); +} + +void GameList::ClearFilter() { + search_field->clear(); +} + +void GameList::SetFilterFocus() { + if (item_model->rowCount() > 0) { + search_field->setFocus(); + } +} + +void GameList::SetFilterVisible(bool visibility) { + search_field->setVisible(visibility); +} + +bool GameList::IsEmpty() const { + return item_model->IsEmpty(); +} + +void GameList::LoadCompatibilityList() { + item_model->LoadCompatibilityList(); +} + +void GameList::OnPopulate() { + m_currentView->setEnabled(false); + + switch (game_list_mode) { + case Settings::GameListMode::TreeView: + tree_view->UpdateColumnVisibility(item_model); + break; + case Settings::GameListMode::GridView: + grid_view->UpdateIconSize(); + break; + case Settings::GameListMode::CarouselView: + carousel_view->UpdateIconSize(); + break; + } +} + +void GameList::PopulateAsync(QVector& game_dirs) { + item_model->PopulateAsync(game_dirs); +} + +void GameList::SaveInterfaceLayout() { + tree_view->SaveInterfaceLayout(); +} + +void GameList::LoadInterfaceLayout() { + tree_view->LoadInterfaceLayout(); +} + +QStandardItemModel* GameList::GetModel() const { + return item_model; +} + void GameList::UnloadController() { controller_navigation->UnloadController(); } -bool GameList::IsTreeMode() { - return m_isTreeMode; -} - void GameList::ResetViewMode() { - auto& setting = UISettings::values.game_list_mode; + const auto mode = UISettings::values.game_list_mode.GetValue(); + game_list_mode = mode; + + if (m_currentView) + layout->removeWidget(m_currentView); + bool newTreeMode = false; - switch (setting.GetValue()) { + switch (mode) { case Settings::GameListMode::TreeView: m_currentView = tree_view; newTreeMode = true; - tree_view->setVisible(true); - list_view->setVisible(false); break; case Settings::GameListMode::GridView: - m_currentView = list_view; + m_currentView = grid_view; + newTreeMode = false; + + break; + case Settings::GameListMode::CarouselView: + m_currentView = carousel_view; newTreeMode = false; - list_view->setVisible(true); - tree_view->setVisible(false); break; default: - break; + UNREACHABLE(); } + tree_view->setVisible(false); + grid_view->setVisible(false); + carousel_view->setVisible(false); + + tree_view->setEnabled(false); + grid_view->setEnabled(false); + carousel_view->setEnabled(false); + + m_currentView->setVisible(true); + m_currentView->setEnabled(true); + layout->insertWidget(0, m_currentView); + auto view = m_currentView->viewport(); view->installEventFilter(this); - // touch gestures view->grabGesture(Qt::SwipeGesture); view->grabGesture(Qt::PanGesture); - // TODO: touch? QScroller::grabGesture(view, QScroller::LeftMouseButtonGesture); auto scroller = QScroller::scroller(view); @@ -517,50 +235,133 @@ void GameList::ResetViewMode() { if (m_isTreeMode != newTreeMode) { m_isTreeMode = newTreeMode; - - RefreshGameDirectory(); + item_model->SetFlat(!m_isTreeMode); + if (!UISettings::values.game_dirs.empty()) { + item_model->PopulateAsync(UISettings::values.game_dirs); + } } } -GameList::~GameList() { - UnloadController(); +void GameList::OnTextChanged(const QString& new_text) { + const QString edit_filter_text = new_text.toLower(); + + if (m_isTreeMode) { + tree_view->ApplyFilter(edit_filter_text, item_model); + } else { + grid_view->ApplyFilter(edit_filter_text, item_model); + } } -void GameList::SetFilterFocus() { - if (tree_view->model()->rowCount() > 0) { +void GameList::OnFilterCloseClicked() { + main_window->filterBarSetChecked(false); +} + +void GameList::OnPopulatingCompleted(const QStringList& watch_list) { + emit ShowList(!item_model->IsEmpty()); + + // favorites row + if (m_isTreeMode) { + tree_view->setRowHidden(0, item_model->invisibleRootItem()->index(), + UISettings::values.favorited_ids.size() == 0); + tree_view->setExpanded(item_model->invisibleRootItem()->child(0)->index(), + UISettings::values.favorites_expanded.GetValue()); + } + + // restore collapsed/expanded flags + if (m_isTreeMode) { + const auto* root = item_model->invisibleRootItem(); + for (int i = 1; i < root->rowCount() - 1; ++i) { + const auto* dir_item = root->child(i); + const auto type = dir_item->data(GameListItem::TypeRole).value(); + if (type == GameListItemType::CustomDir || type == GameListItemType::SdmcDir || + type == GameListItemType::UserNandDir || type == GameListItemType::SysNandDir) { + const int dir_index = dir_item->data(GameListDir::GameDirRole).toInt(); + if (dir_index >= 0 && dir_index < UISettings::values.game_dirs.size()) { + tree_view->setExpanded(dir_item->index(), + UISettings::values.game_dirs[dir_index].expanded); + } + } + } + } + + // Watcher updates + auto* watcher = item_model->GetWatcher(); + auto current_watch_list = watcher->directories(); + + constexpr qsizetype LIMIT_WATCH_DIRECTORIES = 5000; + constexpr int SLICE_SIZE = 25; + + QStringList to_remove, to_add; + + const auto slice = [&](const QStringList& list, std::function callback) { + const int len = (std::min)(list.size(), LIMIT_WATCH_DIRECTORIES); + for (int i = 0; i < len; i += SLICE_SIZE) { + auto chunk = list.mid(i, SLICE_SIZE); + if (!chunk.isEmpty()) { + callback(chunk); + } + QCoreApplication::processEvents(); + } + }; + + // remove any paths not in the new watch list + for (const auto& path : std::as_const(current_watch_list)) { + if (!watch_list.contains(path)) { + to_remove.emplaceBack(path); + } + } + + slice(to_remove, [watcher](const QStringList& chunk) { watcher->removePaths(chunk); }); + + // add any paths not in the old watch list + for (const auto& path : std::as_const(watch_list)) { + if (!current_watch_list.contains(path)) { + to_add.emplaceBack(path); + } + } + + slice(to_add, [watcher](const QStringList& chunk) { watcher->addPaths(chunk); }); + + m_currentView->setEnabled(true); + + int children_total = 0; + for (int i = 1; i < item_model->rowCount() - 1; ++i) { + children_total += item_model->item(i, 0)->rowCount(); + } + + search_field->setFilterResult(children_total, children_total); + if (children_total > 0) { search_field->setFocus(); } + + // TODO: carousel/grid impl. + item_model->sort(tree_view->header()->sortIndicatorSection(), + tree_view->header()->sortIndicatorOrder()); + + emit PopulatingCompleted(); } -void GameList::SetFilterVisible(bool visibility) { - search_field->setVisible(visibility); +void GameList::RefreshGameDirectory() { + item_model->RefreshGameDirectory(); } -void GameList::ClearFilter() { - search_field->clear(); +void GameList::RefreshExternalContent() { + item_model->RefreshExternalContent(); } -void GameList::WorkerEvent() { - current_worker->ProcessEvents(this); -} - -void GameList::AddDirEntry(GameListDir* entry_items) { - if (m_isTreeMode) { - item_model->invisibleRootItem()->appendRow(entry_items); - tree_view->setExpanded( - entry_items->index(), - UISettings::values.game_dirs[entry_items->data(GameListDir::GameDirRole).toInt()] - .expanded); +void GameList::UpdateIconSizes() { + switch (game_list_mode) { + case Settings::GameListMode::GridView: + grid_view->UpdateIconSize(); + break; + case Settings::GameListMode::CarouselView: + carousel_view->UpdateIconSize(); + break; + case Settings::GameListMode::TreeView: + break; } } -void GameList::AddEntry(const QList& entry_items, GameListDir* parent) { - if (!m_isTreeMode) - item_model->invisibleRootItem()->appendRow(entry_items); - else - parent->appendRow(entry_items); -} - void GameList::ValidateEntry(const QModelIndex& item) { const auto selected = item.sibling(item.row(), 0); @@ -584,7 +385,6 @@ void GameList::ValidateEntry(const QModelIndex& item) { const auto title_id = selected.data(GameListItemPath::ProgramIdRole).toULongLong(); - // Users usually want to run a different game after closing one search_field->clear(); emit GameChosen(file_path, title_id); break; @@ -597,82 +397,19 @@ void GameList::ValidateEntry(const QModelIndex& item) { } } -bool GameList::IsEmpty() const { - for (int i = 0; i < item_model->rowCount(); i++) { - const QStandardItem* child = item_model->invisibleRootItem()->child(i); - const auto type = static_cast(child->type()); +void GameList::ToggleFavorite(u64 program_id) { + item_model->ToggleFavorite(program_id); - if (!child->hasChildren() && - (type == GameListItemType::SdmcDir || type == GameListItemType::UserNandDir || - type == GameListItemType::SysNandDir)) { - item_model->invisibleRootItem()->removeRow(child->row()); - i--; - } - } - - return !item_model->invisibleRootItem()->hasChildren(); -} - -void GameList::DonePopulating(const QStringList& watch_list) { - emit ShowList(!IsEmpty()); - - // Add favorites row - if (m_isTreeMode) { - item_model->invisibleRootItem()->appendRow(new GameListAddDir()); - - item_model->invisibleRootItem()->insertRow(0, new GameListFavorites()); + if (UISettings::values.favorited_ids.contains(program_id)) { tree_view->setRowHidden(0, item_model->invisibleRootItem()->index(), - UISettings::values.favorited_ids.size() == 0); - tree_view->setExpanded(item_model->invisibleRootItem()->child(0)->index(), - UISettings::values.favorites_expanded.GetValue()); - for (const auto id : std::as_const(UISettings::values.favorited_ids)) { - AddFavorite(id); + !search_field->filterText().isEmpty()); + item_model->sort(tree_view->header()->sortIndicatorSection(), + tree_view->header()->sortIndicatorOrder()); + } else { + if (UISettings::values.favorited_ids.size() == 0) { + tree_view->setRowHidden(0, item_model->invisibleRootItem()->index(), true); } } - - // Clear out the old directories to watch for changes and add the new ones - auto watch_dirs = watcher->directories(); - if (!watch_dirs.isEmpty()) { - watcher->removePaths(watch_dirs); - } - // Workaround: Add the watch paths in chunks to allow the gui to refresh - // This prevents the UI from stalling when a large number of watch paths are added - // Also artificially caps the watcher to a certain number of directories - constexpr int LIMIT_WATCH_DIRECTORIES = 5000; - constexpr int SLICE_SIZE = 25; - int len = (std::min)(static_cast(watch_list.size()), LIMIT_WATCH_DIRECTORIES); - - // Block signals to prevent the watcher from triggering a refresh while we are adding paths. - // This fixes a refresh loop on macOS. -#ifdef __APPLE__ - const bool old_signals_blocked = watcher->blockSignals(true); -#endif - - for (int i = 0; i < len; i += SLICE_SIZE) { - auto chunk = watch_list.mid(i, SLICE_SIZE); - if (!chunk.isEmpty()) { - watcher->addPaths(chunk); - } - QCoreApplication::processEvents(); - } - -#ifdef __APPLE__ - watcher->blockSignals(old_signals_blocked); -#endif - m_currentView->setEnabled(true); - - int children_total = 0; - for (int i = 1; i < item_model->rowCount() - 1; ++i) { - children_total += item_model->item(i, 0)->rowCount(); - } - search_field->setFilterResult(children_total, children_total); - if (children_total > 0) { - search_field->setFocus(); - } - item_model->sort(tree_view->header()->sortIndicatorSection(), - tree_view->header()->sortIndicatorOrder()); - - emit PopulatingCompleted(); } void GameList::PopupContextMenu(const QPoint& menu_location) { @@ -747,7 +484,6 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri QAction* verify_integrity = context_menu.addAction(tr("Verify Integrity")); QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard")); QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry")); -// TODO: Implement shortcut creation for macOS #if !defined(__APPLE__) QMenu* shortcut_menu = context_menu.addMenu(tr("Create Shortcut")); QAction* create_desktop_shortcut = shortcut_menu->addAction(tr("Add to Desktop")); @@ -769,8 +505,9 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri remove_vk_shader_cache->setVisible(program_id != 0); remove_shader_cache->setVisible(program_id != 0); remove_all_content->setVisible(program_id != 0); - auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); - navigate_to_gamedb_entry->setVisible(it != compatibility_list.end() && program_id != 0); + auto& compat_list = item_model->GetCompatibilityList(); + auto it = FindMatchingCompatibilityEntry(compat_list, program_id); + navigate_to_gamedb_entry->setVisible(it != compat_list.end() && program_id != 0); connect(favorite, &QAction::triggered, this, [this, program_id]() { ToggleFavorite(program_id); }); @@ -831,9 +568,8 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri connect(copy_tid, &QAction::triggered, this, [this, program_id]() { emit CopyTIDRequested(program_id); }); connect(navigate_to_gamedb_entry, &QAction::triggered, this, [this, program_id]() { - emit NavigateToGamedbEntryRequested(program_id, compatibility_list); + emit NavigateToGamedbEntryRequested(program_id, item_model->GetCompatibilityList()); }); -// TODO: Implement shortcut creation for macOS #if !defined(__APPLE__) connect(create_desktop_shortcut, &QAction::triggered, this, [this, program_id, path]() { emit CreateShortcut(program_id, path, QtCommon::Game::ShortcutTarget::Desktop); @@ -885,32 +621,26 @@ void GameList::AddPermDirPopup(QMenu& context_menu, QModelIndex selected) { connect(move_up, &QAction::triggered, this, [this, selected, row, game_dir_index] { const int other_index = selected.sibling(row - 1, 0).data(GameListDir::GameDirRole).toInt(); - // swap the items in the settings std::swap(UISettings::values.game_dirs[game_dir_index], UISettings::values.game_dirs[other_index]); - // swap the indexes held by the QVariants item_model->setData(selected, QVariant(other_index), GameListDir::GameDirRole); item_model->setData(selected.sibling(row - 1, 0), QVariant(game_dir_index), GameListDir::GameDirRole); - // move the treeview items - QList item = item_model->takeRow(row); - item_model->invisibleRootItem()->insertRow(row - 1, item); + QList items = item_model->takeRow(row); + item_model->invisibleRootItem()->insertRow(row - 1, items); tree_view->setExpanded(selected.sibling(row - 1, 0), UISettings::values.game_dirs[other_index].expanded); }); connect(move_down, &QAction::triggered, this, [this, selected, row, game_dir_index] { const int other_index = selected.sibling(row + 1, 0).data(GameListDir::GameDirRole).toInt(); - // swap the items in the settings std::swap(UISettings::values.game_dirs[game_dir_index], UISettings::values.game_dirs[other_index]); - // swap the indexes held by the QVariants item_model->setData(selected, QVariant(other_index), GameListDir::GameDirRole); item_model->setData(selected.sibling(row + 1, 0), QVariant(game_dir_index), GameListDir::GameDirRole); - // move the treeview items - const QList item = item_model->takeRow(row); - item_model->invisibleRootItem()->insertRow(row + 1, item); + const QList items = item_model->takeRow(row); + item_model->invisibleRootItem()->insertRow(row + 1, items); tree_view->setExpanded(selected.sibling(row + 1, 0), UISettings::values.game_dirs[other_index].expanded); }); @@ -925,58 +655,13 @@ void GameList::AddFavoritesPopup(QMenu& context_menu) { QAction* clear = context_menu.addAction(tr("Clear")); connect(clear, &QAction::triggered, this, [this] { - for (const auto id : std::as_const(UISettings::values.favorited_ids)) { - RemoveFavorite(id); - } UISettings::values.favorited_ids.clear(); + item_model->invisibleRootItem()->child(0)->removeRows( + 0, item_model->invisibleRootItem()->child(0)->rowCount()); tree_view->setRowHidden(0, item_model->invisibleRootItem()->index(), true); }); } -void GameList::LoadCompatibilityList() { - QFile compat_list{QStringLiteral(":compatibility_list/compatibility_list.json")}; - - if (!compat_list.open(QFile::ReadOnly | QFile::Text)) { - LOG_ERROR(Frontend, "Unable to open game compatibility list"); - return; - } - - if (compat_list.size() == 0) { - LOG_WARNING(Frontend, "Game compatibility list is empty"); - return; - } - - const QByteArray content = compat_list.readAll(); - if (content.isEmpty()) { - LOG_ERROR(Frontend, "Unable to completely read game compatibility list"); - return; - } - - const QJsonDocument json = QJsonDocument::fromJson(content); - const QJsonArray arr = json.array(); - - for (const QJsonValue& value : arr) { - const QJsonObject game = value.toObject(); - const QString compatibility_key = QStringLiteral("compatibility"); - - if (!game.contains(compatibility_key) || !game[compatibility_key].isDouble()) { - continue; - } - - const int compatibility = game[compatibility_key].toInt(); - const QString directory = game[QStringLiteral("directory")].toString(); - const QJsonArray ids = game[QStringLiteral("releases")].toArray(); - - for (const QJsonValue& id_ref : ids) { - const QJsonObject id_object = id_ref.toObject(); - const QString id = id_object[QStringLiteral("id")].toString(); - - compatibility_list.emplace(id.toUpper().toStdString(), - std::make_pair(QString::number(compatibility), directory)); - } - } -} - void GameList::changeEvent(QEvent* event) { if (event->type() == QEvent::LanguageChange) { RetranslateUI(); @@ -986,258 +671,11 @@ void GameList::changeEvent(QEvent* event) { } void GameList::RetranslateUI() { - item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, tr("Name")); - item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, tr("Compatibility")); - item_model->setHeaderData(COLUMN_ADD_ONS, Qt::Horizontal, tr("Add-ons")); - item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, tr("File type")); - item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, tr("Size")); - item_model->setHeaderData(COLUMN_PLAY_TIME, Qt::Horizontal, tr("Play time")); -} - -void GameListSearchField::changeEvent(QEvent* event) { - if (event->type() == QEvent::LanguageChange) { - RetranslateUI(); - } - - QWidget::changeEvent(event); -} - -void GameListSearchField::RetranslateUI() { - label_filter->setText(tr("Filter:")); - edit_filter->setPlaceholderText(tr("Enter pattern to filter")); -} - -QStandardItemModel* GameList::GetModel() const { - return item_model; -} - -void GameList::UpdateIconSize() { - // Update sizes and stuff for the list view - const u32 icon_size = UISettings::values.game_icon_size.GetValue(); - - int heightMargin = 0; - int widthMargin = 80; - - if (UISettings::values.show_game_name) { - // the scaling on the card is kinda abysmal. - // TODO(crueter): refactor - switch (icon_size) { - case 128: - heightMargin = 65; - break; - case 0: - widthMargin = 120; - heightMargin = 120; - break; - case 64: - heightMargin = 77; - break; - case 32: - case 256: - heightMargin = 81; - break; - } - } else { - widthMargin = 24; - heightMargin = 24; - } - - // "auto" resize // - const int view_width = list_view->viewport()->width(); - - // Tiny space padding to prevent the list view from forcing its own resize operation. - const double spacing = 0.01; - const int min_item_width = icon_size + widthMargin; - - // And now stretch it a bit to fill out remaining space. - // Not perfect but works well enough for now - int columns = std::max(1, (view_width - 16) / min_item_width); - int stretched_width = ((view_width) - (spacing * (columns - 1))) / columns; - - // only updates things if grid size is changed - QSize grid_size(stretched_width, icon_size + heightMargin); - if (list_view->gridSize() != grid_size) { - list_view->setUpdatesEnabled(false); - - list_view->setGridSize(grid_size); - m_gameCard->setSize(grid_size, stretched_width - min_item_width, columns); - - list_view->setUpdatesEnabled(true); - } -} - -void GameList::PopulateAsync(QVector& game_dirs) { - m_currentView->setEnabled(false); - - // Update the columns in case UISettings has changed - tree_view->setColumnHidden(COLUMN_ADD_ONS, !UISettings::values.show_add_ons); - tree_view->setColumnHidden(COLUMN_COMPATIBILITY, !UISettings::values.show_compat); - tree_view->setColumnHidden(COLUMN_FILE_TYPE, !UISettings::values.show_types); - tree_view->setColumnHidden(COLUMN_SIZE, !UISettings::values.show_size); - tree_view->setColumnHidden(COLUMN_PLAY_TIME, !UISettings::values.show_play_time); - - if (!m_isTreeMode) - UpdateIconSize(); - - // Cancel any existing worker. - current_worker.reset(); - - // Delete any rows that might already exist if we're repopulating - item_model->removeRows(0, item_model->rowCount()); - search_field->clear(); - - current_worker = std::make_unique(vfs, provider, game_dirs, compatibility_list, - play_time_manager, system); - - // Get events from the worker as data becomes available - connect(current_worker.get(), &GameListWorker::DataAvailable, this, &GameList::WorkerEvent, - Qt::QueuedConnection); - - QThreadPool::globalInstance()->start(current_worker.get()); -} - -void GameList::SaveInterfaceLayout() { - UISettings::values.gamelist_header_state = tree_view->header()->saveState(); -} - -void GameList::LoadInterfaceLayout() { - auto* header = tree_view->header(); - - if (header->restoreState(UISettings::values.gamelist_header_state)) - return; - - // We are using the name column to display icons and titles - // so make it as large as possible as default. - - // TODO(crueter): width() is not initialized yet, so use a sane default value - header->resizeSection(COLUMN_NAME, 840); -} - -const QStringList GameList::supported_file_extensions = { - QStringLiteral("nso"), QStringLiteral("nro"), QStringLiteral("nca"), - QStringLiteral("xci"), QStringLiteral("nsp"), QStringLiteral("kip")}; - -void GameList::RefreshGameDirectory() { - // Reset the externals watcher whenever the game list is reloaded, - // primarily ensures that new titles and external dirs are caught. - ResetExternalWatcher(); - - if (!UISettings::values.game_dirs.empty() && current_worker != nullptr) { - LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list."); - QtCommon::system->GetFileSystemController().CreateFactories(*QtCommon::vfs); - PopulateAsync(UISettings::values.game_dirs); - } -} - -void GameList::RefreshExternalContent() { - // TODO: Explore the possibility of only resetting the metadata cache for that specific game. - if (!UISettings::values.game_dirs.empty() && current_worker != nullptr) { - LOG_INFO(Frontend, "External content directory changed. Clearing metadata cache."); - QtCommon::Game::ResetMetadata(false); - QtCommon::system->GetFileSystemController().CreateFactories(*QtCommon::vfs); - PopulateAsync(UISettings::values.game_dirs); - } -} - -void GameList::ResetExternalWatcher() { - auto watch_dirs = external_watcher->directories(); - if (!watch_dirs.isEmpty()) { - external_watcher->removePaths(watch_dirs); - } - - for (const std::string& dir : Settings::values.external_content_dirs) { - external_watcher->addPath(QString::fromStdString(dir)); - } -} - -void GameList::ToggleFavorite(u64 program_id) { - if (!UISettings::values.favorited_ids.contains(program_id)) { - tree_view->setRowHidden(0, item_model->invisibleRootItem()->index(), - !search_field->filterText().isEmpty()); - UISettings::values.favorited_ids.append(program_id); - AddFavorite(program_id); - item_model->sort(tree_view->header()->sortIndicatorSection(), - tree_view->header()->sortIndicatorOrder()); - } else { - UISettings::values.favorited_ids.removeOne(program_id); - RemoveFavorite(program_id); - if (UISettings::values.favorited_ids.size() == 0) { - tree_view->setRowHidden(0, item_model->invisibleRootItem()->index(), true); - } - } - emit SaveConfig(); -} - -void GameList::AddFavorite(u64 program_id) { - auto* favorites_row = item_model->item(0); - - for (int i = 1; i < item_model->rowCount() - 1; i++) { - const auto* folder = item_model->item(i); - for (int j = 0; j < folder->rowCount(); j++) { - if (folder->child(j)->data(GameListItemPath::ProgramIdRole).toULongLong() == - program_id) { - QList list; - for (int k = 0; k < COLUMN_COUNT; k++) { - list.append(folder->child(j, k)->clone()); - } - list[0]->setData(folder->child(j)->data(GameListItem::SortRole), - GameListItem::SortRole); - list[0]->setText(folder->child(j)->data(Qt::DisplayRole).toString()); - - favorites_row->appendRow(list); - return; - } - } - } -} - -void GameList::RemoveFavorite(u64 program_id) { - auto* favorites_row = item_model->item(0); - - for (int i = 0; i < favorites_row->rowCount(); i++) { - const auto* game = favorites_row->child(i); - if (game->data(GameListItemPath::ProgramIdRole).toULongLong() == program_id) { - favorites_row->removeRow(i); - return; - } - } -} - -GameListPlaceholder::GameListPlaceholder(MainWindow* parent) : QWidget{parent} { - connect(parent, &MainWindow::UpdateThemedIcons, this, - &GameListPlaceholder::onUpdateThemedIcons); - - layout = new QVBoxLayout; - image = new QLabel; - text = new QLabel; - layout->setAlignment(Qt::AlignCenter); - image->setPixmap(QIcon::fromTheme(QStringLiteral("plus_folder")).pixmap(200)); - - RetranslateUI(); - QFont font = text->font(); - font.setPointSize(20); - text->setFont(font); - text->setAlignment(Qt::AlignHCenter); - image->setAlignment(Qt::AlignHCenter); - - layout->addWidget(image); - layout->addWidget(text); - setLayout(layout); -} - -GameListPlaceholder::~GameListPlaceholder() = default; - -void GameListPlaceholder::onUpdateThemedIcons() { - image->setPixmap(QIcon::fromTheme(QStringLiteral("plus_folder")).pixmap(200)); -} - -void GameListPlaceholder::mouseDoubleClickEvent(QMouseEvent* event) { - emit GameListPlaceholder::AddDirectory(); + item_model->RetranslateUI(); } void GameList::SetupScrollAnimation() { auto setup = [this](QVariantAnimation* anim, QScrollBar* bar) { - // animation handles moving the bar instead of Qt's built in crap anim->setEasingCurve(QEasingCurve::OutCubic); anim->setDuration(200); connect(anim, &QVariantAnimation::valueChanged, this, @@ -1250,8 +688,8 @@ void GameList::SetupScrollAnimation() { setup(vertical_scroll, tree_view->verticalScrollBar()); setup(horizontal_scroll, tree_view->horizontalScrollBar()); - setup(vertical_scroll, list_view->verticalScrollBar()); - setup(horizontal_scroll, list_view->horizontalScrollBar()); + setup(vertical_scroll, grid_view->verticalScrollBar()); + setup(horizontal_scroll, grid_view->horizontalScrollBar()); } bool GameList::eventFilter(QObject* obj, QEvent* event) { @@ -1263,13 +701,11 @@ bool GameList::eventFilter(QObject* obj, QEvent* event) { int deltaX = wheelEvent->angleDelta().x(); int deltaY = wheelEvent->angleDelta().y(); - // if shift is held do a horizontal scroll if (horizontal && deltaY != 0 && deltaX == 0) { deltaX = deltaY; deltaY = 0; } - // TODO(crueter): dedup this if (deltaY != 0) { if (vertical_scroll->state() == QAbstractAnimation::Stopped) vertical_scroll_target = m_currentView->verticalScrollBar()->value(); @@ -1304,7 +740,6 @@ bool GameList::eventFilter(QObject* obj, QEvent* event) { if (obj == m_currentView->viewport() && event->type() == QEvent::MouseButtonPress) { QMouseEvent* mouseEvent = static_cast(event); - // if the user clicks outside of the list, deselect the current item. QModelIndex index = m_currentView->indexAt(mouseEvent->pos()); if (!index.isValid()) { m_currentView->selectionModel()->clearSelection(); @@ -1312,14 +747,44 @@ bool GameList::eventFilter(QObject* obj, QEvent* event) { } } - if (obj == list_view->viewport() && event->type() == QEvent::Resize) { - UpdateIconSize(); + if (obj == grid_view->viewport() && event->type() == QEvent::Resize) { + grid_view->UpdateIconSize(); + return true; + } + + if (obj == carousel_view->viewport() && event->type() == QEvent::Resize) { + carousel_view->UpdateIconSize(); return true; } return QWidget::eventFilter(obj, event); } +GameListPlaceholder::GameListPlaceholder(MainWindow* parent) : QWidget{parent} { + layout = new QVBoxLayout; + image = new QLabel; + text = new QLabel; + layout->setAlignment(Qt::AlignCenter); + image->setPixmap(QIcon::fromTheme(QStringLiteral("plus_folder")).pixmap(200)); + + RetranslateUI(); + QFont font = text->font(); + font.setPointSize(20); + text->setFont(font); + text->setAlignment(Qt::AlignHCenter); + image->setAlignment(Qt::AlignHCenter); + + layout->addWidget(image); + layout->addWidget(text); + setLayout(layout); +} + +GameListPlaceholder::~GameListPlaceholder() = default; + +void GameListPlaceholder::mouseDoubleClickEvent(QMouseEvent* event) { + emit GameListPlaceholder::AddDirectory(); +} + void GameListPlaceholder::changeEvent(QEvent* event) { if (event->type() == QEvent::LanguageChange) { RetranslateUI(); diff --git a/src/yuzu/game/game_list.h b/src/yuzu/game/game_list.h index 42efcbdc3b..027e58df3a 100644 --- a/src/yuzu/game/game_list.h +++ b/src/yuzu/game/game_list.h @@ -6,43 +6,45 @@ #pragma once -#include #include #include #include #include #include #include -#include #include #include #include -#include #include "common/common_types.h" #include "core/core.h" #include "frontend_common/play_time_manager.h" #include "qt_common/config/uisettings.h" +#include "qt_common/game_list/model.h" #include "qt_common/util/game.h" #include "yuzu/compatibility_list.h" class QVariantAnimation; -class QListView; - class GameCard; -namespace Core { -class System; -} - +class GameListModel; +class GameTree; +class GameGrid; +class GameListSearchField; class ControllerNavigation; class GameListWorker; -class GameListSearchField; class GameListDir; class MainWindow; enum class AmLaunchType; enum class StartGameType; +class GameCarousel; + +class QAbstractItemView; +namespace Core { +class System; +} + namespace FileSys { class ManualContentProvider; class VfsFilesystem; @@ -62,16 +64,6 @@ class GameList : public QWidget { Q_OBJECT public: - enum { - COLUMN_NAME, - COLUMN_FILE_TYPE, - COLUMN_SIZE, - COLUMN_PLAY_TIME, - COLUMN_ADD_ONS, - COLUMN_COMPATIBILITY, - COLUMN_COUNT, // Number of columns - }; - explicit GameList(std::shared_ptr vfs_, FileSys::ManualContentProvider* provider_, PlayTime::PlayTimeManager& play_time_manager_, Core::System& system_, @@ -95,15 +87,13 @@ public: /// Disables events from the emulated controller void UnloadController(); - static const QStringList supported_file_extensions; - - bool IsTreeMode(); void ResetViewMode(); public slots: void RefreshGameDirectory(); void RefreshExternalContent(); - void ResetExternalWatcher(); + void UpdateIconSizes(); + void OnPopulate(); signals: void BootGame(const QString& game_path, StartGameType type); @@ -132,27 +122,19 @@ signals: void SaveConfig(); private slots: - void OnItemExpanded(const QModelIndex& item); void OnTextChanged(const QString& new_text); void OnFilterCloseClicked(); - void OnUpdateThemedIcons(); - - void UpdateIconSize(); + void OnPopulatingCompleted(const QStringList& watch_list); private: - friend class GameListWorker; - void WorkerEvent(); + void SetupViews(); + void SetupScrollAnimation(); + bool eventFilter(QObject* obj, QEvent* event) override; + void changeEvent(QEvent*) override; - void AddDirEntry(GameListDir* entry_items); - void AddEntry(const QList& entry_items, GameListDir* parent); - void DonePopulating(const QStringList& watch_list); - -private: void ValidateEntry(const QModelIndex& item); void ToggleFavorite(u64 program_id); - void AddFavorite(u64 program_id); - void RemoveFavorite(u64 program_id); void PopupContextMenu(const QPoint& menu_location); void AddGamePopup(QMenu& context_menu, u64 program_id, const std::string& path); @@ -160,41 +142,34 @@ private: void AddPermDirPopup(QMenu& context_menu, QModelIndex selected); void AddFavoritesPopup(QMenu& context_menu); - void changeEvent(QEvent*) override; void RetranslateUI(); + friend class GameListSearchField; + std::shared_ptr vfs; FileSys::ManualContentProvider* provider; - GameListSearchField* search_field; + GameListSearchField* search_field = nullptr; MainWindow* main_window = nullptr; QVBoxLayout* layout = nullptr; - QTreeView* tree_view = nullptr; - QListView* list_view = nullptr; - GameCard* m_gameCard = nullptr; + GameTree* tree_view = nullptr; + GameGrid* grid_view = nullptr; + GameCarousel* carousel_view = nullptr; + GameListModel* item_model = nullptr; + Settings::GameListMode game_list_mode; - QStandardItemModel* item_model = nullptr; - std::unique_ptr current_worker; - QFileSystemWatcher* watcher = nullptr; - QFileSystemWatcher* external_watcher = nullptr; ControllerNavigation* controller_navigation = nullptr; - CompatibilityList compatibility_list; QVariantAnimation* vertical_scroll = nullptr; QVariantAnimation* horizontal_scroll = nullptr; int vertical_scroll_target = 0; int horizontal_scroll_target = 0; - void SetupScrollAnimation(); - bool eventFilter(QObject* obj, QEvent* event) override; - - friend class GameListSearchField; - const PlayTime::PlayTimeManager& play_time_manager; Core::System& system; bool m_isTreeMode = true; - QAbstractItemView* m_currentView = tree_view; + QAbstractItemView* m_currentView = nullptr; }; class GameListPlaceholder : public QWidget { @@ -206,9 +181,6 @@ public: signals: void AddDirectory(); -private slots: - void onUpdateThemedIcons(); - protected: void mouseDoubleClickEvent(QMouseEvent* event) override; diff --git a/src/yuzu/game/game_tree.cpp b/src/yuzu/game/game_tree.cpp new file mode 100644 index 0000000000..b197eaad03 --- /dev/null +++ b/src/yuzu/game/game_tree.cpp @@ -0,0 +1,153 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include +#include +#include +#include + +#include "qt_common/config/uisettings.h" +#include "qt_common/game_list/game_list_p.h" +#include "qt_common/game_list/model.h" +#include "yuzu/game/common.h" +#include "yuzu/game/game_tree.h" + +GameTree::GameTree(QWidget* parent) : QTreeView{parent} { + setAlternatingRowColors(true); + setSelectionMode(QHeaderView::SingleSelection); + setSelectionBehavior(QHeaderView::SelectRows); + setVerticalScrollMode(QHeaderView::ScrollPerPixel); + setHorizontalScrollMode(QHeaderView::ScrollPerPixel); + setSortingEnabled(true); + setEditTriggers(QHeaderView::NoEditTriggers); + setContextMenuPolicy(Qt::CustomContextMenu); + setAttribute(Qt::WA_AcceptTouchEvents, true); + setStyleSheet(QStringLiteral("QTreeView{ border: none; }")); + + connect(this, &QTreeView::expanded, this, &GameTree::OnItemExpanded); + connect(this, &QTreeView::collapsed, this, &GameTree::OnItemExpanded); +} + +void GameTree::SetModel(GameListModel* model) { + QTreeView::setModel(model); + LoadInterfaceLayout(); + UpdateColumnVisibility(model); +} + +void GameTree::OnItemExpanded(const QModelIndex& item) { + const auto type = item.data(GameListItem::TypeRole).value(); + const bool is_dir = type == GameListItemType::CustomDir || type == GameListItemType::SdmcDir || + type == GameListItemType::UserNandDir || + type == GameListItemType::SysNandDir; + const bool is_fave = type == GameListItemType::Favorites; + if (!is_dir && !is_fave) { + return; + } + const bool is_expanded = isExpanded(item); + if (is_fave) { + UISettings::values.favorites_expanded = is_expanded; + return; + } + const int item_dir_index = item.data(GameListDir::GameDirRole).toInt(); + UISettings::values.game_dirs[item_dir_index].expanded = is_expanded; +} + +void GameTree::SaveInterfaceLayout() { + UISettings::values.gamelist_header_state = header()->saveState(); +} + +void GameTree::LoadInterfaceLayout() { + auto* hdr = header(); + + if (hdr->restoreState(UISettings::values.gamelist_header_state)) + return; + + hdr->resizeSection(GameListModel::COLUMN_NAME, 840); +} + +void GameTree::UpdateColumnVisibility(GameListModel* model) { + Q_UNUSED(model) + setColumnHidden(GameListModel::COLUMN_ADD_ONS, !UISettings::values.show_add_ons); + setColumnHidden(GameListModel::COLUMN_COMPATIBILITY, !UISettings::values.show_compat); + setColumnHidden(GameListModel::COLUMN_FILE_TYPE, !UISettings::values.show_types); + setColumnHidden(GameListModel::COLUMN_SIZE, !UISettings::values.show_size); + setColumnHidden(GameListModel::COLUMN_PLAY_TIME, !UISettings::values.show_play_time); +} + +QString GameTree::GetLastFilterResultItem() const { + QString file_path; + + auto* model = qobject_cast(QTreeView::model()); + if (!model) + return {}; + + for (int i = 1; i < model->rowCount() - 1; ++i) { + const QStandardItem* folder = model->item(i, 0); + const QModelIndex folder_index = folder->index(); + const int children_count = folder->rowCount(); + + for (int j = 0; j < children_count; ++j) { + if (isRowHidden(j, folder_index)) { + continue; + } + + const QStandardItem* child = folder->child(j, 0); + file_path = child->data(GameListItemPath::FullPathRole).toString(); + } + } + + return file_path; +} + +int GameTree::FilterClosedResultCount(GameListModel* model) { + int children_total = 0; + + auto hide_favorites_row = UISettings::values.favorited_ids.size() == 0; + setRowHidden(0, model->invisibleRootItem()->index(), hide_favorites_row); + + for (int i = 1; i < model->rowCount() - 1; ++i) { + auto* folder = model->item(i, 0); + const QModelIndex folder_index = folder->index(); + const int children_count = folder->rowCount(); + for (int j = 0; j < children_count; ++j) { + ++children_total; + setRowHidden(j, folder_index, false); + } + } + + return children_total; +} + +void GameTree::ApplyFilter(const QString& edit_filter_text, GameListModel* model) { + int children_total = 0; + int result_count = 0; + + if (edit_filter_text.isEmpty()) { + children_total = FilterClosedResultCount(model); + emit FilterResultReady(children_total, children_total); + return; + } + + setRowHidden(0, model->invisibleRootItem()->index(), true); + + for (int i = 1; i < model->rowCount() - 1; ++i) { + auto* folder = model->item(i, 0); + const QModelIndex folder_index = folder->index(); + const int children_count = folder->rowCount(); + + for (int j = 0; j < children_count; ++j) { + ++children_total; + + const QStandardItem* child = folder->child(j, 0); + + if (Yuzu::FilterMatches(edit_filter_text, child)) { + setRowHidden(j, folder_index, false); + ++result_count; + } else { + setRowHidden(j, folder_index, true); + } + } + } + + emit FilterResultReady(result_count, children_total); +} diff --git a/src/yuzu/game/game_tree.h b/src/yuzu/game/game_tree.h new file mode 100644 index 0000000000..e4417803aa --- /dev/null +++ b/src/yuzu/game/game_tree.h @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include + +class GameListModel; + +class GameTree : public QTreeView { + Q_OBJECT + +public: + explicit GameTree(QWidget* parent = nullptr); + + void SetModel(GameListModel* model); + + QString GetLastFilterResultItem() const; + int FilterClosedResultCount(GameListModel* model); + void ApplyFilter(const QString& edit_filter_text, GameListModel* model); + + void SaveInterfaceLayout(); + void LoadInterfaceLayout(); + + void UpdateColumnVisibility(GameListModel* model); + +signals: + void ItemExpandedChanged(const QModelIndex& item); + void FilterResultReady(int visible, int total); + +private slots: + void OnItemExpanded(const QModelIndex& item); +}; diff --git a/src/yuzu/game/search_field.cpp b/src/yuzu/game/search_field.cpp new file mode 100644 index 0000000000..c2d4893742 --- /dev/null +++ b/src/yuzu/game/search_field.cpp @@ -0,0 +1,124 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "game/game_list.h" +#include "game/search_field.h" + +#include +#include + +// TODO: Remove GameList dependence? +GameListSearchField::KeyReleaseEater::KeyReleaseEater(GameList* gamelist_, QObject* parent) + : QObject(parent), gamelist{gamelist_} {} + +// EventFilter in order to process systemkeys while editing the searchfield +bool GameListSearchField::KeyReleaseEater::eventFilter(QObject* obj, QEvent* event) { + // If it isn't a KeyRelease event then continue with standard event processing + if (event->type() != QEvent::KeyRelease) + return QObject::eventFilter(obj, event); + + QKeyEvent* keyEvent = static_cast(event); + QString edit_filter_text = gamelist->search_field->edit_filter->text().toLower(); + + // If the searchfield's text hasn't changed special function keys get checked + // If no function key changes the searchfield's text the filter doesn't need to get reloaded + if (edit_filter_text == edit_filter_text_old) { + switch (keyEvent->key()) { + // Escape: Resets the searchfield + case Qt::Key_Escape: { + if (edit_filter_text_old.isEmpty()) { + return QObject::eventFilter(obj, event); + } else { + gamelist->search_field->edit_filter->clear(); + edit_filter_text.clear(); + } + break; + } + // Return and Enter + // If the enter key gets pressed first checks how many and which entry is visible + // If there is only one result launch this game + case Qt::Key_Return: + case Qt::Key_Enter: { + if (gamelist->search_field->visible == 1) { + const QString file_path = gamelist->GetLastFilterResultItem(); + + // To avoid loading error dialog loops while confirming them using enter + // Also users usually want to run a different game after closing one + gamelist->search_field->edit_filter->clear(); + edit_filter_text.clear(); + emit gamelist->GameChosen(file_path); + } else { + return QObject::eventFilter(obj, event); + } + break; + } + default: + return QObject::eventFilter(obj, event); + } + } + edit_filter_text_old = edit_filter_text; + return QObject::eventFilter(obj, event); +} + +void GameListSearchField::setFilterResult(int visible_, int total_) { + visible = visible_; + total = total_; + + label_filter_result->setText(tr("%1 of %n result(s)", "", total).arg(visible)); +} + +QString GameListSearchField::filterText() const { + return edit_filter->text(); +} + +void GameListSearchField::clear() { + edit_filter->clear(); +} + +void GameListSearchField::setFocus() { + if (edit_filter->isVisible()) { + edit_filter->setFocus(); + } +} + +GameListSearchField::GameListSearchField(GameList* parent) : QWidget{parent} { + auto* const key_release_eater = new KeyReleaseEater(parent, this); + layout_filter = new QHBoxLayout; + layout_filter->setContentsMargins(8, 8, 8, 8); + label_filter = new QLabel; + edit_filter = new QLineEdit; + edit_filter->clear(); + edit_filter->installEventFilter(key_release_eater); + edit_filter->setClearButtonEnabled(true); + connect(edit_filter, &QLineEdit::textChanged, parent, &GameList::OnTextChanged); + label_filter_result = new QLabel; + button_filter_close = new QToolButton(this); + button_filter_close->setText(QStringLiteral("X")); + button_filter_close->setCursor(Qt::ArrowCursor); + button_filter_close->setStyleSheet( + QStringLiteral("QToolButton{ border: none; padding: 0px; color: " + "#000000; font-weight: bold; background: #F0F0F0; }" + "QToolButton:hover{ border: none; padding: 0px; color: " + "#EEEEEE; font-weight: bold; background: #E81123}")); + connect(button_filter_close, &QToolButton::clicked, parent, &GameList::OnFilterCloseClicked); + layout_filter->setSpacing(10); + layout_filter->addWidget(label_filter); + layout_filter->addWidget(edit_filter); + layout_filter->addWidget(label_filter_result); + layout_filter->addWidget(button_filter_close); + setLayout(layout_filter); + RetranslateUI(); +} + +void GameListSearchField::changeEvent(QEvent* event) { + if (event->type() == QEvent::LanguageChange) { + RetranslateUI(); + } + + QWidget::changeEvent(event); +} + +void GameListSearchField::RetranslateUI() { + label_filter->setText(tr("Filter:")); + edit_filter->setPlaceholderText(tr("Enter pattern to filter")); +} diff --git a/src/yuzu/game/search_field.h b/src/yuzu/game/search_field.h new file mode 100644 index 0000000000..18f97300f7 --- /dev/null +++ b/src/yuzu/game/search_field.h @@ -0,0 +1,52 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include + +class GameList; +class QHBoxLayout; +class QTreeView; +class QLabel; +class QLineEdit; +class QToolButton; + +class GameListSearchField : public QWidget { + Q_OBJECT + +public: + explicit GameListSearchField(GameList* parent = nullptr); + + QString filterText() const; + void setFilterResult(int visible_, int total_); + + void clear(); + void setFocus(); + +private: + void changeEvent(QEvent*) override; + void RetranslateUI(); + + class KeyReleaseEater : public QObject { + public: + explicit KeyReleaseEater(GameList* gamelist_, QObject* parent = nullptr); + + private: + GameList* gamelist = nullptr; + QString edit_filter_text_old; + + protected: + // EventFilter in order to process systemkeys while editing the searchfield + bool eventFilter(QObject* obj, QEvent* event) override; + }; + int visible; + int total; + + QHBoxLayout* layout_filter = nullptr; + QTreeView* tree_view = nullptr; + QLabel* label_filter = nullptr; + QLineEdit* edit_filter = nullptr; + QLabel* label_filter_result = nullptr; + QToolButton* button_filter_close = nullptr; +}; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index f8528d9671..29a1f075fa 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -15,6 +15,10 @@ #include "qt_common/gui_settings.h" #endif +#ifndef _WIN32 +#include +#endif + #include "main_window.h" #ifdef _WIN32 @@ -106,10 +110,27 @@ int main(int argc, char* argv[]) { QCoreApplication::setOrganizationName(QStringLiteral("eden")); QCoreApplication::setApplicationName(QStringLiteral("eden")); + // Increases the maximum open file limit. + // TODO: This should be common to all frontends. #ifdef _WIN32 - // Increases the maximum open file limit to 8192 + // MSVCRT limits this to 2048 for some inexplicable (and likely arcane) reason, + // so we have to account for that as well. +#ifdef __MSVCRT__ + _setmaxstdio(2048); +#else _setmaxstdio(8192); -#elif defined(__APPLE__) +#endif // __MSVCRT__ +#elif defined(__unix__) || defined(__APPLE__) + // Set the max open file limit to 8192, or the hard limit. + // Most sane systems should not hit the hard limit here. + struct rlimit rl; + if (getrlimit(RLIMIT_NOFILE, &rl) == 0) { + rl.rlim_cur = std::min(8192, rl.rlim_max); + setrlimit(RLIMIT_NOFILE, &rl); + } +#endif // _WIN32 + +#if defined(__APPLE__) // If you start a bundle (binary) on OSX without the Terminal, the working directory is "/". // But since we require the working directory to be the executable path for the location of // the user folder in the Qt Frontend, we need to cd into that working directory diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index 59aab0ef93..deb7774d25 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui @@ -45,7 +45,7 @@ 0 0 1280 - 22 + 23 @@ -111,6 +111,7 @@ + @@ -620,6 +621,14 @@ Show &Performance Overlay + + + true + + + &Carousel View + + diff --git a/src/yuzu/main_window.cpp b/src/yuzu/main_window.cpp index 7549325567..bc74f5da79 100644 --- a/src/yuzu/main_window.cpp +++ b/src/yuzu/main_window.cpp @@ -2,17 +2,18 @@ // SPDX-License-Identifier: GPL-3.0-or-later // Qt on macOS doesn't define VMA shit -#include -#include "common/settings.h" -#include "common/settings_enums.h" -#include "frontend_common/settings_generator.h" -#include "qt_common/qt_string_lookup.h" -#include "render/performance_overlay.h" -#include "updater/update_dialog.h" #if defined(QT_STATICPLUGIN) && !defined(__APPLE__) #undef VMA_IMPLEMENTATION #endif +#include +#include "common/fs/path_util.h" +#include "common/settings.h" +#include "common/settings_enums.h" +#include "frontend_common/settings_generator.h" +#include "render/performance_overlay.h" +#include "updater/update_dialog.h" + #include "common/fs/ryujinx_compat.h" #include "main_window.h" #include "network/network.h" @@ -30,10 +31,10 @@ #include "bootmanager.h" #include "loading_screen.h" +#include "qt_common/util/vk.h" #include "ryujinx_dialog.h" #include "set_play_time_dialog.h" #include "util/util.h" -#include "vk_device_info.h" #include "yuzu/game/game_list.h" #include "applets/qt_amiibo_settings.h" @@ -85,6 +86,7 @@ #include "qt_common/abstract/frontend.h" #include "qt_common/qt_common.h" +#include "qt_common/qt_string_lookup.h" #include "qt_common/util/content.h" #include "qt_common/util/fs.h" @@ -92,6 +94,8 @@ #include "qt_common/util/mod.h" #include "qt_common/util/path.h" +#include "qt_common/render/emu_thread.h" + // These are wrappers to avoid the calls to CreateDirectory and CreateFile because of the Windows // defines. static FileSys::VirtualDir VfsFilesystemCreateDirectoryWrapper(const std::string& path, @@ -120,7 +124,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include "common/string_util.h" #ifdef ARCHITECTURE_x86_64 -#include "common/x64/cpu_detect.h" +#include "common/cpu_features.h" #endif // Core // @@ -153,7 +157,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include "video_core/renderer_base.h" #include "video_core/shader_notify.h" -#include +#include #include @@ -314,73 +318,6 @@ enum class CalloutFlag : uint32_t { const int MainWindow::max_recent_files_item; -static void RemoveCachedContents() { - const auto cache_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::CacheDir); - const auto offline_fonts = cache_dir / "fonts"; - const auto offline_manual = cache_dir / "offline_web_applet_manual"; - const auto offline_legal_information = cache_dir / "offline_web_applet_legal_information"; - const auto offline_system_data = cache_dir / "offline_web_applet_system_data"; - - Common::FS::RemoveDirRecursively(offline_fonts); - Common::FS::RemoveDirRecursively(offline_manual); - Common::FS::RemoveDirRecursively(offline_legal_information); - Common::FS::RemoveDirRecursively(offline_system_data); -} - -static void LogRuntimes() { -#ifdef _MSC_VER - // It is possible that the name of the dll will change. - // vcruntime140.dll is for 2015 and onwards - static constexpr char runtime_dll_name[] = "vcruntime140.dll"; - UINT sz = GetFileVersionInfoSizeA(runtime_dll_name, nullptr); - bool runtime_version_inspection_worked = false; - if (sz > 0) { - std::vector buf(sz); - if (GetFileVersionInfoA(runtime_dll_name, 0, sz, buf.data())) { - VS_FIXEDFILEINFO* pvi; - sz = sizeof(VS_FIXEDFILEINFO); - if (VerQueryValueA(buf.data(), "\\", reinterpret_cast(&pvi), &sz)) { - if (pvi->dwSignature == VS_FFI_SIGNATURE) { - runtime_version_inspection_worked = true; - LOG_INFO(Frontend, "MSVC Compiler: {} Runtime: {}.{}.{}.{}", _MSC_VER, - pvi->dwProductVersionMS >> 16, pvi->dwProductVersionMS & 0xFFFF, - pvi->dwProductVersionLS >> 16, pvi->dwProductVersionLS & 0xFFFF); - } - } - } - } - if (!runtime_version_inspection_worked) { - LOG_INFO(Frontend, "Unable to inspect {}", runtime_dll_name); - } -#endif - LOG_INFO(Frontend, "Qt Compile: {} Runtime: {}", QT_VERSION_STR, qVersion()); -} - -static QString PrettyProductName() { -#ifdef _WIN32 - // After Windows 10 Version 2004, Microsoft decided to switch to a different notation: 20H2 - // With that notation change they changed the registry key used to denote the current version - QSettings windows_registry( - QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"), - QSettings::NativeFormat); - const QString release_id = windows_registry.value(QStringLiteral("ReleaseId")).toString(); - if (release_id == QStringLiteral("2009")) { - const u32 current_build = windows_registry.value(QStringLiteral("CurrentBuild")).toUInt(); - const QString display_version = - windows_registry.value(QStringLiteral("DisplayVersion")).toString(); - const u32 ubr = windows_registry.value(QStringLiteral("UBR")).toUInt(); - u32 version = 10; - if (current_build >= 22000) { - version = 11; - } - return QStringLiteral("Windows %1 Version %2 (Build %3.%4)") - .arg(QString::number(version), display_version, QString::number(current_build), - QString::number(ubr)); - } -#endif - return QSysInfo::prettyProductName(); -} - namespace { constexpr std::array, 5> default_game_icon_sizes{ @@ -446,11 +383,6 @@ MainWindow::MainWindow(bool has_broken_vulkan) UISettings::RestoreWindowState(config); - QtCommon::system->Initialize(); - - Common::Log::Initialize(); - Common::Log::Start(); - LoadTranslation(); FrontendCommon::GenerateSettings(); @@ -471,10 +403,6 @@ MainWindow::MainWindow(bool has_broken_vulkan) play_time_manager = std::make_unique(); - Network::Init(); - - QtCommon::Meta::RegisterMetaTypes(); - InitializeWidgets(); InitializeDebugWidgets(); InitializeRecentFileMenuActions(); @@ -486,53 +414,9 @@ MainWindow::MainWindow(bool has_broken_vulkan) ConnectMenuEvents(); ConnectWidgetEvents(); - QtCommon::system->HIDCore().ReloadInputDevices(); + QtCommon::SetupHID(); controller_dialog->refreshConfiguration(); - const auto branch_name = std::string(Common::g_scm_branch); - const auto description = std::string(Common::g_scm_desc); - const auto build_id = std::string(Common::g_build_id); - - const auto yuzu_build = fmt::format("Eden Development Build | {}-{}", branch_name, description); - const auto override_build = - fmt::format(fmt::runtime(std::string(Common::g_title_bar_format_idle)), build_id); - const auto yuzu_build_version = override_build.empty() ? yuzu_build : override_build; - const auto processor_count = std::thread::hardware_concurrency(); - - LOG_INFO(Frontend, "Eden Version: {}", yuzu_build_version); - LogRuntimes(); -#ifdef ARCHITECTURE_x86_64 - const auto& caps = Common::GetCPUCaps(); - std::string cpu_string = caps.cpu_string; - if (caps.avx || caps.avx2 || caps.avx512f) { - cpu_string += " | AVX"; - if (caps.avx512f) { - cpu_string += "512"; - } else if (caps.avx2) { - cpu_string += '2'; - } - if (caps.fma || caps.fma4) { - cpu_string += " | FMA"; - } - } - LOG_INFO(Frontend, "Host CPU: {}", cpu_string); - if (std::optional processor_core = Common::GetProcessorCount()) { - LOG_INFO(Frontend, "Host CPU Cores: {}", *processor_core); - } -#endif - LOG_INFO(Frontend, "Host CPU Threads: {}", processor_count); - LOG_INFO(Frontend, "Host OS: {}", PrettyProductName().toStdString()); - LOG_INFO(Frontend, "Host RAM: {:.2f} GiB", - Common::GetMemInfo().TotalPhysicalMemory / f64{1_GiB}); - LOG_INFO(Frontend, "Host Swap: {:.2f} GiB", Common::GetMemInfo().TotalSwapMemory / f64{1_GiB}); -#ifdef _WIN32 - LOG_INFO(Frontend, "Host Timer Resolution: {:.4f} ms", - std::chrono::duration_cast>( - Common::Windows::SetCurrentTimerResolutionToMaximum()) - .count()); - QtCommon::system->CoreTiming().SetTimerResolutionNs( - Common::Windows::GetCurrentTimerResolution()); -#endif UpdateWindowTitle(); show(); @@ -547,13 +431,8 @@ MainWindow::MainWindow(bool has_broken_vulkan) } #endif - QtCommon::system->SetContentProvider(std::make_unique()); - QtCommon::system->RegisterContentProvider(FileSys::ContentProviderUnionSlot::FrontendManual, - QtCommon::provider.get()); - QtCommon::system->GetFileSystemController().CreateFactories(*QtCommon::vfs); - - // Remove cached contents generated during the previous session - RemoveCachedContents(); + // Setup content providers. + QtCommon::SetupContentProviders(); // Gen keys if necessary OnCheckFirmwareDecryption(); @@ -635,6 +514,7 @@ MainWindow::MainWindow(bool has_broken_vulkan) QString game_path; bool should_launch_qlaunch = false; + bool should_launch_hlaunch = false; bool should_launch_setup = false; bool has_gamepath = false; bool is_fullscreen = false; @@ -676,6 +556,8 @@ MainWindow::MainWindow(bool has_broken_vulkan) players[0].profile_name = args[++i].toStdString(); } else if (args[i] == QStringLiteral("-qlaunch")) { should_launch_qlaunch = true; + } else if (args[i] == QStringLiteral("-hlaunch")) { + should_launch_hlaunch = true; } else if (args[i] == QStringLiteral("-setup")) { should_launch_setup = true; } else { @@ -694,10 +576,15 @@ MainWindow::MainWindow(bool has_broken_vulkan) } else { if (!game_path.isEmpty()) { BootGame(game_path, ApplicationAppletParameters()); - } else { - if (should_launch_qlaunch) { - LaunchFirmwareApplet(u64(Service::AM::AppletProgramId::QLaunch), std::nullopt); - } + } else if (should_launch_qlaunch) { + LaunchFirmwareApplet(u64(Service::AM::AppletProgramId::QLaunch), std::nullopt); + } else if (should_launch_hlaunch) { + std::filesystem::path const sd_dir = + Common::FS::GetEdenPathString(Common::FS::EdenPath::SDMCDir); + auto const hbl_path = (sd_dir / "atmosphere" / "hbl.nsp").string(); + BootGame( + QString::fromStdString(hbl_path), + LibraryAppletParameters(0x010000000000100Dull, Service::AM::AppletId::QLaunch)); } } } @@ -1073,7 +960,7 @@ void MainWindow::InitializeWidgets() { #ifdef YUZU_ENABLE_COMPATIBILITY_REPORTING ui->action_Report_Compatibility->setVisible(true); #endif - render_window = new GRenderWindow(this, emu_thread.get(), input_subsystem, *QtCommon::system); + render_window = new GRenderWindow(this, input_subsystem); render_window->hide(); game_list = new GameList(QtCommon::vfs, QtCommon::provider.get(), *play_time_manager, @@ -1204,10 +1091,9 @@ void MainWindow::InitializeWidgets() { aa_status_button->setFocusPolicy(Qt::NoFocus); connect(aa_status_button, &QPushButton::clicked, [&] { auto aa_mode = Settings::values.anti_aliasing.GetValue(); - aa_mode = static_cast(static_cast(aa_mode) + 1); - if (aa_mode == Settings::AntiAliasing::MaxEnum) { - aa_mode = Settings::AntiAliasing::None; - } + aa_mode = Settings::AntiAliasing(u32(aa_mode) + 1); + if (u32(aa_mode) > u32(Settings::EnumMetadata::GetLast())) + aa_mode = Settings::EnumMetadata::GetFirst(); Settings::values.anti_aliasing.SetValue(aa_mode); aa_status_button->setChecked(true); UpdateAAText(); @@ -1533,11 +1419,12 @@ void MainWindow::OnAppFocusStateChanged(Qt::ApplicationState state) { return; } if (UISettings::values.pause_when_in_background) { - if (emu_thread->IsRunning() && + if (QtCommon::emu_thread->IsRunning() && (state & (Qt::ApplicationHidden | Qt::ApplicationInactive))) { auto_paused = true; OnPauseGame(); - } else if (!emu_thread->IsRunning() && auto_paused && (state & Qt::ApplicationActive)) { + } else if (!QtCommon::emu_thread->IsRunning() && auto_paused && + (state & Qt::ApplicationActive)) { auto_paused = false; OnStartGame(); } @@ -1589,11 +1476,6 @@ void MainWindow::ConnectWidgetEvents() { connect(this, &MainWindow::UpdateInstallProgress, this, &MainWindow::IncrementInstallProgress); - connect(this, &MainWindow::EmulationStarting, render_window, - &GRenderWindow::OnEmulationStarting); - connect(this, &MainWindow::EmulationStopping, render_window, - &GRenderWindow::OnEmulationStopping); - // Software Keyboard Applet connect(this, &MainWindow::EmulationStarting, this, &MainWindow::SoftwareKeyboardExit); connect(this, &MainWindow::EmulationStopping, this, &MainWindow::SoftwareKeyboardExit); @@ -1647,6 +1529,7 @@ void MainWindow::ConnectMenuEvents() { connect_menu(ui->action_Grid_View, &MainWindow::SetGridView); connect_menu(ui->action_Tree_View, &MainWindow::SetTreeView); + connect_menu(ui->action_Carousel_View, &MainWindow::SetCarouselView); game_size_actions = new QActionGroup(this); game_size_actions->setExclusive(true); @@ -1666,6 +1549,8 @@ void MainWindow::ConnectMenuEvents() { if (checked) { UISettings::values.game_icon_size.SetValue(size); CheckIconSize(); + + game_list->UpdateIconSizes(); game_list->RefreshGameDirectory(); } }); @@ -1747,7 +1632,7 @@ void MainWindow::ConnectMenuEvents() { } void MainWindow::UpdateMenuState() { - const bool is_paused = emu_thread == nullptr || !emu_thread->IsRunning(); + const bool is_paused = QtCommon::emu_thread == nullptr || !QtCommon::emu_thread->IsRunning(); const bool is_firmware_available = CheckFirmwarePresence(); const std::array running_actions{ @@ -1815,17 +1700,16 @@ void MainWindow::SetupPrepareForSleep() { } void MainWindow::OnPrepareForSleep(bool prepare_sleep) { - if (emu_thread == nullptr) { + if (QtCommon::emu_thread == nullptr) return; - } if (prepare_sleep) { - if (emu_thread->IsRunning()) { + if (QtCommon::emu_thread->IsRunning()) { auto_paused = true; OnPauseGame(); } } else { - if (!emu_thread->IsRunning() && auto_paused) { + if (!QtCommon::emu_thread->IsRunning() && auto_paused) { auto_paused = false; OnStartGame(); } @@ -1898,19 +1782,16 @@ void MainWindow::AllowOSSleep() { bool MainWindow::LoadROM(const QString& filename, Service::AM::FrontendAppletParameters params) { // Shutdown previous session if the emu thread is still active... - if (emu_thread != nullptr) { + if (QtCommon::emu_thread != nullptr) ShutdownGame(); - } - if (!render_window->InitRenderTarget()) { + if (!render_window->InitRenderTarget()) return false; - } QtCommon::system->SetFilesystem(QtCommon::vfs); - if (params.launch_type == Service::AM::LaunchType::FrontendInitiated) { + if (params.launch_type == Service::AM::LaunchType::FrontendInitiated) QtCommon::system->GetUserChannel().clear(); - } QtCommon::system->SetFrontendAppletSet({ std::make_unique(*this), // Amiibo Settings @@ -2017,36 +1898,6 @@ bool MainWindow::SelectAndSetCurrentUser( return true; } -void MainWindow::ConfigureFilesystemProvider(const std::string& filepath) { - // Ensure all NCAs are registered before launching the game - const auto file = QtCommon::vfs->OpenFile(filepath, FileSys::OpenMode::Read); - if (!file) { - return; - } - - if (QtCommon::provider->AddEntriesFromContainer(file)) { - return; - } - - auto loader = Loader::GetLoader(*QtCommon::system, file); - if (!loader) { - return; - } - - const auto file_type = loader->GetFileType(); - if (file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error) { - return; - } - - u64 program_id = 0; - const auto res2 = loader->ReadProgramId(program_id); - if (res2 == Loader::ResultStatus::Success && file_type == Loader::FileType::NCA) { - QtCommon::provider->AddEntry(FileSys::TitleType::Application, - FileSys::GetCRTypeFromNCAType(FileSys::NCA{file}.GetType()), - program_id, file); - } -} - void MainWindow::BootGame(const QString& filename, Service::AM::FrontendAppletParameters params, StartGameType type) { LOG_INFO(Frontend, "Eden starting..."); @@ -2065,7 +1916,7 @@ void MainWindow::BootGame(const QString& filename, Service::AM::FrontendAppletPa last_filename_booted = filename; - ConfigureFilesystemProvider(filename.toStdString()); + QtCommon::Content::configureFilesystemProvider(filename.toStdString()); const auto v_file = Core::GetGameFileFromPath(QtCommon::vfs, filename.toUtf8().constData()); const auto loader = Loader::GetLoader(*QtCommon::system, v_file, params.program_id, params.program_index); @@ -2110,23 +1961,23 @@ void MainWindow::BootGame(const QString& filename, Service::AM::FrontendAppletPa game_list->setDisabled(true); // Create and start the emulation thread - emu_thread = std::make_unique(*QtCommon::system); - emit EmulationStarting(emu_thread.get()); - emu_thread->start(); + QtCommon::emu_thread = std::make_unique(); + emit EmulationStarting(); + QtCommon::emu_thread->start(); // Register an ExecuteProgram callback such that Core can execute a sub-program QtCommon::system->RegisterExecuteProgramCallback( [this](std::size_t program_index_) { render_window->ExecuteProgram(program_index_); }); QtCommon::system->RegisterExitCallback([this] { - emu_thread->ForceStop(); + QtCommon::emu_thread->ForceStop(); render_window->Exit(); }); connect(render_window, &GRenderWindow::Closed, this, &MainWindow::OnStopGame); connect(render_window, &GRenderWindow::MouseActivity, this, &MainWindow::OnMouseActivity); - connect(emu_thread.get(), &EmuThread::LoadProgress, loading_screen, + connect(QtCommon::emu_thread.get(), &EmuThread::LoadProgress, loading_screen, &LoadingScreen::OnLoadProgress, Qt::QueuedConnection); // Update the GUI @@ -2221,9 +2072,10 @@ bool MainWindow::OnShutdownBegin() { discord_rpc->Pause(); RequestGameExit(); - emu_thread->disconnect(); - emu_thread->SetRunning(true); + QtCommon::emu_thread->disconnect(); + QtCommon::emu_thread->SetRunning(true); + connect(QtCommon::emu_thread.get(), &QThread::finished, this, &MainWindow::OnEmulationStopped); emit EmulationStopping(); int shutdown_time = 1000; @@ -2237,7 +2089,6 @@ bool MainWindow::OnShutdownBegin() { shutdown_timer.setSingleShot(true); shutdown_timer.start(shutdown_time); connect(&shutdown_timer, &QTimer::timeout, this, &MainWindow::OnEmulationStopTimeExpired); - connect(emu_thread.get(), &QThread::finished, this, &MainWindow::OnEmulationStopped); // Disable everything to prevent anything from being triggered here ui->action_Pause->setEnabled(false); @@ -2255,17 +2106,17 @@ void MainWindow::OnShutdownBeginDialog() { } void MainWindow::OnEmulationStopTimeExpired() { - if (emu_thread) { - emu_thread->ForceStop(); + if (QtCommon::emu_thread) { + QtCommon::emu_thread->ForceStop(); } } void MainWindow::OnEmulationStopped() { shutdown_timer.stop(); - if (emu_thread) { - emu_thread->disconnect(); - emu_thread->wait(); - emu_thread.reset(); + if (QtCommon::emu_thread) { + QtCommon::emu_thread->disconnect(); + QtCommon::emu_thread->wait(); + QtCommon::emu_thread.reset(); } if (shutdown_dialog) { @@ -2906,7 +2757,7 @@ void MainWindow::OnMenuLoadFile() { is_load_file_select_active = true; const QString extensions = QStringLiteral("*.") - .append(GameList::supported_file_extensions.join(QStringLiteral(" *."))) + .append(QtCommon::supported_file_extensions.join(QStringLiteral(" *."))) .append(QStringLiteral(" main")); const QString file_filter = tr("Switch Executable (%1);;All Files (*.*)", "%1 is an identifier for the Switch executable file extensions.") @@ -3142,7 +2993,7 @@ void MainWindow::OnMenuRecentFile() { void MainWindow::OnStartGame() { PreventOSSleep(); - emu_thread->SetRunning(true); + QtCommon::emu_thread->SetRunning(true); UpdateMenuState(); OnTasStateChanged(); @@ -3168,7 +3019,7 @@ void MainWindow::OnRestartGame() { } void MainWindow::OnPauseGame() { - emu_thread->SetRunning(false); + QtCommon::emu_thread->SetRunning(false); play_time_manager->Stop(); UpdateMenuState(); AllowOSSleep(); @@ -3177,7 +3028,7 @@ void MainWindow::OnPauseGame() { void MainWindow::OnPauseContinueGame() { if (emulation_running) { - if (emu_thread->IsRunning()) { + if (QtCommon::emu_thread->IsRunning()) { OnPauseGame(); } else { OnStartGame(); @@ -3273,8 +3124,8 @@ void MainWindow::OnMenuReportCompatibility() { tr("Compatibility list reporting is currently disabled. Check back later!")); // #if defined(ARCHITECTURE_x86_64) && !defined(__APPLE__) - // const auto& caps = Common::GetCPUCaps(); - // const bool has_fma = caps.fma || caps.fma4; + // const auto& caps = g_cpu_caps; + // const bool has_fma = caps.fma; // const auto processor_count = std::thread::hardware_concurrency(); // const bool has_4threads = processor_count == 0 || processor_count >= 4; // const bool has_8gb_ram = Common::GetMemInfo().TotalPhysicalMemory >= 8_GiB; @@ -3467,9 +3318,10 @@ void MainWindow::ResetWindowSize1080() { void MainWindow::SetGameListMode(Settings::GameListMode mode) { ui->action_Grid_View->setChecked(mode == Settings::GameListMode::GridView); ui->action_Tree_View->setChecked(mode == Settings::GameListMode::TreeView); + ui->action_Carousel_View->setChecked(mode == Settings::GameListMode::CarouselView); UISettings::values.game_list_mode = mode; - ui->action_Show_Game_Name->setEnabled(mode == Settings::GameListMode::GridView); + ui->action_Show_Game_Name->setEnabled(mode != Settings::GameListMode::TreeView); CheckIconSize(); game_list->ResetViewMode(); @@ -3483,11 +3335,15 @@ void MainWindow::SetTreeView() { SetGameListMode(Settings::GameListMode::TreeView); } +void MainWindow::SetCarouselView() { + SetGameListMode(Settings::GameListMode::CarouselView); +} + void MainWindow::CheckIconSize() { - // When in grid view mode, with text off - // there is no point in having icons turned off.. + // When in grid/carousel view mode, with text off + // there is no point in having icons turned off auto actions = game_size_actions->actions(); - if (UISettings::values.game_list_mode.GetValue() == Settings::GameListMode::GridView && + if (UISettings::values.game_list_mode.GetValue() != Settings::GameListMode::TreeView && !UISettings::values.show_game_name.GetValue()) { u32 newSize = UISettings::values.game_icon_size.GetValue(); if (newSize == 0) { @@ -3518,6 +3374,7 @@ void MainWindow::ToggleShowGameName() { CheckIconSize(); + game_list->UpdateIconSizes(); game_list->RefreshGameDirectory(); } @@ -3777,10 +3634,9 @@ void MainWindow::OnIncreaseVolume() { void MainWindow::OnToggleAdaptingFilter() { auto filter = Settings::values.scaling_filter.GetValue(); - filter = static_cast(static_cast(filter) + 1); - if (filter == Settings::ScalingFilter::MaxEnum) { - filter = Settings::ScalingFilter::NearestNeighbor; - } + filter = Settings::ScalingFilter(u32(filter) + 1); + if (u32(filter) > u32(Settings::EnumMetadata::GetLast())) + filter = Settings::EnumMetadata::GetFirst(); Settings::values.scaling_filter.SetValue(filter); filter_status_button->setChecked(true); UpdateFilterText(); @@ -3857,12 +3713,9 @@ void MainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file_ } void MainWindow::OnLoadAmiibo() { - if (emu_thread == nullptr || !emu_thread->IsRunning()) { + if (QtCommon::emu_thread == nullptr || !QtCommon::emu_thread->IsRunning() || + is_amiibo_file_select_active) return; - } - if (is_amiibo_file_select_active) { - return; - } auto* virtual_amiibo = input_subsystem->GetVirtualAmiibo(); @@ -3966,79 +3819,21 @@ void MainWindow::OnVerifyInstalledContents() { QtCommon::Content::VerifyInstalledContents(); } -void MainWindow::InstallFirmware(const QString& location, bool recursive) { - QtCommon::Content::InstallFirmware(location, recursive); +void MainWindow::OnInstallFirmware() { + QtCommon::Content::InstallFirmware(); OnCheckFirmwareDecryption(); } -void MainWindow::OnInstallFirmware() { - // Don't do this while emulation is running, that'd probably be a bad idea. - if (emu_thread != nullptr && emu_thread->IsRunning()) { - return; - } - - // Check for installed keys, error out, suggest restart? - if (!ContentManager::AreKeysPresent()) { - QMessageBox::information( - this, tr("Keys not installed"), - tr("Install decryption keys and restart Eden before attempting to install firmware.")); - return; - } - - const QString firmware_source_location = QFileDialog::getExistingDirectory( - this, tr("Select Dumped Firmware Source Location"), {}, QFileDialog::ShowDirsOnly); - if (firmware_source_location.isEmpty()) { - return; - } - - InstallFirmware(firmware_source_location); -} - void MainWindow::OnInstallFirmwareFromZIP() { - // Don't do this while emulation is running, that'd probably be a bad idea. - if (emu_thread != nullptr && emu_thread->IsRunning()) { - return; - } - - // Check for installed keys, error out, suggest restart? - if (!ContentManager::AreKeysPresent()) { - QMessageBox::information( - this, tr("Keys not installed"), - tr("Install decryption keys and restart Eden before attempting to install firmware.")); - return; - } - - const QString firmware_zip_location = QFileDialog::getOpenFileName( - this, tr("Select Dumped Firmware ZIP"), {}, tr("Zipped Archives (*.zip)")); - if (firmware_zip_location.isEmpty()) { - return; - } - - const QString qCacheDir = QtCommon::Content::UnzipFirmwareToTmp(firmware_zip_location); - - // In this case, it has to be done recursively, since sometimes people - // will pack it into a subdirectory after dumping - if (!qCacheDir.isEmpty()) { - InstallFirmware(qCacheDir, true); - std::error_code ec; - std::filesystem::remove_all(std::filesystem::temp_directory_path() / "eden" / "firmware", - ec); - - if (ec) { - QMessageBox::warning(this, tr("Firmware cleanup failed"), - tr("Failed to clean up extracted firmware cache.\n" - "Check write permissions in the system temp directory and try " - "again.\nOS reported error: %1") - .arg(QString::fromStdString(ec.message()))); - } - } + QtCommon::Content::InstallFirmwareZip(); + OnCheckFirmwareDecryption(); } +// TODO(crueter): QtCommon this: game list populate can be a signal? void MainWindow::OnInstallDecryptionKeys() { // Don't do this while emulation is running. - if (emu_thread != nullptr && emu_thread->IsRunning()) { + if (QtCommon::emu_thread != nullptr && QtCommon::emu_thread->IsRunning()) return; - } QtCommon::Content::InstallKeys(); @@ -4066,11 +3861,10 @@ void MainWindow::OnDataDialog() { void MainWindow::OnToggleFilterBar() { game_list->SetFilterVisible(ui->action_Show_Filter_Bar->isChecked()); - if (ui->action_Show_Filter_Bar->isChecked()) { + if (ui->action_Show_Filter_Bar->isChecked()) game_list->SetFilterFocus(); - } else { + else game_list->ClearFilter(); - } } void MainWindow::OnToggleStatusBar() { @@ -4078,15 +3872,15 @@ void MainWindow::OnToggleStatusBar() { } void MainWindow::OnTogglePerfOverlay() { - if (perf_overlay) { + if (perf_overlay) perf_overlay->setVisible(ui->action_Show_Performance_Overlay->isChecked()); - } } void MainWindow::OnGameListRefresh() { // Resets metadata cache and reloads QtCommon::Game::ResetMetadata(false); game_list->RefreshGameDirectory(); + game_list->RefreshExternalContent(); SetFirmwareVersion(); } @@ -4185,9 +3979,8 @@ void MainWindow::OnCreateHomeMenuApplicationMenuShortcut() { } void MainWindow::OnCaptureScreenshot() { - if (emu_thread == nullptr || !emu_thread->IsRunning()) { + if (QtCommon::emu_thread == nullptr || !QtCommon::emu_thread->IsRunning()) return; - } const u64 title_id = QtCommon::system->GetApplicationProcessProgramID(); const auto screenshot_path = @@ -4199,9 +3992,8 @@ void MainWindow::OnCaptureScreenshot() { .arg(title_id, 16, 16, QLatin1Char{'0'}) .arg(date); - if (!Common::FS::CreateDir(screenshot_path.toStdString())) { + if (!Common::FS::CreateDir(screenshot_path.toStdString())) return; - } #ifdef _WIN32 if (UISettings::values.enable_screenshot_save_as) { @@ -4307,16 +4099,15 @@ void MainWindow::OnTasStateChanged() { } void MainWindow::UpdateStatusBar() { - if (emu_thread == nullptr || !QtCommon::system->IsPoweredOn()) { + if (QtCommon::emu_thread == nullptr || !QtCommon::system->IsPoweredOn()) { status_bar_update_timer.stop(); return; } - if (Settings::values.tas_enable) { + if (Settings::values.tas_enable) tas_label->setText(GetTasStateDescription()); - } else { + else tas_label->clear(); - } auto results = QtCommon::system->GetAndResetPerfStats(); auto& shader_notify = QtCommon::system->GPU().ShaderNotify(); @@ -4384,8 +4175,7 @@ void MainWindow::UpdateAPIText() { void MainWindow::UpdateFilterText() { const auto filter = Settings::values.scaling_filter.GetValue(); const auto filter_text = ConfigurationShared::scaling_filter_texts_map.find(filter)->second; - filter_status_button->setText(filter == Settings::ScalingFilter::Fsr ? tr("FSR") - : filter_text.toUpper()); + filter_status_button->setText(filter_text.toUpper()); } void MainWindow::UpdateAAText() { @@ -4438,14 +4228,13 @@ void MainWindow::UpdateUISettings() { } void MainWindow::UpdateInputDrivers() { - if (!input_subsystem) { + if (!input_subsystem) return; - } input_subsystem->PumpEvents(); } void MainWindow::HideMouseCursor() { - if (emu_thread == nullptr && UISettings::values.hide_mouse) { + if (QtCommon::emu_thread == nullptr && UISettings::values.hide_mouse) { mouse_hide_timer.stop(); ShowMouseCursor(); return; @@ -4455,9 +4244,8 @@ void MainWindow::HideMouseCursor() { void MainWindow::ShowMouseCursor() { render_window->unsetCursor(); - if (emu_thread != nullptr && UISettings::values.hide_mouse) { + if (QtCommon::emu_thread != nullptr && UISettings::values.hide_mouse) mouse_hide_timer.start(); - } } void MainWindow::OnMouseActivity() { @@ -4637,14 +4425,14 @@ bool MainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed } bool MainWindow::ConfirmClose() { - if (emu_thread == nullptr || - UISettings::values.confirm_before_stopping.GetValue() == ConfirmStop::Ask_Never) { + if (QtCommon::emu_thread == nullptr || + UISettings::values.confirm_before_stopping.GetValue() == ConfirmStop::Ask_Never) return true; - } + if (!QtCommon::system->GetExitLocked() && - UISettings::values.confirm_before_stopping.GetValue() == ConfirmStop::Ask_Based_On_Game) { + UISettings::values.confirm_before_stopping.GetValue() == ConfirmStop::Ask_Based_On_Game) return true; - } + const auto text = tr("Are you sure you want to close Eden?"); return question(this, tr("Eden"), text); } @@ -4665,9 +4453,8 @@ void MainWindow::closeEvent(QCloseEvent* event) { game_list->UnloadController(); // Shutdown session if the emu thread is active... - if (emu_thread != nullptr) { + if (QtCommon::emu_thread != nullptr) ShutdownGame(); - } render_window->close(); multiplayer_state->Close(); @@ -4730,7 +4517,7 @@ void MainWindow::dragMoveEvent(QDragMoveEvent* event) { } bool MainWindow::ConfirmChangeGame() { - if (emu_thread == nullptr) + if (QtCommon::emu_thread == nullptr) return true; // Use custom question to link controller navigation @@ -4741,9 +4528,9 @@ bool MainWindow::ConfirmChangeGame() { } bool MainWindow::ConfirmForceLockedExit() { - if (emu_thread == nullptr) { + if (QtCommon::emu_thread == nullptr) return true; - } + const auto text = tr("The currently running application has requested Eden to not exit.\n\n" "Would you like to bypass this and exit anyway?"); @@ -4751,9 +4538,8 @@ bool MainWindow::ConfirmForceLockedExit() { } void MainWindow::RequestGameExit() { - if (!QtCommon::system->IsPoweredOn()) { + if (!QtCommon::system->IsPoweredOn()) return; - } QtCommon::system->SetExitRequested(true); QtCommon::system->GetAppletManager().RequestExit(); @@ -4766,14 +4552,14 @@ void MainWindow::filterBarSetChecked(bool state) { static void AdjustLinkColor() { QPalette new_pal(qApp->palette()); - if (UISettings::IsDarkTheme()) { + + if (UISettings::IsDarkTheme()) new_pal.setColor(QPalette::Link, QColor(0, 190, 255, 255)); - } else { + else new_pal.setColor(QPalette::Link, QColor(0, 140, 200, 255)); - } - if (qApp->palette().color(QPalette::Link) != new_pal.color(QPalette::Link)) { + + if (qApp->palette().color(QPalette::Link) != new_pal.color(QPalette::Link)) qApp->setPalette(new_pal); - } } void MainWindow::UpdateUITheme() { @@ -4781,9 +4567,8 @@ void MainWindow::UpdateUITheme() { UISettings::themes[static_cast(UISettings::default_theme)].second); QString current_theme = QString::fromStdString(UISettings::values.theme); - if (current_theme.isEmpty()) { + if (current_theme.isEmpty()) current_theme = default_theme; - } #ifdef _WIN32 QIcon::setThemeName(current_theme); @@ -4844,17 +4629,15 @@ void MainWindow::LoadTranslation() { QStringLiteral(":/languages/")); } - if (loaded) { + if (loaded) qApp->installTranslator(&translator); - } else { + else UISettings::values.language = std::string("en"); - } } void MainWindow::OnLanguageChanged(const QString& locale) { - if (UISettings::values.language.GetValue() != std::string("en")) { + if (UISettings::values.language.GetValue() != std::string("en")) qApp->removeTranslator(&translator); - } QList actions = game_size_actions->actions(); for (size_t i = 0; i < default_game_icon_sizes.size(); i++) { @@ -4870,11 +4653,10 @@ void MainWindow::OnLanguageChanged(const QString& locale) { void MainWindow::SetDiscordEnabled([[maybe_unused]] bool state) { #ifdef USE_DISCORD_PRESENCE - if (state) { + if (state) discord_rpc = std::make_unique(*QtCommon::system); - } else { + else discord_rpc = std::make_unique(); - } #else discord_rpc = std::make_unique(); #endif diff --git a/src/yuzu/main_window.h b/src/yuzu/main_window.h index b676c0534a..40253aa52f 100644 --- a/src/yuzu/main_window.h +++ b/src/yuzu/main_window.h @@ -20,9 +20,9 @@ #include "common/common_types.h" #include "common/settings_enums.h" #include "frontend_common/content_manager.h" -#include "frontend_common/update_checker.h" #include "input_common/drivers/tas_input.h" #include "qt_common/config/qt_config.h" +#include "qt_common/qt_common.h" #include "qt_common/util/game.h" #include "yuzu/compatibility_list.h" #include "yuzu/hotkeys.h" @@ -38,6 +38,7 @@ #ifdef ENABLE_UPDATE_CHECKER #include #include +#include "common/net/net.h" #endif class QtConfig; @@ -66,11 +67,6 @@ class QtProfileSelectionDialog; class QtSoftwareKeyboardDialog; class QtNXWebEngineView; -enum class StartGameType { - Normal, // Can use custom configuration - Global, // Only uses global configuration -}; - namespace VideoCore { class ShaderNotify; } @@ -184,11 +180,8 @@ signals: * Signal that is emitted when a new EmuThread has been created and an emulation session is * about to start. At this time, the core system emulation has been initialized, and all * emulation handles and memory should be valid. - * - * @param emu_thread Pointer to the newly created EmuThread (to be used by widgets that need to - * access/change emulation state). */ - void EmulationStarting(EmuThread* emu_thread); + void EmulationStarting(); /** * Signal that is emitted when emulation is about to stop. At this time, the EmuThread and core @@ -417,6 +410,7 @@ private slots: void SetGameListMode(Settings::GameListMode mode); void SetGridView(); void SetTreeView(); + void SetCarouselView(); void CheckIconSize(); void ToggleShowGameName(); @@ -465,7 +459,6 @@ private: bool CheckFirmwarePresence(); void SetFirmwareVersion(); void SetFPSSuffix(); - void ConfigureFilesystemProvider(const std::string& filepath); /** * Open (or not) the right confirm dialog based on current setting and game exit lock * @returns true if the player confirmed or the settings do no require it @@ -538,7 +531,6 @@ private: // Whether emulation is currently running in yuzu. bool emulation_running = false; - std::unique_ptr emu_thread; // The path to the game currently running QString current_game_path; // Whether a user was set on the command line (skips UserSelector if it's forced to show up) @@ -599,8 +591,6 @@ private: const std::string& game_title, QtCommon::Game::ShortcutTarget target, std::string arguments, const bool needs_title); - void InstallFirmware(const QString& location, bool recursive = false); - protected: void dropEvent(QDropEvent* event) override; void dragEnterEvent(QDragEnterEvent* event) override; diff --git a/src/yuzu/migration_dialog.h b/src/yuzu/migration_dialog.h index a2a2c80358..1cd0f75760 100644 --- a/src/yuzu/migration_dialog.h +++ b/src/yuzu/migration_dialog.h @@ -1,8 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later -#ifndef MIGRATION_DIALOG_H -#define MIGRATION_DIALOG_H +#pragma once #include #include @@ -29,5 +28,3 @@ private: QAbstractButton* m_clickedButton; }; - -#endif // MIGRATION_DIALOG_H diff --git a/src/yuzu/migration_worker.h b/src/yuzu/migration_worker.h index 42abedb9c8..0a8a5b8275 100644 --- a/src/yuzu/migration_worker.h +++ b/src/yuzu/migration_worker.h @@ -1,8 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later -#ifndef MIGRATION_WORKER_H -#define MIGRATION_WORKER_H +#pragma once #include #include "common/fs/path_util.h" @@ -73,5 +72,3 @@ private: MigrationStrategy strategy; QString success_text = tr("Data was migrated successfully."); }; - -#endif // MIGRATION_WORKER_H diff --git a/src/yuzu/multiplayer/chat_room.cpp b/src/yuzu/multiplayer/chat_room.cpp index e274aaf62d..c28ae31bee 100644 --- a/src/yuzu/multiplayer/chat_room.cpp +++ b/src/yuzu/multiplayer/chat_room.cpp @@ -20,7 +20,7 @@ #include "common/logging.h" #include "network/announce_multiplayer_session.h" #include "ui_chat_room.h" -#include "yuzu/game/game_list_p.h" +#include "qt_common/game_list/game_list_p.h" #include "yuzu/multiplayer/chat_room.h" #include "yuzu/multiplayer/message.h" #ifdef ENABLE_WEB_SERVICE diff --git a/src/yuzu/multiplayer/client_room.cpp b/src/yuzu/multiplayer/client_room.cpp index 391ca56c05..31e5e20e52 100644 --- a/src/yuzu/multiplayer/client_room.cpp +++ b/src/yuzu/multiplayer/client_room.cpp @@ -14,7 +14,7 @@ #include "common/logging.h" #include "network/announce_multiplayer_session.h" #include "ui_client_room.h" -#include "yuzu/game/game_list_p.h" +#include "qt_common/game_list/game_list_p.h" #include "yuzu/multiplayer/client_room.h" #include "yuzu/multiplayer/message.h" #include "yuzu/multiplayer/moderation_dialog.h" diff --git a/src/yuzu/multiplayer/host_room.cpp b/src/yuzu/multiplayer/host_room.cpp index 2a5530e02c..ac6017e71f 100644 --- a/src/yuzu/multiplayer/host_room.cpp +++ b/src/yuzu/multiplayer/host_room.cpp @@ -20,7 +20,7 @@ #include "network/announce_multiplayer_session.h" #include "qt_common/config/uisettings.h" #include "ui_host_room.h" -#include "yuzu/game/game_list_p.h" +#include "qt_common/game_list/game_list_p.h" #include "yuzu/main_window.h" #include "yuzu/multiplayer/host_room.h" #include "yuzu/multiplayer/message.h" diff --git a/src/yuzu/multiplayer/lobby.cpp b/src/yuzu/multiplayer/lobby.cpp index 8e9ff6563f..d9e752896a 100644 --- a/src/yuzu/multiplayer/lobby.cpp +++ b/src/yuzu/multiplayer/lobby.cpp @@ -15,7 +15,7 @@ #include "network/network.h" #include "qt_common/config/uisettings.h" #include "ui_lobby.h" -#include "yuzu/game/game_list_p.h" +#include "qt_common/game_list/game_list_p.h" #include "yuzu/main_window.h" #include "yuzu/multiplayer/client_room.h" #include "yuzu/multiplayer/lobby.h" diff --git a/src/yuzu/ryujinx_dialog.h b/src/yuzu/ryujinx_dialog.h index c067499cc1..e668cf5ca0 100644 --- a/src/yuzu/ryujinx_dialog.h +++ b/src/yuzu/ryujinx_dialog.h @@ -1,8 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later -#ifndef RYUJINX_DIALOG_H -#define RYUJINX_DIALOG_H +#pragma once #include #include @@ -28,5 +27,3 @@ private: std::filesystem::path m_eden; std::filesystem::path m_ryu; }; - -#endif // RYUJINX_DIALOG_H diff --git a/src/yuzu/updater/update_dialog.cpp b/src/yuzu/updater/update_dialog.cpp index c2c0556e9c..d5af18cbf1 100644 --- a/src/yuzu/updater/update_dialog.cpp +++ b/src/yuzu/updater/update_dialog.cpp @@ -46,6 +46,7 @@ UpdateDialog::UpdateDialog(const Common::Net::Release& release, QWidget* parent) QDesktopServices::openUrl(QUrl{QString::fromStdString(release.html_url)}); }); } else if (assets.size() == 1) { + ui->groupBox->setHidden(true); m_asset = assets[0]; connect(this, &QDialog::accepted, this, &UpdateDialog::Download); diff --git a/src/yuzu/updater/update_dialog.h b/src/yuzu/updater/update_dialog.h index 692481b710..e821ef660d 100644 --- a/src/yuzu/updater/update_dialog.h +++ b/src/yuzu/updater/update_dialog.h @@ -15,7 +15,7 @@ class UpdateDialog : public QDialog { Q_OBJECT public: - explicit UpdateDialog(const Common::Net::Release &release, QWidget* parent = nullptr); + explicit UpdateDialog(const Common::Net::Release& release, QWidget* parent = nullptr); ~UpdateDialog(); private slots: @@ -23,6 +23,6 @@ private slots: private: Ui::UpdateDialog* ui; - QList m_buttons; + QList m_buttons; Common::Net::Asset m_asset; }; diff --git a/src/yuzu/util/util.cpp b/src/yuzu/util/util.cpp index a3933d9b63..eb6583b358 100644 --- a/src/yuzu/util/util.cpp +++ b/src/yuzu/util/util.cpp @@ -4,14 +4,11 @@ // SPDX-FileCopyrightText: 2015 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include #include #include "applets/qt_profile_select.h" -#include "common/logging.h" #include "core/frontend/applets/profile_select.h" #include "core/hle/service/acc/profile_manager.h" -#include "frontend_common/data_manager.h" #include "qt_common/qt_common.h" #include "yuzu/util/util.h" @@ -28,126 +25,6 @@ QFont GetMonospaceFont() { return font; } -QString ReadableByteSize(qulonglong size) { - return QString::fromStdString(FrontendCommon::DataManager::ReadableBytesSize(size)); -} - -QPixmap CreateCirclePixmapFromColor(const QColor& color) { - QPixmap circle_pixmap(16, 16); - circle_pixmap.fill(Qt::transparent); - QPainter painter(&circle_pixmap); - painter.setRenderHint(QPainter::Antialiasing); - painter.setPen(color); - painter.setBrush(color); - painter.drawEllipse({circle_pixmap.width() / 2.0, circle_pixmap.height() / 2.0}, 7.0, 7.0); - return circle_pixmap; -} - -bool SaveIconToFile(const std::filesystem::path& icon_path, const QImage& image) { -#if defined(WIN32) -#pragma pack(push, 2) - struct IconDir { - WORD id_reserved; - WORD id_type; - WORD id_count; - }; - - struct IconDirEntry { - BYTE width; - BYTE height; - BYTE color_count; - BYTE reserved; - WORD planes; - WORD bit_count; - DWORD bytes_in_res; - DWORD image_offset; - }; -#pragma pack(pop) - - const QImage source_image = image.convertToFormat(QImage::Format_RGB32); - constexpr std::array scale_sizes{256, 128, 64, 48, 32, 24, 16}; - constexpr int bytes_per_pixel = 4; - - const IconDir icon_dir{ - .id_reserved = 0, - .id_type = 1, - .id_count = static_cast(scale_sizes.size()), - }; - - Common::FS::IOFile icon_file(icon_path.string(), Common::FS::FileAccessMode::Write, - Common::FS::FileType::BinaryFile); - if (!icon_file.IsOpen()) { - return false; - } - - if (!icon_file.Write(icon_dir)) { - return false; - } - - std::size_t image_offset = sizeof(IconDir) + (sizeof(IconDirEntry) * scale_sizes.size()); - for (std::size_t i = 0; i < scale_sizes.size(); i++) { - const int image_size = scale_sizes[i] * scale_sizes[i] * bytes_per_pixel; - const IconDirEntry icon_entry{ - .width = static_cast(scale_sizes[i]), - .height = static_cast(scale_sizes[i]), - .color_count = 0, - .reserved = 0, - .planes = 1, - .bit_count = bytes_per_pixel * 8, - .bytes_in_res = static_cast(sizeof(BITMAPINFOHEADER) + image_size), - .image_offset = static_cast(image_offset), - }; - image_offset += icon_entry.bytes_in_res; - if (!icon_file.Write(icon_entry)) { - return false; - } - } - - for (std::size_t i = 0; i < scale_sizes.size(); i++) { - const QImage scaled_image = source_image.scaled( - scale_sizes[i], scale_sizes[i], Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - const BITMAPINFOHEADER info_header{ - .biSize = sizeof(BITMAPINFOHEADER), - .biWidth = scaled_image.width(), - .biHeight = scaled_image.height() * 2, - .biPlanes = 1, - .biBitCount = bytes_per_pixel * 8, - .biCompression = BI_RGB, - .biSizeImage{}, - .biXPelsPerMeter{}, - .biYPelsPerMeter{}, - .biClrUsed{}, - .biClrImportant{}, - }; - - if (!icon_file.Write(info_header)) { - return false; - } - - for (int y = 0; y < scaled_image.height(); y++) { - const auto* line = scaled_image.scanLine(scaled_image.height() - 1 - y); - std::vector line_data(scaled_image.width() * bytes_per_pixel); - std::memcpy(line_data.data(), line, line_data.size()); - if (!icon_file.Write(line_data)) { - return false; - } - } - } - icon_file.Close(); - - return true; -#elif defined(__unix__) && !defined(__APPLE__) && !defined(__ANDROID__) - // Convert and write the icon as a PNG - if (!image.save(QString::fromStdString(icon_path.string()))) { - LOG_ERROR(Frontend, "Could not write icon as PNG to file"); - } else { - LOG_INFO(Frontend, "Wrote an icon to {}", icon_path.string()); - } - return true; -#else - return false; -#endif -} const std::optional GetProfileID() { // if there's only a single profile, the user probably wants to use that... right? const auto& profiles = QtCommon::system->GetProfileManager().FindExistingProfileUUIDs(); diff --git a/src/yuzu/util/util.h b/src/yuzu/util/util.h index 7b482aa11d..2e4e325e22 100644 --- a/src/yuzu/util/util.h +++ b/src/yuzu/util/util.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: 2015 Citra Emulator Project @@ -6,7 +6,6 @@ #pragma once -#include #include #include #include "common/uuid.h" @@ -14,24 +13,6 @@ /// Returns a QFont object appropriate to use as a monospace font for debugging widgets, etc. [[nodiscard]] QFont GetMonospaceFont(); -/// Convert a size in bytes into a readable format (KiB, MiB, etc.) -[[nodiscard]] QString ReadableByteSize(qulonglong size); - -/** - * Creates a circle pixmap from a specified color - * @param color The color the pixmap shall have - * @return QPixmap circle pixmap - */ -[[nodiscard]] QPixmap CreateCirclePixmapFromColor(const QColor& color); - -/** - * Saves a windows icon to a file - * @param path The icons path - * @param image The image to save - * @return bool If the operation succeeded - */ -[[nodiscard]] bool SaveIconToFile(const std::filesystem::path& icon_path, const QImage& image); - /** * Prompt the user for a profile ID. If there is only one valid profile, returns that profile. * @return The selected profile, or an std::nullopt if none were selected diff --git a/src/yuzu/vk_device_info.h b/src/yuzu/vk_device_info.h deleted file mode 100644 index bda8262f4e..0000000000 --- a/src/yuzu/vk_device_info.h +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-FileCopyrightText: 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include -#include -#include -#include -#include -#include "common/common_types.h" -#include "vulkan/vulkan_core.h" - -class QWindow; - -namespace Settings { -enum class VSyncMode : u32; -} -// #include "common/settings.h" - -namespace VkDeviceInfo { -// Short class to record Vulkan driver information for configuration purposes -class Record { -public: - explicit Record(std::string_view name, const std::vector& vsync_modes, - bool has_broken_compute); - ~Record(); - - const std::string name; - const std::vector vsync_support; - const bool has_broken_compute; -}; - -void PopulateRecords(std::vector& records, QWindow* window); -} // namespace VkDeviceInfo diff --git a/src/yuzu_cmd/CMakeLists.txt b/src/yuzu_cmd/CMakeLists.txt index 35df53f381..09a9bd6da1 100644 --- a/src/yuzu_cmd/CMakeLists.txt +++ b/src/yuzu_cmd/CMakeLists.txt @@ -17,20 +17,20 @@ endfunction() if (ENABLE_OPENGL) list(APPEND OPENGL_SOURCES - emu_window/emu_window_sdl2_gl.cpp - emu_window/emu_window_sdl2_gl.h + emu_window/emu_window_sdl3_gl.cpp + emu_window/emu_window_sdl3_gl.h ) else() set(OPENGL_SOURCES "") endif() add_executable(yuzu-cmd - emu_window/emu_window_sdl2.cpp - emu_window/emu_window_sdl2.h - emu_window/emu_window_sdl2_null.cpp - emu_window/emu_window_sdl2_null.h - emu_window/emu_window_sdl2_vk.cpp - emu_window/emu_window_sdl2_vk.h + emu_window/emu_window_sdl3.cpp + emu_window/emu_window_sdl3.h + emu_window/emu_window_sdl3_null.cpp + emu_window/emu_window_sdl3_null.h + emu_window/emu_window_sdl3_vk.cpp + emu_window/emu_window_sdl3_vk.h sdl_config.cpp sdl_config.h yuzu.cpp @@ -50,7 +50,7 @@ target_link_libraries(yuzu-cmd PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) create_resource("../../dist/eden.bmp" "yuzu_cmd/yuzu_icon.h" "yuzu_icon") target_include_directories(yuzu-cmd PRIVATE ${RESOURCES_DIR}) -target_link_libraries(yuzu-cmd PRIVATE SDL2::SDL2) +target_link_libraries(yuzu-cmd PRIVATE SDL3::SDL3) if(UNIX AND NOT APPLE) install(TARGETS yuzu-cmd) diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp deleted file mode 100644 index c54e5b4fda..0000000000 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp +++ /dev/null @@ -1,98 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include -#include - -#include - -#include "common/logging.h" -#include "common/scm_rev.h" -#include "video_core/renderer_vulkan/renderer_vulkan.h" -#include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h" - -#include -#include - -EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem_, - Core::System& system_, bool fullscreen) - : EmuWindow_SDL2{input_subsystem_, system_} { - const std::string window_title = fmt::format("Eden {} | {}-{} (Vulkan)", - Common::g_build_name, - Common::g_scm_branch, - Common::g_scm_desc); - render_window = - SDL_CreateWindow(window_title.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, - Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height, - SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); - - SDL_SysWMinfo wm; - SDL_VERSION(&wm.version); - if (SDL_GetWindowWMInfo(render_window, &wm) == SDL_FALSE) { - LOG_CRITICAL(Frontend, "Failed to get information from the window manager: {}", - SDL_GetError()); - std::exit(EXIT_FAILURE); - } - - SetWindowIcon(); - - if (fullscreen) { - Fullscreen(); - ShowCursor(false); - } - - switch (wm.subsystem) { -#ifdef SDL_VIDEO_DRIVER_WINDOWS - case SDL_SYSWM_TYPE::SDL_SYSWM_WINDOWS: - window_info.type = Core::Frontend::WindowSystemType::Windows; - window_info.render_surface = reinterpret_cast(wm.info.win.window); - break; -#endif -#ifdef SDL_VIDEO_DRIVER_X11 - case SDL_SYSWM_TYPE::SDL_SYSWM_X11: - window_info.type = Core::Frontend::WindowSystemType::X11; - window_info.display_connection = wm.info.x11.display; - window_info.render_surface = reinterpret_cast(wm.info.x11.window); - break; -#endif -#ifdef SDL_VIDEO_DRIVER_WAYLAND - case SDL_SYSWM_TYPE::SDL_SYSWM_WAYLAND: - window_info.type = Core::Frontend::WindowSystemType::Wayland; - window_info.display_connection = wm.info.wl.display; - window_info.render_surface = wm.info.wl.surface; - break; -#endif -#ifdef SDL_VIDEO_DRIVER_COCOA - case SDL_SYSWM_TYPE::SDL_SYSWM_COCOA: - window_info.type = Core::Frontend::WindowSystemType::Cocoa; - window_info.render_surface = SDL_Metal_CreateView(render_window); - break; -#endif -#ifdef SDL_VIDEO_DRIVER_ANDROID - case SDL_SYSWM_TYPE::SDL_SYSWM_ANDROID: - window_info.type = Core::Frontend::WindowSystemType::Android; - window_info.render_surface = reinterpret_cast(wm.info.android.window); - break; -#endif - default: - LOG_CRITICAL(Frontend, "Window manager subsystem {} not implemented", wm.subsystem); - std::exit(EXIT_FAILURE); - break; - } - - OnResize(); - OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); - SDL_PumpEvents(); - LOG_INFO(Frontend, "Eden Version: {} | {}-{} (Vulkan)", Common::g_build_name, - Common::g_scm_branch, Common::g_scm_desc); -} - -EmuWindow_SDL2_VK::~EmuWindow_SDL2_VK() = default; - -std::unique_ptr EmuWindow_SDL2_VK::CreateSharedContext() const { - return std::make_unique(); -} diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl3.cpp similarity index 68% rename from src/yuzu_cmd/emu_window/emu_window_sdl2.cpp rename to src/yuzu_cmd/emu_window/emu_window_sdl3.cpp index c00734216e..cd390d29f9 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl3.cpp @@ -1,9 +1,10 @@ // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2016 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include +#include #include "common/logging.h" #include "common/scm_rev.h" @@ -15,26 +16,25 @@ #include "input_common/drivers/mouse.h" #include "input_common/drivers/touch_screen.h" #include "input_common/main.h" -#include "yuzu_cmd/emu_window/emu_window_sdl2.h" +#include "yuzu_cmd/emu_window/emu_window_sdl3.h" #include "yuzu_cmd/yuzu_icon.h" -EmuWindow_SDL2::EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem_, Core::System& system_) +EmuWindow_SDL3::EmuWindow_SDL3(InputCommon::InputSubsystem* input_subsystem_, Core::System& system_) : input_subsystem{input_subsystem_}, system{system_} { input_subsystem->Initialize(); - if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) < 0) { - LOG_CRITICAL(Frontend, "Failed to initialize SDL2: {}, Exiting...", SDL_GetError()); + if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD)) { + LOG_CRITICAL(Frontend, "Failed to initialize SDL3: {}, Exiting...", SDL_GetError()); exit(1); } - SDL_SetMainReady(); } -EmuWindow_SDL2::~EmuWindow_SDL2() { +EmuWindow_SDL3::~EmuWindow_SDL3() { system.HIDCore().UnloadInputDevices(); input_subsystem->Shutdown(); SDL_Quit(); } -InputCommon::MouseButton EmuWindow_SDL2::SDLButtonToMouseButton(u32 button) const { +InputCommon::MouseButton EmuWindow_SDL3::SDLButtonToMouseButton(u32 button) const { switch (button) { case SDL_BUTTON_LEFT: return InputCommon::MouseButton::Left; @@ -52,7 +52,7 @@ InputCommon::MouseButton EmuWindow_SDL2::SDLButtonToMouseButton(u32 button) cons } /// @brief Translates pixel position to float position -EmuWindow_SDL2::FloatPairNonHFA EmuWindow_SDL2::MouseToTouchPos(s32 touch_x, s32 touch_y) const { +EmuWindow_SDL3::FloatPairNonHFA EmuWindow_SDL3::MouseToTouchPos(s32 touch_x, s32 touch_y) const { int w = 0, h = 0; SDL_GetWindowSize(render_window, &w, &h); const float fx = float(touch_x) / w; @@ -64,9 +64,9 @@ EmuWindow_SDL2::FloatPairNonHFA EmuWindow_SDL2::MouseToTouchPos(s32 touch_x, s32 }; } -void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) { +void EmuWindow_SDL3::OnMouseButton(u32 button, u8 state, s32 x, s32 y) { const auto mouse_button = SDLButtonToMouseButton(button); - if (state == SDL_PRESSED) { + if (state != 0) { auto const [touch_x, touch_y, _] = MouseToTouchPos(x, y); input_subsystem->GetMouse()->PressButton(x, y, mouse_button); input_subsystem->GetMouse()->PressMouseButton(mouse_button); @@ -76,64 +76,70 @@ void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) { } } -void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { +void EmuWindow_SDL3::OnMouseMotion(s32 x, s32 y) { auto const [touch_x, touch_y, _] = MouseToTouchPos(x, y); input_subsystem->GetMouse()->Move(x, y, 0, 0); input_subsystem->GetMouse()->MouseMove(touch_x, touch_y); input_subsystem->GetMouse()->TouchMove(touch_x, touch_y); } -void EmuWindow_SDL2::OnFingerDown(float x, float y, std::size_t id) { +void EmuWindow_SDL3::OnFingerDown(float x, float y, std::size_t id) { input_subsystem->GetTouchScreen()->TouchPressed(x, y, id); } -void EmuWindow_SDL2::OnFingerMotion(float x, float y, std::size_t id) { +void EmuWindow_SDL3::OnFingerMotion(float x, float y, std::size_t id) { input_subsystem->GetTouchScreen()->TouchMoved(x, y, id); } -void EmuWindow_SDL2::OnFingerUp() { +void EmuWindow_SDL3::OnFingerUp() { input_subsystem->GetTouchScreen()->ReleaseAllTouch(); } -void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) { - if (state == SDL_PRESSED) { +void EmuWindow_SDL3::OnKeyEvent(int key, u8 state) { + if (state != 0) { input_subsystem->GetKeyboard()->PressKey(static_cast(key)); - } else if (state == SDL_RELEASED) { + } else { input_subsystem->GetKeyboard()->ReleaseKey(static_cast(key)); } } -bool EmuWindow_SDL2::IsOpen() const { +bool EmuWindow_SDL3::IsOpen() const { return is_open; } -bool EmuWindow_SDL2::IsShown() const { +bool EmuWindow_SDL3::IsShown() const { return is_shown; } -void EmuWindow_SDL2::OnResize() { +void EmuWindow_SDL3::OnResize() { int width, height; - SDL_GL_GetDrawableSize(render_window, &width, &height); + SDL_GetWindowSizeInPixels(render_window, &width, &height); UpdateCurrentFramebufferLayout(width, height); } -void EmuWindow_SDL2::ShowCursor(bool show_cursor) { - SDL_ShowCursor(show_cursor ? SDL_ENABLE : SDL_DISABLE); +void EmuWindow_SDL3::ShowCursor(bool show_cursor) { + if (show_cursor) { + SDL_ShowCursor(); + } else { + SDL_HideCursor(); + } } -void EmuWindow_SDL2::Fullscreen() { +void EmuWindow_SDL3::Fullscreen() { SDL_DisplayMode display_mode; switch (Settings::values.fullscreen_mode.GetValue()) { case Settings::FullscreenMode::Exclusive: - // Set window size to render size before entering fullscreen -- SDL2 does not resize window - // to display dimensions automatically in this mode. - if (SDL_GetDesktopDisplayMode(0, &display_mode) == 0) { + // Set window size to render size before entering fullscreen in exclusive mode. + if (const SDL_DisplayMode* display_mode_ptr = + SDL_GetDesktopDisplayMode(SDL_GetDisplayForWindow(render_window))) { + display_mode = *display_mode_ptr; SDL_SetWindowSize(render_window, display_mode.w, display_mode.h); + SDL_SetWindowFullscreenMode(render_window, &display_mode); } else { LOG_ERROR(Frontend, "SDL_GetDesktopDisplayMode failed: {}", SDL_GetError()); } - if (SDL_SetWindowFullscreen(render_window, SDL_WINDOW_FULLSCREEN) == 0) { + if (SDL_SetWindowFullscreen(render_window, true)) { return; } @@ -141,7 +147,8 @@ void EmuWindow_SDL2::Fullscreen() { LOG_INFO(Frontend, "Attempting to use borderless fullscreen..."); [[fallthrough]]; case Settings::FullscreenMode::Borderless: - if (SDL_SetWindowFullscreen(render_window, SDL_WINDOW_FULLSCREEN_DESKTOP) == 0) { + SDL_SetWindowFullscreenMode(render_window, nullptr); + if (SDL_SetWindowFullscreen(render_window, true)) { return; } @@ -156,7 +163,7 @@ void EmuWindow_SDL2::Fullscreen() { } } -void EmuWindow_SDL2::WaitEvent() { +void EmuWindow_SDL3::WaitEvent() { // Called on main thread SDL_Event event; @@ -174,52 +181,52 @@ void EmuWindow_SDL2::WaitEvent() { } switch (event.type) { - case SDL_WINDOWEVENT: - switch (event.window.event) { - case SDL_WINDOWEVENT_SIZE_CHANGED: - case SDL_WINDOWEVENT_RESIZED: - case SDL_WINDOWEVENT_MAXIMIZED: - case SDL_WINDOWEVENT_RESTORED: - OnResize(); - break; - case SDL_WINDOWEVENT_MINIMIZED: - case SDL_WINDOWEVENT_EXPOSED: - is_shown = event.window.event == SDL_WINDOWEVENT_EXPOSED; - OnResize(); - break; - case SDL_WINDOWEVENT_CLOSE: - is_open = false; - break; - } + case SDL_EVENT_WINDOW_RESIZED: + case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED: + case SDL_EVENT_WINDOW_MAXIMIZED: + case SDL_EVENT_WINDOW_RESTORED: + OnResize(); break; - case SDL_KEYDOWN: - case SDL_KEYUP: - OnKeyEvent(static_cast(event.key.keysym.scancode), event.key.state); + case SDL_EVENT_WINDOW_MINIMIZED: + is_shown = false; + OnResize(); break; - case SDL_MOUSEMOTION: + case SDL_EVENT_WINDOW_EXPOSED: + is_shown = true; + OnResize(); + break; + case SDL_EVENT_WINDOW_CLOSE_REQUESTED: + is_open = false; + break; + case SDL_EVENT_KEY_DOWN: + case SDL_EVENT_KEY_UP: + OnKeyEvent(static_cast(event.key.scancode), event.key.down ? 1 : 0); + break; + case SDL_EVENT_MOUSE_MOTION: // ignore if it came from touch if (event.button.which != SDL_TOUCH_MOUSEID) OnMouseMotion(event.motion.x, event.motion.y); break; - case SDL_MOUSEBUTTONDOWN: - case SDL_MOUSEBUTTONUP: + case SDL_EVENT_MOUSE_BUTTON_DOWN: + case SDL_EVENT_MOUSE_BUTTON_UP: // ignore if it came from touch if (event.button.which != SDL_TOUCH_MOUSEID) { - OnMouseButton(event.button.button, event.button.state, event.button.x, event.button.y); + OnMouseButton(event.button.button, event.button.down ? 1 : 0, + static_cast(event.button.x), static_cast(event.button.y)); } break; - case SDL_FINGERDOWN: + case SDL_EVENT_FINGER_DOWN: OnFingerDown(event.tfinger.x, event.tfinger.y, - static_cast(event.tfinger.touchId)); + static_cast(event.tfinger.touchID)); break; - case SDL_FINGERMOTION: + case SDL_EVENT_FINGER_MOTION: OnFingerMotion(event.tfinger.x, event.tfinger.y, - static_cast(event.tfinger.touchId)); + static_cast(event.tfinger.touchID)); break; - case SDL_FINGERUP: + case SDL_EVENT_FINGER_UP: OnFingerUp(); break; - case SDL_QUIT: + case SDL_EVENT_QUIT: is_open = false; break; default: @@ -241,22 +248,22 @@ void EmuWindow_SDL2::WaitEvent() { } // Credits to Samantas5855 and others for this function. -void EmuWindow_SDL2::SetWindowIcon() { - SDL_RWops* const yuzu_icon_stream = SDL_RWFromConstMem((void*)yuzu_icon, yuzu_icon_size); +void EmuWindow_SDL3::SetWindowIcon() { + SDL_IOStream* const yuzu_icon_stream = SDL_IOFromConstMem((void*)yuzu_icon, yuzu_icon_size); if (yuzu_icon_stream == nullptr) { LOG_WARNING(Frontend, "Failed to create Eden icon stream."); return; } - SDL_Surface* const window_icon = SDL_LoadBMP_RW(yuzu_icon_stream, 1); + SDL_Surface* const window_icon = SDL_LoadBMP_IO(yuzu_icon_stream, true); if (window_icon == nullptr) { LOG_WARNING(Frontend, "Failed to read BMP from stream."); return; } // The icon is attached to the window pointer SDL_SetWindowIcon(render_window, window_icon); - SDL_FreeSurface(window_icon); + SDL_DestroySurface(window_icon); } -void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest(std::pair minimal_size) { +void EmuWindow_SDL3::OnMinimalClientAreaChangeRequest(std::pair minimal_size) { SDL_SetWindowMinimumSize(render_window, minimal_size.first, minimal_size.second); } diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl3.h similarity index 92% rename from src/yuzu_cmd/emu_window/emu_window_sdl2.h rename to src/yuzu_cmd/emu_window/emu_window_sdl3.h index 8aec1efda0..8e809a3f02 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl3.h @@ -1,5 +1,6 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2016 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -22,10 +23,10 @@ class InputSubsystem; enum class MouseButton; } // namespace InputCommon -class EmuWindow_SDL2 : public Core::Frontend::EmuWindow { +class EmuWindow_SDL3 : public Core::Frontend::EmuWindow { public: - explicit EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem_, Core::System& system_); - ~EmuWindow_SDL2(); + explicit EmuWindow_SDL3(InputCommon::InputSubsystem* input_subsystem_, Core::System& system_); + ~EmuWindow_SDL3(); /// Whether the window is still open, and a close request hasn't yet been sent bool IsOpen() const; @@ -85,7 +86,7 @@ protected: /// Is the window being shown? bool is_shown = true; - /// Internal SDL2 render window + /// Internal SDL3 render window SDL_Window* render_window{}; /// Keeps track of how often to update the title bar during gameplay diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl3_gl.cpp similarity index 79% rename from src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp rename to src/yuzu_cmd/emu_window/emu_window_sdl3_gl.cpp index 448c902131..e86858a232 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl3_gl.cpp @@ -9,7 +9,7 @@ #include #define SDL_MAIN_HANDLED -#include +#include #include #include @@ -20,7 +20,13 @@ #include "core/core.h" #include "input_common/main.h" #include "video_core/renderer_base.h" -#include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h" +#include "yuzu_cmd/emu_window/emu_window_sdl3_gl.h" + +namespace { +void* SDLGLGetProcAddress(const char* proc_name) { + return reinterpret_cast(SDL_GL_GetProcAddress(proc_name)); +} +} // Anonymous namespace class SDLGLContext : public Core::Frontend::GraphicsContext { public: @@ -30,7 +36,7 @@ public: ~SDLGLContext() { DoneCurrent(); - SDL_GL_DeleteContext(context); + SDL_GL_DestroyContext(context); } void SwapBuffers() override { @@ -58,7 +64,7 @@ private: bool is_current = false; }; -bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() { +bool EmuWindow_SDL3_GL::SupportsRequiredGLExtensions() { std::vector unsupported_ext{}; #ifdef HAS_OPENGL // Extensions required to support some texture formats. @@ -72,9 +78,9 @@ bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() { return unsupported_ext.empty(); } -EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem_, +EmuWindow_SDL3_GL::EmuWindow_SDL3_GL(InputCommon::InputSubsystem* input_subsystem_, Core::System& system_, bool fullscreen) - : EmuWindow_SDL2{input_subsystem_, system_} { + : EmuWindow_SDL3{input_subsystem_, system_} { SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 6); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY); @@ -92,14 +98,13 @@ EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsyste std::string window_title = fmt::format("{} | {}-{}", Common::g_build_fullname, Common::g_scm_branch, Common::g_scm_desc); render_window = - SDL_CreateWindow(window_title.c_str(), - SDL_WINDOWPOS_UNDEFINED, // x position - SDL_WINDOWPOS_UNDEFINED, // y position - Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height, - SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); + SDL_CreateWindow(window_title.c_str(), Layout::ScreenUndocked::Width, + Layout::ScreenUndocked::Height, + SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | + SDL_WINDOW_HIGH_PIXEL_DENSITY); if (render_window == nullptr) { - LOG_CRITICAL(Frontend, "Failed to create SDL2 window! {}", SDL_GetError()); + LOG_CRITICAL(Frontend, "Failed to create SDL3 window! {}", SDL_GetError()); exit(1); } @@ -116,15 +121,15 @@ EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsyste core_context = CreateSharedContext(); if (window_context == nullptr) { - LOG_CRITICAL(Frontend, "Failed to create SDL2 GL context: {}", SDL_GetError()); + LOG_CRITICAL(Frontend, "Failed to create SDL3 GL context: {}", SDL_GetError()); exit(1); } if (core_context == nullptr) { - LOG_CRITICAL(Frontend, "Failed to create shared SDL2 GL context: {}", SDL_GetError()); + LOG_CRITICAL(Frontend, "Failed to create shared SDL3 GL context: {}", SDL_GetError()); exit(1); } - if (!gladLoadGLLoader(static_cast(SDL_GL_GetProcAddress))) { + if (!gladLoadGLLoader(static_cast(SDLGLGetProcAddress))) { LOG_CRITICAL(Frontend, "Failed to initialize GL functions! {}", SDL_GetError()); exit(1); } @@ -142,11 +147,11 @@ EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsyste Settings::LogSettings(); } -EmuWindow_SDL2_GL::~EmuWindow_SDL2_GL() { +EmuWindow_SDL3_GL::~EmuWindow_SDL3_GL() { core_context.reset(); - SDL_GL_DeleteContext(window_context); + SDL_GL_DestroyContext(window_context); } -std::unique_ptr EmuWindow_SDL2_GL::CreateSharedContext() const { +std::unique_ptr EmuWindow_SDL3_GL::CreateSharedContext() const { return std::make_unique(render_window); } diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h b/src/yuzu_cmd/emu_window/emu_window_sdl3_gl.h similarity index 70% rename from src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h rename to src/yuzu_cmd/emu_window/emu_window_sdl3_gl.h index 39346e7047..73a7d3d482 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl3_gl.h @@ -1,11 +1,15 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include +#include #include "core/frontend/emu_window.h" -#include "yuzu_cmd/emu_window/emu_window_sdl2.h" +#include "yuzu_cmd/emu_window/emu_window_sdl3.h" namespace Core { class System; @@ -15,11 +19,11 @@ namespace InputCommon { class InputSubsystem; } -class EmuWindow_SDL2_GL final : public EmuWindow_SDL2 { +class EmuWindow_SDL3_GL final : public EmuWindow_SDL3 { public: - explicit EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem_, Core::System& system_, + explicit EmuWindow_SDL3_GL(InputCommon::InputSubsystem* input_subsystem_, Core::System& system_, bool fullscreen); - ~EmuWindow_SDL2_GL(); + ~EmuWindow_SDL3_GL(); std::unique_ptr CreateSharedContext() const override; @@ -27,8 +31,6 @@ private: /// Whether the GPU and driver supports the OpenGL extension required bool SupportsRequiredGLExtensions(); - using SDL_GLContext = void*; - /// The OpenGL context associated with the window SDL_GLContext window_context; diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_null.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl3_null.cpp similarity index 67% rename from src/yuzu_cmd/emu_window/emu_window_sdl2_null.cpp rename to src/yuzu_cmd/emu_window/emu_window_sdl3_null.cpp index 4947080313..f5e4e19a30 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_null.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl3_null.cpp @@ -13,25 +13,25 @@ #include "common/logging.h" #include "common/scm_rev.h" #include "video_core/renderer_null/renderer_null.h" -#include "yuzu_cmd/emu_window/emu_window_sdl2_null.h" +#include "yuzu_cmd/emu_window/emu_window_sdl3_null.h" -#ifdef YUZU_USE_EXTERNAL_SDL2 +#ifdef YUZU_USE_EXTERNAL_SDL3 // Include this before SDL.h to prevent the external from including a dummy #define USING_GENERATED_CONFIG_H -#include +#include #endif -#include +#include -EmuWindow_SDL2_Null::EmuWindow_SDL2_Null(InputCommon::InputSubsystem* input_subsystem_, +EmuWindow_SDL3_Null::EmuWindow_SDL3_Null(InputCommon::InputSubsystem* input_subsystem_, Core::System& system_, bool fullscreen) - : EmuWindow_SDL2{input_subsystem_, system_} { + : EmuWindow_SDL3{input_subsystem_, system_} { const std::string window_title = fmt::format("Eden {} | {}-{} (Vulkan)", Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc); render_window = - SDL_CreateWindow(window_title.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, - Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height, - SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); + SDL_CreateWindow(window_title.c_str(), Layout::ScreenUndocked::Width, + Layout::ScreenUndocked::Height, + SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIGH_PIXEL_DENSITY); SetWindowIcon(); @@ -47,8 +47,8 @@ EmuWindow_SDL2_Null::EmuWindow_SDL2_Null(InputCommon::InputSubsystem* input_subs Common::g_scm_branch, Common::g_scm_desc); } -EmuWindow_SDL2_Null::~EmuWindow_SDL2_Null() = default; +EmuWindow_SDL3_Null::~EmuWindow_SDL3_Null() = default; -std::unique_ptr EmuWindow_SDL2_Null::CreateSharedContext() const { +std::unique_ptr EmuWindow_SDL3_Null::CreateSharedContext() const { return std::make_unique(); } diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_null.h b/src/yuzu_cmd/emu_window/emu_window_sdl3_null.h similarity index 58% rename from src/yuzu_cmd/emu_window/emu_window_sdl2_null.h rename to src/yuzu_cmd/emu_window/emu_window_sdl3_null.h index 35aee286df..7c046e72b8 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_null.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl3_null.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -6,7 +9,7 @@ #include #include "core/frontend/emu_window.h" -#include "yuzu_cmd/emu_window/emu_window_sdl2.h" +#include "yuzu_cmd/emu_window/emu_window_sdl3.h" namespace Core { class System; @@ -16,11 +19,11 @@ namespace InputCommon { class InputSubsystem; } -class EmuWindow_SDL2_Null final : public EmuWindow_SDL2 { +class EmuWindow_SDL3_Null final : public EmuWindow_SDL3 { public: - explicit EmuWindow_SDL2_Null(InputCommon::InputSubsystem* input_subsystem_, + explicit EmuWindow_SDL3_Null(InputCommon::InputSubsystem* input_subsystem_, Core::System& system, bool fullscreen); - ~EmuWindow_SDL2_Null() override; + ~EmuWindow_SDL3_Null() override; std::unique_ptr CreateSharedContext() const override; }; diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl3_vk.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl3_vk.cpp new file mode 100644 index 0000000000..9019cbbba0 --- /dev/null +++ b/src/yuzu_cmd/emu_window/emu_window_sdl3_vk.cpp @@ -0,0 +1,99 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include + +#include + +#include "common/logging.h" +#include "common/scm_rev.h" +#include "video_core/renderer_vulkan/renderer_vulkan.h" +#include "yuzu_cmd/emu_window/emu_window_sdl3_vk.h" + +#include +#include + +EmuWindow_SDL3_VK::EmuWindow_SDL3_VK(InputCommon::InputSubsystem* input_subsystem_, + Core::System& system_, bool fullscreen) + : EmuWindow_SDL3{input_subsystem_, system_} { + const std::string window_title = fmt::format("Eden {} | {}-{} (Vulkan)", + Common::g_build_name, + Common::g_scm_branch, + Common::g_scm_desc); + render_window = + SDL_CreateWindow(window_title.c_str(), Layout::ScreenUndocked::Width, + Layout::ScreenUndocked::Height, + SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIGH_PIXEL_DENSITY); + + const SDL_PropertiesID window_props = SDL_GetWindowProperties(render_window); + + SetWindowIcon(); + + if (fullscreen) { + Fullscreen(); + ShowCursor(false); + } + + if (void* hwnd = + SDL_GetPointerProperty(window_props, SDL_PROP_WINDOW_WIN32_HWND_POINTER, nullptr)) { + window_info.type = Core::Frontend::WindowSystemType::Windows; + window_info.render_surface = hwnd; + } else if (void* wl_display = + SDL_GetPointerProperty(window_props, SDL_PROP_WINDOW_WAYLAND_DISPLAY_POINTER, + nullptr); + wl_display != nullptr) { + void* wl_surface = + SDL_GetPointerProperty(window_props, SDL_PROP_WINDOW_WAYLAND_SURFACE_POINTER, nullptr); + if (wl_surface == nullptr) { + LOG_CRITICAL(Frontend, "Wayland surface is unavailable"); + std::exit(EXIT_FAILURE); + } + window_info.type = Core::Frontend::WindowSystemType::Wayland; + window_info.display_connection = wl_display; + window_info.render_surface = wl_surface; + } else if (void* x11_display = + SDL_GetPointerProperty(window_props, SDL_PROP_WINDOW_X11_DISPLAY_POINTER, + nullptr); + x11_display != nullptr) { + const auto x11_window = + SDL_GetNumberProperty(window_props, SDL_PROP_WINDOW_X11_WINDOW_NUMBER, 0); + if (x11_window == 0) { + LOG_CRITICAL(Frontend, "X11 window handle is unavailable"); + std::exit(EXIT_FAILURE); + } + window_info.type = Core::Frontend::WindowSystemType::X11; + window_info.display_connection = x11_display; + window_info.render_surface = reinterpret_cast(static_cast(x11_window)); + } else if (SDL_GetPointerProperty(window_props, SDL_PROP_WINDOW_COCOA_WINDOW_POINTER, + nullptr) != nullptr) { + window_info.type = Core::Frontend::WindowSystemType::Cocoa; + window_info.render_surface = SDL_Metal_CreateView(render_window); + } else if (void* android_window = + SDL_GetPointerProperty(window_props, SDL_PROP_WINDOW_ANDROID_WINDOW_POINTER, + nullptr); + android_window != nullptr) { + window_info.type = Core::Frontend::WindowSystemType::Android; + window_info.render_surface = android_window; + } else { + LOG_CRITICAL(Frontend, "Unable to determine native window backend from SDL properties"); + std::exit(EXIT_FAILURE); + } + + OnResize(); + OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); + SDL_PumpEvents(); + LOG_INFO(Frontend, "Eden Version: {} | {}-{} (Vulkan)", Common::g_build_name, + Common::g_scm_branch, Common::g_scm_desc); +} + +EmuWindow_SDL3_VK::~EmuWindow_SDL3_VK() = default; + +std::unique_ptr EmuWindow_SDL3_VK::CreateSharedContext() const { + return std::make_unique(); +} diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h b/src/yuzu_cmd/emu_window/emu_window_sdl3_vk.h similarity index 59% rename from src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h rename to src/yuzu_cmd/emu_window/emu_window_sdl3_vk.h index 9467d164a4..5eb2626dd7 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl3_vk.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -6,7 +9,7 @@ #include #include "core/frontend/emu_window.h" -#include "yuzu_cmd/emu_window/emu_window_sdl2.h" +#include "yuzu_cmd/emu_window/emu_window_sdl3.h" namespace Core { class System; @@ -16,11 +19,11 @@ namespace InputCommon { class InputSubsystem; } -class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 { +class EmuWindow_SDL3_VK final : public EmuWindow_SDL3 { public: - explicit EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem_, Core::System& system, + explicit EmuWindow_SDL3_VK(InputCommon::InputSubsystem* input_subsystem_, Core::System& system, bool fullscreen); - ~EmuWindow_SDL2_VK() override; + ~EmuWindow_SDL3_VK() override; std::unique_ptr CreateSharedContext() const override; }; diff --git a/src/yuzu_cmd/sdl_config.cpp b/src/yuzu_cmd/sdl_config.cpp index 136043dc9e..b06a5a698c 100644 --- a/src/yuzu_cmd/sdl_config.cpp +++ b/src/yuzu_cmd/sdl_config.cpp @@ -6,7 +6,7 @@ // SDL will break our main function in yuzu-cmd if we don't define this before adding SDL.h #define SDL_MAIN_HANDLED -#include +#include #include "common/logging.h" #include "input_common/main.h" diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index 54cc832bf2..d20c126159 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -30,12 +30,12 @@ #include "network/network.h" #include "sdl_config.h" #include "video_core/renderer_base.h" -#include "yuzu_cmd/emu_window/emu_window_sdl2.h" +#include "yuzu_cmd/emu_window/emu_window_sdl3.h" #ifdef HAS_OPENGL -#include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h" +#include "yuzu_cmd/emu_window/emu_window_sdl3_gl.h" #endif -#include "yuzu_cmd/emu_window/emu_window_sdl2_null.h" -#include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h" +#include "yuzu_cmd/emu_window/emu_window_sdl3_null.h" +#include "yuzu_cmd/emu_window/emu_window_sdl3_vk.h" #ifdef _WIN32 // windows.h needs to be included before shellapi.h @@ -349,20 +349,20 @@ int main(int argc, char** argv) { // Apply the command line arguments system.ApplySettings(); - std::unique_ptr emu_window; + std::unique_ptr emu_window; switch (Settings::values.renderer_backend.GetValue()) { #ifdef HAS_OPENGL case Settings::RendererBackend::OpenGL_GLSL: case Settings::RendererBackend::OpenGL_GLASM: case Settings::RendererBackend::OpenGL_SPIRV: - emu_window = std::make_unique(&input_subsystem, system, fullscreen); + emu_window = std::make_unique(&input_subsystem, system, fullscreen); break; #endif case Settings::RendererBackend::Vulkan: - emu_window = std::make_unique(&input_subsystem, system, fullscreen); + emu_window = std::make_unique(&input_subsystem, system, fullscreen); break; case Settings::RendererBackend::Null: - emu_window = std::make_unique(&input_subsystem, system, fullscreen); + emu_window = std::make_unique(&input_subsystem, system, fullscreen); break; default: LOG_CRITICAL(Frontend, "Invalid renderer backend"); diff --git a/tools/README.md b/tools/README.md index 767892e7ac..d4f5aa1baa 100644 --- a/tools/README.md +++ b/tools/README.md @@ -1,12 +1,18 @@ # Tools -Tools for Eden and other subprojects. +Tools for Eden and other subprojects. When adding new scripts please use `#!/bin/sh -e` or `#!/usr/bin/env ` (for `.py`, `.rb`, or `.perl`). Keep scripts POSIX compliant (i.e not require hard `bash` to run, just plain old `sh`). ## Third-Party - [CPMUtil Scripts](./cpm) -## Eden +## Binaries + +- `maxwell-spirv`: Converts Maxwell shaders (dumped from `.ash` files) into SPIR-V code (emitted into STDOUT). +- `maxwell-disas`: Dumb raw Maxwell dissasembler. +- `maxwell-ir`: Dump generated IR of Maxwell shaders. + +## Scripts - `generate_converters.py`: Generates converters for given formats of textures (C++ helper). - `svc_generator.py`: Generates the files `src/core/hle/kernel/svc.cpp` and `src/core/hle/kernel/svc.h` based off prototypes. @@ -22,6 +28,8 @@ Tools for Eden and other subprojects. - `clang-format.sh`: Runs `clang-format` on the entire codebase. * Requires: clang - `find-unused-strings.sh`: Find any unused strings in the Android app (XML -> Kotlin). +- `cpp-lint.sh`: Homemade dumb C++ linter. +- `fuzzsettings.cpp`: Fuzz settings files. ## Android It's recommended to run these scritps after almost any Android change, as they are relatively fast and important both for APK bloat and CI. diff --git a/tools/clang-format.sh b/tools/clang-format.sh index e2857d9723..ca5e6fce39 100755 --- a/tools/clang-format.sh +++ b/tools/clang-format.sh @@ -1,4 +1,4 @@ -#! /bin/sh +#!/bin/sh -e # SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project # SPDX-License-Identifier: GPL-3.0-or-later diff --git a/tools/cpm/common.sh b/tools/cpm/common.sh index 97a2fed003..09da6358be 100755 --- a/tools/cpm/common.sh +++ b/tools/cpm/common.sh @@ -3,35 +3,43 @@ # SPDX-FileCopyrightText: Copyright 2026 crueter # SPDX-License-Identifier: LGPL-3.0-or-later -################################## -# CHANGE THESE FOR YOUR PROJECT! # -################################## +: "${CPM_SOURCE_CACHE:=$PWD/.cache/cpm}" +: "${CPMUTIL_PATCH_DIR:=$PWD/.patch}" -# TODO: cache cpmfile defs +# TODO: cache cpmfile defs? + +cmd_exists() { + command -v "$1" >/dev/null 2>&1 +} must_install() { for cmd in "$@"; do - command -v "$cmd" >/dev/null 2>&1 || { echo "-- $cmd must be installed" && exit 1; } + cmd_exists "$cmd" || { echo "-- $cmd must be installed" && exit 1; } done } -must_install jq find mktemp tar 7z unzip sha512sum git patch curl xargs +# Random integer between 100000 and 999999 +_randint() { + awk 'BEGIN { srand(); print int(100000 + rand() * 900000) }' +} -# How many levels to go (3 is 2 subdirs max) -MAXDEPTH=3 +# Use mktemp if available, use a local temp dir otherwise +make_temp_dir() { + if cmd_exists mktemp; then + mktemp -d + else + TMP="$PWD/.cpm/tmp-$(_randint)" + mkdir -p "$TMP" + echo "$TMP" + fi +} -# For your project you'll want to change this to define what dirs you have cpmfiles in -# Remember to account for the MAXDEPTH variable! -# Adding ./ before each will help to remove duplicates -CPMFILES=$(find . -maxdepth "$MAXDEPTH" -name cpmfile.json | sort | uniq) +# must_install jq find mktemp tar 7z unzip sha512sum git patch curl -# shellcheck disable=SC2016 -PACKAGES=$(echo "$CPMFILES" | xargs jq -s 'reduce .[] as $item ({}; . * $item)') - -LIBS=$(echo "$PACKAGES" | jq -j 'keys_unsorted | join(" ")') - -export PACKAGES -export CPMFILES -export LIBS -export DIRS -export MAXDEPTH +if [ ! -s cpmfile.json ]; then + # TODO: actually make it a no-op + echo "-- Warning: cpmfile.json does not exist or is empty, most commands will be no-ops" +else + LIBS=$(jq -j 'keys_unsorted | join(" ")' cpmfile.json) + export LIBS +fi diff --git a/tools/cpm/format.sh b/tools/cpm/format.sh index df65f5a000..c1452c4cb2 100755 --- a/tools/cpm/format.sh +++ b/tools/cpm/format.sh @@ -1,9 +1,9 @@ #!/bin/sh -e -# SPDX-FileCopyrightText: Copyright 2025 crueter +# SPDX-FileCopyrightText: Copyright 2026 crueter # SPDX-License-Identifier: LGPL-3.0-or-later -for file in $CPMFILES; do - jq --indent 4 <"$file" >"$file".new - mv "$file".new "$file" -done +jq --indent 4 -S cpmfile.json.new +mv cpmfile.json.new cpmfile.json + +# TODO: Run some sanity checks e.g. patches exist, etc. diff --git a/tools/cpm/migrate.sh b/tools/cpm/migrate.sh index b24bbfbbbf..8be2b1786c 100755 --- a/tools/cpm/migrate.sh +++ b/tools/cpm/migrate.sh @@ -33,7 +33,7 @@ for i in $SUBMODULES; do '{ ($name): { sha: $sha, - git_version: $ver, + version: $ver, repo: $repo } + (if $host != "" then {git_host: $host} else {} end) }') diff --git a/tools/cpm/package.sh b/tools/cpm/package.sh index 474a2f351e..bf2f3feaa7 100755 --- a/tools/cpm/package.sh +++ b/tools/cpm/package.sh @@ -18,8 +18,11 @@ Commands: add Add a new package rm Remove a package version Change the version of a package - which Find which cpmfile a package is defined in + which Check if a package is defined download Get the download URL for a package + dir Get the local directory for a package + reset Reset a fetched package to its original state + patch Create an in-tree patch based on local modifications EOF @@ -31,7 +34,7 @@ export SCRIPTS while :; do case "$1" in - hash | update | fetch | add | rm | version | which | download) + hash | update | fetch | add | rm | version | which | download | reset | patch | dir) cmd="$1" shift "$SCRIPTS/$cmd".sh "$@" diff --git a/tools/cpm/package/add.sh b/tools/cpm/package/add.sh index f8071ee1bd..8531f524fb 100755 --- a/tools/cpm/package/add.sh +++ b/tools/cpm/package/add.sh @@ -7,31 +7,20 @@ RETURN=0 usage() { cat < Use the specified cpmfile instead of the root cpmfile - Note that you are still responsible for integrating this into your CMake. EOF - exit $RETURN + exit "$RETURN" } die() { echo "-- $*" >&2 - exit 1 -} - -_cpmfile() { - [ -n "$1" ] || die "You must specify a valid cpmfile." - CPMFILE="$1" + RETURN=1 usage } while :; do @@ -44,21 +33,11 @@ while :; do opt=$(echo "$opt" | cut -c2-) case "$char" in - t) TAG=1 ;; - c) - _cpmfile "$2" - shift - ;; h) usage ;; *) die "Invalid option -$char" ;; esac done ;; - --tag) TAG=1 ;; - --cpmfile) - _cpmfile "$2" - shift - ;; --help) usage ;; "$0") break ;; "") break ;; @@ -68,12 +47,8 @@ while :; do shift done -: "${CPMFILE:=$PWD/cpmfile.json}" - [ -n "$PKG" ] || die "You must specify a package name." export PKG -export CPMFILE -export TAG "$SCRIPTS"/util/interactive.sh diff --git a/tools/cpm/package/dir.sh b/tools/cpm/package/dir.sh new file mode 100755 index 0000000000..82a220509d --- /dev/null +++ b/tools/cpm/package/dir.sh @@ -0,0 +1,57 @@ +#!/bin/sh -e + +# SPDX-FileCopyrightText: Copyright 2026 crueter +# SPDX-License-Identifier: LGPL-3.0-or-later + +# shellcheck disable=SC1091 +. "$SCRIPTS"/../common.sh + +usage() { + cat </dev/null - ;; - *.tar*) - tar xf "$OUTFILE" >/dev/null - ;; - *.zip) - unzip "$OUTFILE" >/dev/null - ;; - esac - - # basically if only one real item exists at the top we just move everything from there - # since github and some vendors hate me - DIRS=$(find . -maxdepth 1 -type d -o -type f) - - # thanks gnu - if [ "$(echo "$DIRS" | wc -l)" -eq 2 ]; then - SUBDIR=$(find . -maxdepth 1 -type d -not -name ".") - mv "$SUBDIR"/* "$OUTDIR" - mv "$SUBDIR"/.* "$OUTDIR" 2>/dev/null || true - rmdir "$SUBDIR" - else - mv ./* "$OUTDIR" - mv ./.* "$OUTDIR" 2>/dev/null || true - fi - - cd "$OUTDIR" - - if echo "$JSON" | grep -e "patches" >/dev/null; then - PATCHES=$(echo "$JSON" | jq -r '.patches | join(" ")') - for patch in $PATCHES; do - patch --binary -p1 <"$ROOTDIR/.patch/$PACKAGE/$patch" - done - fi - - cd "$PREVDIR" -} - -ci_package() { - [ "$REPO" != null ] || { echo "-- ! No repo defined" && return; } - - echo "-- CI package $PACKAGE_NAME" - - for platform in \ - windows-amd64 windows-arm64 \ - mingw-amd64 mingw-arm64 \ - android-aarch64 android-x86_64 \ - solaris-amd64 freebsd-amd64 openbsd-amd64 \ - linux-amd64 linux-aarch64 \ - macos-universal; do - echo "-- * platform $platform" - - case $DISABLED in - *"$platform"*) - echo "-- * -- disabled" - continue - ;; - esac - - FILENAME="${NAME}-${platform}-${VERSION}.${EXT}" - DOWNLOAD="https://$GIT_HOST/${REPO}/releases/download/v${VERSION}/${FILENAME}" - KEY="$platform-$VERSION" - - LOWER_PACKAGE=$(echo "$PACKAGE_NAME" | tr '[:upper:]' '[:lower:]') - OUTDIR="${CPM_SOURCE_CACHE}/${LOWER_PACKAGE}/${KEY}" - [ -d "$OUTDIR" ] && continue - - HASH_ALGO=$(echo "$JSON" | jq -r ".hash_algo") - [ "$HASH_ALGO" != null ] || HASH_ALGO=sha512 - - HASH_SUFFIX="${HASH_ALGO}sum" - HASH_URL="${DOWNLOAD}.${HASH_SUFFIX}" - - HASH=$(curl "$HASH_URL" -sS -q -L -o -) - - download_package - done -} +# shellcheck disable=SC1091 +. "$SCRIPTS/util/fetch.sh" usage() { cat <&2 + RETURN=1 usage +} + +while :; do + case "$1" in + -h | --help) usage ;; + "$0") break ;; + "") break ;; + *) + if [ -n "$PACKAGE" ]; then + die "You may only specify one package." + else + PACKAGE="$1" + fi + ;; + esac + + shift +done + +[ -n "$PACKAGE" ] || usage + +unset JSON +export PACKAGE + +# shellcheck disable=SC1091 +. "$SCRIPTS"/vars.sh + +if [ "$CI" = true ]; then + die "CI packages do not support in-tree patching" +fi + +patch_dir="$PWD/.patch/$PACKAGE" +TMP=$(make_temp_dir) + +tmp_package="${TMP}/${LOWER_PACKAGE}/${KEY}" + +# First fetch the package... + +# shellcheck disable=SC1091 +. "$SCRIPTS"/util/fetch.sh + +local_dir="$CPM_SOURCE_CACHE/$LOWER_PACKAGE/$KEY" + +if [ ! -d "$local_dir" ]; then + echo "-- $PACKAGE_NAME is not fetched locally" >&2 + exit 1 +fi + +src_dir="$tmp_package/$LOWER_PACKAGE/$KEY" + +CPM_SOURCE_CACHE="$tmp_package" fetch_package + +mkdir -p "$patch_dir" + +existing=$(find "$patch_dir" -maxdepth 1 -type f -name '[0-9][0-9][0-9][0-9]-*.patch' 2>/dev/null | sort | tail -1) +if [ -n "$existing" ]; then + last_num=$(basename "$existing" | cut -c1-4) + last_num=$(echo "$last_num" | sed 's/^0*//') + last_num="${last_num:-0}" + next_num=$(printf "%04d" $((last_num + 1))) +else + next_num="0001" +fi + +echo "-- Creating patch for $PACKAGE..." + +orig_dir="$PWD" +if cmd_exists git; then + cd "$src_dir" + + git init >/dev/null 2>&1 + git add -A >/dev/null 2>&1 + git commit -m "base" >/dev/null 2>&1 + + git --work-tree="$local_dir" add -A #>/dev/null 2>&1 + + if git diff --cached --quiet; then + echo "-- No differences found between local and source" + cd "$orig_dir" + rm -rf "$TMP" + exit 0 + fi + + if ! git commit -v; then + echo "-- Patch cancelled" >&2 + cd "$orig_dir" + rm -rf "$TMP" + exit 1 + fi + + description=$(git log -1 --format="%s") + + git format-patch -1 HEAD --stdout >"$patch_dir/tmp.patch" + rm -rf .git + + cd "$orig_dir" + unset orig_dir +else + if diff -ruN "$src_dir" "$local_dir" >/dev/null 2>&1; then + echo "-- No differences found between local and source" + rm -rf "$TMP" + exit 0 + fi + + printf -- "-- Enter a patch description: " + if ! read -r description; then + echo "" >&2 + echo "-- Patch cancelled" >&2 + rm -rf "$TMP" + exit 1 + fi + + mkdir -p "$TMP/patchdir/a" "$TMP/patchdir/b" + cp -r "$src_dir/." "$TMP/patchdir/a/" + cp -r "$local_dir/." "$TMP/patchdir/b/" + cd "$TMP/patchdir" + { + echo "Subject: [PATCH] $description" | fold -s -w 80 + echo "---" + diff -ruN a b || true + } >"$patch_dir/tmp.patch" + + cd "$orig_dir" + unset orig_dir +fi + +rm -rf "$TMP" + +if [ ! -s "$patch_dir/tmp.patch" ]; then + rm -f "$patch_dir/tmp.patch" + echo "-- No differences found between local and source for $PACKAGE" + exit 0 +fi + +name_part=$(printf "%s" "$description" | sed 's/^[0-9]\{4\}-//' | sed 's/ /-/g; s/[^a-zA-Z0-9-]//g; s/--*/-/g; s/^-//; s/-$//') +[ -n "$name_part" ] || name_part="patch" + +# max is 60 chars, minus 5 for number, minus 6 for patch suffix +max_name_len=49 +name_part=$(printf "%s" "$name_part" | cut -c1-"$max_name_len" | sed 's/-$//') + +patch_name="${next_num}-${name_part}.patch" + +mv "$patch_dir/tmp.patch" "$patch_dir/$patch_name" + +NEW_JSON=$(echo "$JSON" | jq ".patches += [\"$patch_name\"]") +"$SCRIPTS"/util/replace.sh "$PACKAGE" "$NEW_JSON" + +echo "-- Patch created at $patch_dir/$patch_name" diff --git a/tools/cpm/package/reset.sh b/tools/cpm/package/reset.sh new file mode 100755 index 0000000000..6e98c00d48 --- /dev/null +++ b/tools/cpm/package/reset.sh @@ -0,0 +1,56 @@ +#!/bin/sh -e + +# SPDX-FileCopyrightText: Copyright 2026 crueter +# SPDX-License-Identifier: LGPL-3.0-or-later + +# shellcheck disable=SC1091 +. "$SCRIPTS"/../common.sh + +usage() { + cat <"$JSON".tmp - mv "$JSON".tmp "$JSON" - echo "-- Removed $pkg from $JSON" || : + jq --indent 4 "del(.\"$pkg\")" cpmfile.json >cpmfile.json.new + mv cpmfile.json.new cpmfile.json + echo "-- Removed $pkg" done diff --git a/tools/cpm/package/update.sh b/tools/cpm/package/update.sh index 92f4f423a9..96e143f662 100755 --- a/tools/cpm/package/update.sh +++ b/tools/cpm/package/update.sh @@ -63,8 +63,9 @@ done [ -n "$packages" ] || usage for pkg in $packages; do - PACKAGE="$pkg" - export PACKAGE + unset JSON + export PACKAGE="$pkg" + # shellcheck disable=SC1091 . "$SCRIPTS"/vars.sh @@ -82,9 +83,15 @@ for pkg in $packages; do echo "-- Package $PACKAGE" - # TODO(crueter): Support for Forgejo updates w/ forgejo_token - # Use gh-cli to avoid ratelimits lmao - TAGS=$(gh api --method GET "/repos/$REPO/tags") + # TODO(crueter): Support for forgejo_token? + endpoint="/repos/$REPO/tags" + if command -v gh >/dev/null 2>&1; then + TAGS=$(gh api --method GET "$endpoint") + elif [ "$GIT_HOST" = github.com ]; then + TAGS=$(curl -sfL "https://api.github.com$endpoint") + else + TAGS=$(curl -sfL "https://$GIT_HOST/api/v1$endpoint") + fi # filter out some commonly known annoyances # TODO add more @@ -97,7 +104,6 @@ for pkg in $packages; do filter_out yotta # mbedtls - # ???????????????????????????????? filter_out vksc # ignore betas/alphas (remove if needed) @@ -108,6 +114,12 @@ for pkg in $packages; do # Add package-specific overrides here, e.g. here for fmt: [ "$PACKAGE" != fmt ] || filter_out v0.11 + # Or for OpenSSL: + if [ "$PACKAGE" = openssl ]; then + filter_out rsaref + filter_in "openssl-" + fi + LATEST=$(echo "$TAGS" | jq -r '.[0].name') if [ "$LATEST" = "null" ] || @@ -116,45 +128,47 @@ for pkg in $packages; do continue fi + # TODO: This is identical to version.sh + if [ "$HAS_REPLACE" = "true" ]; then # this just extracts the tag prefix VERSION_PREFIX=$(echo "$ORIGINAL_TAG" | cut -d"%" -f1) - # then we strip out the prefix from the new tag, and make that our new git_version + # then we strip out the prefix from the new tag, and make that our new version if [ -z "$VERSION_PREFIX" ]; then - NEW_GIT_VERSION="$LATEST" + NEW_VERSION="$LATEST" else - NEW_GIT_VERSION=$(echo "$LATEST" | sed "s/$VERSION_PREFIX//g") + NEW_VERSION=$(echo "$LATEST" | sed "s/$VERSION_PREFIX//g") fi else - NEW_GIT_VERSION="$LATEST" + NEW_VERSION="$LATEST" fi _commit="$_commit -* $PACKAGE: $GIT_VERSION -> $NEW_GIT_VERSION" +* $PACKAGE: $VERSION -> $NEW_VERSION" echo "-- * Version $LATEST available, current is $TAG" if [ "$UPDATE" = "true" ]; then if [ "$HAS_REPLACE" = "true" ]; then - NEW_JSON=$(echo "$JSON" | jq ".git_version = \"$NEW_GIT_VERSION\"") + JSON=$(echo "$JSON" | jq ".version = \"$NEW_VERSION\"") else - NEW_JSON=$(echo "$JSON" | jq ".tag = \"$NEW_GIT_VERSION\"") + JSON=$(echo "$JSON" | jq ".tag = \"$NEW_VERSION\"") fi - "$SCRIPTS"/util/replace.sh "$PACKAGE" "$NEW_JSON" - echo "-- * -- Updating hash" - export UPDATE - QUIET=true "$SCRIPTS"/util/fix-hash.sh "$PACKAGE" + # shellcheck disable=SC1091 + . "$SCRIPTS"/vars.sh + HASH=$("$SCRIPTS"/util/url-hash.sh "$DOWNLOAD") + JSON=$(echo "$JSON" | jq ".hash = \"$HASH\"") + + "$SCRIPTS"/util/replace.sh "$PACKAGE" "$JSON" fi done if [ "$UPDATE" = "true" ] && [ "$COMMIT" = "true" ] && [ -n "$_commit" ]; then - for file in $CPMFILES; do - git add "$file" - done + git add "cpmfile.json" git commit -m "Update dependencies $_commit" fi diff --git a/tools/cpm/package/util/fetch.sh b/tools/cpm/package/util/fetch.sh new file mode 100755 index 0000000000..7db568f3e6 --- /dev/null +++ b/tools/cpm/package/util/fetch.sh @@ -0,0 +1,134 @@ +#!/bin/sh -e + +# SPDX-FileCopyrightText: Copyright 2026 crueter +# SPDX-License-Identifier: LGPL-3.0-or-later + +# shellcheck disable=SC1091 +. "$SCRIPTS"/../common.sh + +download_package() { + mkdir -p "$CPM_SOURCE_CACHE" + + tmp=$(make_temp_dir) + + FILENAME=$(basename "$DOWNLOAD") + + OUTFILE="$tmp/$FILENAME" + + OUTDIR="${CPM_SOURCE_CACHE}/${LOWER_PACKAGE}/${KEY}" + if [ -d "$OUTDIR" ]; then return; fi + + curl "$DOWNLOAD" -sS -L -o "$OUTFILE" + + ACTUAL_HASH=$(sha512sum "$OUTFILE" | cut -d" " -f1) + if [ "$ACTUAL_HASH" != "$HASH" ]; then + echo "!! $FILENAME did not match expected hash; expected $HASH but got $ACTUAL_HASH" >&2 + exit 1 + fi + + TMPDIR="$tmp/extracted" + mkdir -p "$OUTDIR" + mkdir -p "$TMPDIR" + + PREVDIR="$PWD" + mkdir -p "$TMPDIR" + cd "$TMPDIR" + + case "$FILENAME" in + *.7z) + must_install 7z + 7z x "$OUTFILE" >/dev/null + ;; + *.tar*) + # TODO: Extensions + must_install tar + tar xf "$OUTFILE" >/dev/null + ;; + *.zip) + must_install unzip + unzip "$OUTFILE" >/dev/null + ;; + esac + + # basically if only one real item exists at the top we just move everything from there + # since github and some vendors hate me + DIRS=$(find . -maxdepth 1 -type d -o -type f) + + # thanks gnu + if [ "$(echo "$DIRS" | wc -l)" -eq 2 ]; then + SUBDIR=$(find . -maxdepth 1 -type d -not -name ".") + mv "$SUBDIR"/* "$OUTDIR" + mv "$SUBDIR"/.* "$OUTDIR" 2>/dev/null || true + rmdir "$SUBDIR" + else + mv ./* "$OUTDIR" + mv ./.* "$OUTDIR" 2>/dev/null || true + fi + + cd "$OUTDIR" + + # TODO: Common custom patch/source cache dirs + if echo "$JSON" | grep -e "patches" >/dev/null; then + PATCHES=$(echo "$JSON" | jq -r '.patches | join(" ")') + for patch in $PATCHES; do + abs_patch="$CPMUTIL_PATCH_DIR/$PACKAGE/$patch" + if [ ! -f "$abs_patch" ]; then + echo "-- * Attempted to apply nonexistent patch $patch!" + continue + fi + + echo "-- * Applying patch $patch" + patch --binary -p1 <"$abs_patch" + done + fi + + cd "$PREVDIR" + + rm -rf "$tmp" +} + +# TODO: individual platform fetch? +ci_package() { + [ "$REPO" != null ] || { echo "-- ! No repo defined" && return; } + mkdir -p "$CPM_SOURCE_CACHE" + + echo "-- CI package $PACKAGE_NAME" + + for platform in \ + windows-amd64 windows-arm64 \ + mingw-amd64 mingw-arm64 \ + android-aarch64 android-x86_64 \ + linux-amd64 linux-aarch64 \ + macos-universal ios-aarch64; do + echo "-- * platform $platform" + + case $DISABLED in + *"$platform"*) + echo "-- * -- disabled" + continue + ;; + esac + + FILENAME="${NAME}-${platform}-${VERSION}.${EXT}" + DOWNLOAD="https://$GIT_HOST/${REPO}/releases/download/v${VERSION}/${FILENAME}" + KEY="$platform-$VERSION" + + OUTDIR="${CPM_SOURCE_CACHE}/${LOWER_PACKAGE}/${KEY}" + [ -d "$OUTDIR" ] && continue + + HASH_URL="${DOWNLOAD}.sha512sum" + + HASH=$(curl "$HASH_URL" -sS -q -L -o -) + + download_package + done +} + +fetch_package() { + if [ "$CI" = "true" ]; then + ci_package + else + echo "-- Downloading regular package $PACKAGE, with key $KEY, from $DOWNLOAD" + download_package + fi +} diff --git a/tools/cpm/package/util/fix-hash.sh b/tools/cpm/package/util/fix-hash.sh index 3a69f0aaa7..476b15af89 100755 --- a/tools/cpm/package/util/fix-hash.sh +++ b/tools/cpm/package/util/fix-hash.sh @@ -9,18 +9,10 @@ # re-read json files # shellcheck disable=SC2016 -PACKAGES=$(echo "$CPMFILES" | xargs jq -s 'reduce .[] as $item ({}; . * $item)') -export PACKAGES - . "$SCRIPTS"/vars.sh [ "$CI" = null ] || exit 0 -[ "$HASH_URL" = null ] || exit 0 -[ "$HASH_SUFFIX" = null ] || exit 0 - -[ "$HASH" != null ] || { echo "-- * Package has no hash specified" && exit 0; } - ACTUAL=$("$SCRIPTS"/util/url-hash.sh "$DOWNLOAD") if [ "$ACTUAL" != "$HASH" ] && [ "$QUIET" != true ]; then diff --git a/tools/cpm/package/util/interactive.sh b/tools/cpm/package/util/interactive.sh index 99db77e20d..1f5a16aa07 100755 --- a/tools/cpm/package/util/interactive.sh +++ b/tools/cpm/package/util/interactive.sh @@ -86,6 +86,18 @@ optional "Additional find_package arguments, space-separated" \ FIND_ARGS="$reply" +optional "Git host (default: github.com)" \ + "The hostname of the Git server, if not GitHub (e.g. codeberg.org, git.crueter.xyz)" + +GIT_HOST="$reply" + +required "Numeric version of the bundled package" \ + "The semantic version of the bundled package. This is only used for package identification, +and if you use tag/artifact fetching. Do not input the entire tag here; for example, if you're using +tag v1.3.0, then set this to 1.3.0 and set the tag to v%VERSION%." + +VERSION="$reply" + optional "Is this a CI package? [y/N]" \ "Yes if the package is a prebuilt binary distribution (e.g. crueter-ci), no if the package is built from source if it's bundled." @@ -96,27 +108,31 @@ case "$reply" in esac if [ "$CI" = "false" ]; then - optional "Git host (default: github.com)" \ - "The hostname of the Git server, if not GitHub (e.g. codeberg.org, git.crueter.xyz)" + while :; do + required "Use tag or commit sha versioning? [tag/sha]" \ + "Tag versioning is compatible with auto-updating. +Use sha versioning for projects with improper tagging practices" - GIT_HOST="$reply" + if [ "$reply" = "tag" ]; then + TAG=1 + break + elif [ "$reply" = "sha" ]; then + SHA=1 + break + else + echo "-- Invalid choice $reply" + fi + done if [ "$TAG" = "1" ]; then - required "Numeric version of the bundled package" \ - "The semantic version of the bundled package. This is only used for package identification, -and if you use tag/artifact fetching. Do not input the entire tag here; for example, if you're using -tag v1.3.0, then set this to 1.3.0 and set the tag to v%VERSION%." - - GIT_VERSION="$reply" - - optional "Name of the upstream tag. %VERSION% is replaced by the numeric version (default: %VERSION%)" \ + optional "Name of the upstream tag. %VERSION% is replaced by the numeric version ($VERSION) (default: %VERSION%)" \ "Most commonly this will be something like v%VERSION% or release-%VERSION%, or just %VERSION%." TAGNAME="$reply" [ -n "$TAGNAME" ] || TAGNAME="%VERSION%" optional "Name of the release artifact to download, if applicable. --- %VERSION% is replaced by the numeric version and %TAG% is replaced by the tag name" \ +-- %VERSION% is replaced by the numeric version ($VERSION) and %TAG% is replaced by the tag name" \ "Download the specified artifact from the release with the previously specified tag. If unspecified, the source code at the specified tag will be used instead." @@ -134,11 +150,6 @@ should be set in CMake with AddJsonPackage's OPTIONS parameter." OPTIONS="$reply" else - required "Version of the CI package (e.g. 3.6.0-9eff87adb1)" \ - "CI artifacts are stored as --.tar.zst. This option controls the version." - - VERSION="$reply" - required "Name of the CI artifact" \ "CI artifacts are stored as --.tar.zst. This option controls the name." @@ -149,9 +160,8 @@ else windows-amd64 windows-arm64 mingw-amd64 mingw-arm64 android-aarch64 android-x86_64 -solaris-amd64 freebsd-amd64 openbsd-amd64 linux-amd64 linux-aarch64 -macos-universal" +macos-universal ios-aarch64" DISABLED_PLATFORMS="$reply" fi @@ -163,11 +173,15 @@ jq_input='{repo: "'"$REPO"'"}' [ -z "$PACKAGE" ] || jq_input="$jq_input + {package: \"$PACKAGE\"}" [ -z "$MIN_VERSION" ] || jq_input="$jq_input + {min_version: \"$MIN_VERSION\"}" [ -z "$FIND_ARGS" ] || jq_input="$jq_input + {find_args: \"$FIND_ARGS\"}" +jq_input="$jq_input + {version: \"$VERSION\"}" + +if [ -n "$GIT_HOST" ] && [ "$GIT_HOST" != "github.com" ]; then + jq_input="$jq_input + {git_host: \"$GIT_HOST\"}" +fi if [ "$CI" = "true" ]; then jq_input="$jq_input + { ci: true, - version: \"$VERSION\", artifact: \"$ARTIFACT\" }" @@ -186,32 +200,29 @@ else jq_input="$jq_input + {options: $options_json}" fi - # Git host - if [ -n "$GIT_HOST" ] && [ "$GIT_HOST" != "github.com" ]; then - jq_input="$jq_input + {git_host: \"$GIT_HOST\"}" - fi - # versioning stuff - if [ -n "$GIT_VERSION" ]; then - jq_input="$jq_input + {git_version: \"$GIT_VERSION\"}" - [ -z "$TAGNAME" ] || jq_input="$jq_input + {tag: \"$TAGNAME\"}" + if [ "$TAG" = 1 ]; then + jq_input="$jq_input + {tag: \"$TAGNAME\"}" [ -z "$ARTIFACT" ] || jq_input="$jq_input + {artifact: \"$ARTIFACT\"}" else jq_input="$jq_input + {sha: \"$SHA\"}" fi fi -new_json=$(jq -n "$jq_input") +JSON=$(jq -n "$jq_input") -jq --arg key "$PKG" --argjson new "$new_json" \ - '.[$key] = $new' "$CPMFILE" --indent 4 >"${CPMFILE}.tmp" && - mv "${CPMFILE}.tmp" "$CPMFILE" +# shellcheck disable=SC1091 +. "$SCRIPTS"/vars.sh -# now correct the hash if [ "$CI" != true ]; then - # shellcheck disable=SC1091 - . "$ROOTDIR"/common.sh - QUIET=true UPDATE=true "$SCRIPTS"/util/fix-hash.sh "$PKG" + HASH=$("$SCRIPTS"/util/url-hash.sh "$DOWNLOAD") + JSON=$(echo "$JSON" | jq ".hash = \"$HASH\"") fi -echo "Added package $PKG to $CPMFILE. Include it in your project with AddJsonPackage($PKG)" +jq --arg key "$PKG" --argjson new "$JSON" \ + '.[$key] = $new' "cpmfile.json" --indent 4 >"cpmfile.json.tmp" && + mv "cpmfile.json.tmp" cpmfile.json + +"$SCRIPTS"/format.sh + +echo "Added package $PKG to cpmfile.json. Include it in your project with AddJsonPackage($PKG)" diff --git a/tools/cpm/package/util/replace.sh b/tools/cpm/package/util/replace.sh index 4ec65384da..7141747a12 100755 --- a/tools/cpm/package/util/replace.sh +++ b/tools/cpm/package/util/replace.sh @@ -1,13 +1,11 @@ #!/bin/sh -e -# SPDX-FileCopyrightText: Copyright 2025 crueter +# SPDX-FileCopyrightText: Copyright 2026 crueter # SPDX-License-Identifier: LGPL-3.0-or-later # Replace a specified package with a modified json. -FILE=$(echo "$CPMFILES" | xargs grep -l "\"$1\"") +jq --indent 4 --argjson repl "$2" ".\"$1\" *= \$repl" cpmfile.json >cpmfile.json.new +mv cpmfile.json.new cpmfile.json -jq --indent 4 --argjson repl "$2" ".\"$1\" *= \$repl" "$FILE" >"$FILE".new -mv "$FILE".new "$FILE" - -echo "-- * -- Updated $FILE" +echo "-- * -- Updated cpmfile.json" diff --git a/tools/cpm/package/vars/url.sh b/tools/cpm/package/util/url.sh similarity index 60% rename from tools/cpm/package/vars/url.sh rename to tools/cpm/package/util/url.sh index 205d3a06ef..22a69bf898 100755 --- a/tools/cpm/package/vars/url.sh +++ b/tools/cpm/package/util/url.sh @@ -1,28 +1,23 @@ -#!/bin/sh +#!/bin/sh -e -# SPDX-FileCopyrightText: Copyright 2025 crueter +# SPDX-FileCopyrightText: Copyright 2026 crueter # SPDX-License-Identifier: LGPL-3.0-or-later -# Required vars: URL, GIT_HOST, REPO, TAG, ARTIFACT, BRANCH, SHA +# Get the download URL for a package + if [ "$URL" != "null" ]; then DOWNLOAD="$URL" elif [ "$REPO" != "null" ]; then GIT_URL="https://$GIT_HOST/$REPO" - if [ "$TAG" != "null" ]; then + if [ "$SHA" != "null" ]; then + DOWNLOAD="${GIT_URL}/archive/${SHA}.tar.gz" + elif [ "$TAG" != "null" ]; then if [ "$ARTIFACT" != "null" ]; then DOWNLOAD="${GIT_URL}/releases/download/${TAG}/${ARTIFACT}" else DOWNLOAD="${GIT_URL}/archive/refs/tags/${TAG}.tar.gz" fi - elif [ "$SHA" != "null" ]; then - DOWNLOAD="${GIT_URL}/archive/${SHA}.tar.gz" - else - if [ "$BRANCH" = null ]; then - BRANCH=master - fi - - DOWNLOAD="${GIT_URL}/archive/refs/heads/${BRANCH}.tar.gz" fi else echo "!! No repo or URL defined for $PACKAGE_NAME" diff --git a/tools/cpm/package/vars.sh b/tools/cpm/package/vars.sh index 07f4883ddf..dd497407c8 100755 --- a/tools/cpm/package/vars.sh +++ b/tools/cpm/package/vars.sh @@ -9,14 +9,16 @@ value() { echo "$JSON" | jq -r ".$1" } -[ -n "$PACKAGE" ] || { echo "Package was not specified" && exit 0; } - -# shellcheck disable=SC2153 -JSON=$(echo "$PACKAGES" | jq -r ".\"$PACKAGE\" | select( . != null )") - if [ -z "$JSON" ]; then - echo "!! No cpmfile definition for $PACKAGE" >&2 - exit 1 + [ -n "$PACKAGE" ] || { echo "Package was not specified" && exit 0; } + + # shellcheck disable=SC2153 + JSON=$(jq -r ".\"$PACKAGE\" | select( . != null )" cpmfile.json) + + if [ -z "$JSON" ]; then + echo "!! No cpmfile definition for $PACKAGE" >&2 + exit 1 + fi fi # unset stuff @@ -31,7 +33,7 @@ export TAG="null" export ARTIFACT="null" export SHA="null" export VERSION="null" -export GIT_VERSION="null" +export MIN_VERSION="null" export DOWNLOAD="null" export URL="null" export KEY="null" @@ -39,9 +41,6 @@ export HASH="null" export ORIGINAL_TAG="null" export HAS_REPLACE="null" export VERSION_REPLACE="null" -export HASH_URL="null" -export HASH_SUFFIX="null" -export HASH_ALGO="null" ######## # Meta # @@ -56,7 +55,11 @@ PACKAGE_NAME=$(value "package") GIT_HOST=$(value "git_host") [ "$GIT_HOST" != null ] || GIT_HOST=github.com +# used for cache key +LOWER_PACKAGE=$(echo "$PACKAGE_NAME" | tr '[:upper:]' '[:lower:]') + export PACKAGE_NAME +export LOWER_PACKAGE export REPO export CI export GIT_HOST @@ -65,8 +68,12 @@ export GIT_HOST # CI Package Parsing # ###################### +MIN_VERSION=$(value "min_version") VERSION=$(value "version") +export VERSION +export MIN_VERSION + if [ "$CI" = "true" ]; then EXT=$(value "extension") [ "$EXT" != null ] || EXT="tar.zst" @@ -79,7 +86,6 @@ if [ "$CI" = "true" ]; then export EXT export NAME export DISABLED - export VERSION return 0 fi @@ -91,15 +97,6 @@ fi TAG=$(value "tag") ARTIFACT=$(value "artifact") SHA=$(value "sha") -GIT_VERSION=$(value "git_version") - -[ "$GIT_VERSION" != null ] || GIT_VERSION="$VERSION" - -if [ "$GIT_VERSION" != null ]; then - VERSION_REPLACE="$GIT_VERSION" -else - VERSION_REPLACE="$VERSION" -fi if echo "$TAG" | grep -e "%VERSION%" >/dev/null; then HAS_REPLACE=true @@ -109,30 +106,24 @@ fi ORIGINAL_TAG="$TAG" -TAG=$(echo "$TAG" | sed "s/%VERSION%/$VERSION_REPLACE/g") -ARTIFACT=$(echo "$ARTIFACT" | sed "s/%VERSION%/$VERSION_REPLACE/g") +TAG=$(echo "$TAG" | sed "s/%VERSION%/$VERSION/g") +ARTIFACT=$(echo "$ARTIFACT" | sed "s/%VERSION%/$VERSION/g") ARTIFACT=$(echo "$ARTIFACT" | sed "s/%TAG%/$TAG/g") export TAG export ARTIFACT export SHA export VERSION -export GIT_VERSION export ORIGINAL_TAG export HAS_REPLACE -export VERSION_REPLACE ############### # URL Parsing # ############### URL=$(value "url") -BRANCH=$(value "branch") -export BRANCH -export URL - -. "$SCRIPTS"/vars/url.sh +. "$SCRIPTS"/util/url.sh export DOWNLOAD @@ -140,9 +131,16 @@ export DOWNLOAD # Key Parsing # ############### -KEY=$(value "key") - -. "$SCRIPTS"/vars/key.sh +if [ "$SHA" != null ]; then + KEY=$(echo "$SHA" | cut -c1-4) +elif [ "$VERSION" != null ]; then + KEY="$VERSION" +elif [ "$TAG" != null ]; then + KEY="$TAG" +else + echo "!! No valid key could be determined for $PACKAGE_NAME. Must define one of: sha, tag, version" + exit 1 +fi export KEY @@ -150,27 +148,11 @@ export KEY # Hash Parsing # ################ -HASH_ALGO=$(value "hash_algo") -[ "$HASH_ALGO" != null ] || HASH_ALGO=sha512 - HASH=$(value "hash") if [ "$HASH" = null ]; then - HASH_SUFFIX="${HASH_ALGO}sum" - HASH_URL=$(value "hash_url") - - if [ "$HASH_URL" = null ]; then - HASH_URL="${DOWNLOAD}.${HASH_SUFFIX}" - fi - - HASH=$(curl "$HASH_URL" -Ss -L -o -) -else - HASH_URL=null - HASH_SUFFIX=null + echo "!! No hash defined for $PACKAGE_NAME" >&2 fi -export HASH_URL -export HASH_SUFFIX export HASH -export HASH_ALGO export JSON diff --git a/tools/cpm/package/vars/key.sh b/tools/cpm/package/vars/key.sh deleted file mode 100755 index e6d68c2abb..0000000000 --- a/tools/cpm/package/vars/key.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/sh -e - -# SPDX-FileCopyrightText: Copyright 2025 crueter -# SPDX-License-Identifier: LGPL-3.0-or-later - -if [ "$KEY" = null ]; then - if [ "$SHA" != null ]; then - KEY=$(echo "$SHA" | cut -c1-4) - elif [ "$GIT_VERSION" != null ]; then - KEY="$GIT_VERSION" - elif [ "$TAG" != null ]; then - KEY="$TAG" - elif [ "$VERSION" != null ]; then - KEY="$VERSION" - else - echo "!! No valid key could be determined for $PACKAGE_NAME. Must define one of: key, sha, tag, version, git_version" - exit 1 - fi -fi - -export KEY diff --git a/tools/cpm/package/version.sh b/tools/cpm/package/version.sh index fef78124a0..cfeaa27f1e 100755 --- a/tools/cpm/package/version.sh +++ b/tools/cpm/package/version.sh @@ -34,27 +34,29 @@ if [ "$HAS_REPLACE" = "true" ]; then VERSION_PREFIX=$(echo "$ORIGINAL_TAG" | cut -d"%" -f1) # then we strip out the prefix from the new tag, and make that our new git_version - if [ -z "$VERSION_PREFIX" ]; then - NEW_GIT_VERSION="$NEW_VERSION" - else - NEW_GIT_VERSION=$(echo "$NEW_VERSION" | sed "s/$VERSION_PREFIX//g") + if [ -n "$VERSION_PREFIX" ]; then + NEW_VERSION=$(echo "$NEW_VERSION" | sed "s/$VERSION_PREFIX//g") fi fi if [ "$SHA" != null ]; then - NEW_JSON=$(echo "$JSON" | jq ".sha = \"$NEW_VERSION\"") -elif [ "$CI" = "true" ]; then - NEW_JSON=$(echo "$JSON" | jq ".version = \"$NEW_VERSION\"") -elif [ "$HAS_REPLACE" = "true" ]; then - NEW_JSON=$(echo "$JSON" | jq ".git_version = \"$NEW_GIT_VERSION\"") + JSON=$(echo "$JSON" | jq ".sha = \"$NEW_VERSION\"") +elif [ "$CI" = "true" ] || [ "$HAS_REPLACE" = "true" ]; then + JSON=$(echo "$JSON" | jq ".version = \"$NEW_VERSION\"") else - NEW_JSON=$(echo "$JSON" | jq ".tag = \"$NEW_VERSION\"") + JSON=$(echo "$JSON" | jq ".tag = \"$NEW_VERSION\"") fi -echo "-- * -- Updating $PACKAGE to version $NEW_VERSION" -"$SCRIPTS"/util/replace.sh "$PACKAGE" "$NEW_JSON" +echo "-- * Updating $PACKAGE to version $NEW_VERSION" -[ "$CI" != "true" ] || exit 0 -echo "-- * -- Fixing hash" -. "$ROOTDIR"/common.sh -UPDATE=true QUIET=true "$SCRIPTS"/util/fix-hash.sh +# TODO: ci hash thing please +if [ "$CI" != true ]; then + echo "-- * -- Updating hash" + + # shellcheck disable=SC1091 + . "$SCRIPTS"/vars.sh + HASH=$("$SCRIPTS"/util/url-hash.sh "$DOWNLOAD") + JSON=$(echo "$JSON" | jq ".hash = \"$HASH\"") +fi + +"$SCRIPTS"/util/replace.sh "$PACKAGE" "$JSON" diff --git a/tools/cpm/package/which.sh b/tools/cpm/package/which.sh index 24c80760e3..905d055e87 100755 --- a/tools/cpm/package/which.sh +++ b/tools/cpm/package/which.sh @@ -3,10 +3,6 @@ # SPDX-FileCopyrightText: Copyright 2026 crueter # SPDX-License-Identifier: LGPL-3.0-or-later -# check which file a package is in +# check if a package is defined -JSON=$(echo "$CPMFILES" | xargs grep -l "\"$1\"") - -[ -n "$JSON" ] || { echo "!! No cpmfile definition for $1" >&2 && exit 1; } - -echo "$JSON" +echo "$LIBS" | grep "$1" >/dev/null 2>&1 diff --git a/tools/cpmutil.sh b/tools/cpmutil.sh index cec758a726..6c18e6999b 100755 --- a/tools/cpmutil.sh +++ b/tools/cpmutil.sh @@ -25,9 +25,8 @@ General command-line utility for CPMUtil operations. Commands: package Run operations on a package or packages - format Format all cpmfiles + format Format cpmfile update Update CPMUtil and its tooling - ls List all cpmfiles migrate Convert submodules to a basic cpmfile Package commands: @@ -37,8 +36,11 @@ Package commands: add Add a new package rm Remove a package version Change the version of a package - which Find which cpmfile a package is defined in + which Check if a package is defined download Get the download URL for a package + dir Get the local directory for a package + reset Reset a fetched package to its original state + patch Create an in-tree patch based on local modifications EOF @@ -49,10 +51,6 @@ export ROOTDIR while :; do case "$1" in - ls) - echo "$CPMFILES" | tr ' ' '\n' - break - ;; format | update | migrate) "$SCRIPTS/$1".sh break diff --git a/tools/cpp-lint.sh b/tools/cpp-lint.sh new file mode 100755 index 0000000000..4c5a6e10dc --- /dev/null +++ b/tools/cpp-lint.sh @@ -0,0 +1,55 @@ +#!/bin/sh -ex + +# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +# SPDX-License-Identifier: GPL-3.0-or-later + +# tools/../ +ROOTDIR=$(CDPATH='' cd -- "$(dirname -- "$0")/../" && pwd) +BUILD_DIR="$ROOTDIR"/build +SRC_DIR="$ROOTDIR"/src + +die() { + echo "-- $*" >&2 + exit 1 +} + +usage() { + cat <td_name), stack(100)] = count(); -} -profile-".$sampling_hz." /pid == ".$sampling_pid." && arg1/ { - @[stringof(curthread->td_name), ustack(100)] = count(); -} -tick-".$sampling_time."s { - exit(0); -}"; +profile-".$sampling_hz." /pid == ".$sampling_pid." && arg0/ { @[stringof(curthread->td_name), stack(100)] = count(); } +profile-".$sampling_hz." /pid == ".$sampling_pid." && arg1/ { @[stringof(curthread->td_name), ustack(100)] = count(); } +tick-".$sampling_time."s { exit(0); }"; } elsif ($sampling_type eq 3) { + # trace I/O requests return " -io::start /pid == ".$sampling_pid."/ { - @[ustack(100)] = count(); -} -tick-".$sampling_time."s { - exit(0); -}"; +io::start /pid == ".$sampling_pid."/ { @[ustack(100)] = count(); } +tick-".$sampling_time."s { exit(0); }"; } else { die "idk"; } @@ -111,11 +98,17 @@ sub dtrace_generate { foreach my $i (0 .. $#ARGV) { if ($ARGV[$i] eq '-h') { - print "Usage: $0\n"; - printf "%-20s%s\n", "-p", "Prompt for parameters"; - printf "%-20s%s\n", "-g", "Generate dtrace output"; - printf "%-20s%s\n", "-s", "Continously generate output until Ctrl^C"; - printf "%-20s%s\n", "-", "Select dtrace type"; + print " +Usage: $0\n +-p Prompt for parameters\n +-g Generate dtrace output\n +-s Continously generate output until Ctrl^C\n +- Select dtrace type\n + 0: Profile kernel + user stacks (default)\n + 1: Trace syscall entries\n + 2: Profile kernel + user stacks with thread names\n + 3: Trace I/O requests\n +"; } elsif ($ARGV[$i] eq '-g') { dtrace_generate; } elsif ($ARGV[$i] eq '-s') { diff --git a/tools/fuzzsettings.cpp b/tools/fuzzsettings.cpp new file mode 100644 index 0000000000..e9685ce9f4 --- /dev/null +++ b/tools/fuzzsettings.cpp @@ -0,0 +1,63 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char *argv[]) { + std::srand(unsigned(std::time(nullptr))); + + FILE *fp = std::fopen(argv[1], "rt"); + if (fp) { + char line[BUFSIZ]; + while (std::fgets(line, sizeof(line), fp)) { + if (line[0] == '[') { + std::printf("%s", line); + } else if (std::isspace(line[0])) { + std::printf("%s", line); + } else { + char *p = std::strchr(line, '='); + if (std::strstr(line, "\\default") == nullptr) { + // not default + *p = '\0'; + std::string new_line{line}; + std::string value{p + 1}; + if (value == "true" || value == "false") { + new_line += std::string{} + "=TreufLAlse857874FJJakshjryiu475" + '\n'; + } else if (std::isdigit(value[0])) { + if (new_line == "size" + || std::strstr(new_line.c_str(), "entries\\size") != nullptr + || std::strstr(new_line.c_str(), "\\size")) { + new_line += "=-1\n"; + } else { + new_line += '=' + std::to_string(int(std::rand())) + '\n'; + } + } else { + std::string_view const cset{"03832///1/1/.1/1./1./1./1.1/.1194573290uwmgjouidyhiomHMNIODASJK,POF MSHDVLJPOIuksdtpsunmghns"}; + std::string rst{"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}; + for (size_t i = 0; i < rst.size(); ++i) + rst[i] = cset[std::rand() % cset.size()]; + + //new_line += "=\"" + rst + "\""; + new_line += "=" + value; + } + std::printf("%s", new_line.c_str()); + } else { + // yes default + *p = '\0'; + std::string new_line{line}; + std::string value{p + 1}; + new_line += "=false\n"; + std::printf("%s", new_line.c_str()); + } + } + } + std::fclose(fp); + } + return 0; +} diff --git a/tools/fuzzsettings.sh b/tools/fuzzsettings.sh new file mode 100755 index 0000000000..7cf613dc3d --- /dev/null +++ b/tools/fuzzsettings.sh @@ -0,0 +1,12 @@ +#!/bin/sh -ex + +# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +# SPDX-License-Identifier: GPL-3.0-or-later + +ROOTDIR=$(CDPATH='' cd -- "$(dirname -- "$0")/" && pwd) + +touch "$2" + +c++ "$ROOTDIR/fuzzsettings.cpp" -o fuzzsettings +./fuzzsettings "$1" >"$2" +rm fuzzsettings diff --git a/tools/gendynarm.cpp b/tools/gendynarm.cpp index 3d1588e7e8..3e28363a10 100644 --- a/tools/gendynarm.cpp +++ b/tools/gendynarm.cpp @@ -17,12 +17,6 @@ namespace mcl { template constexpr std::size_t bitsizeof = CHAR_BIT * sizeof(T); } -namespace mcl::bit { -template -inline size_t count_ones(T x) { - return std::bitset>(x).count(); -} -} template inline consteval std::array StringToArray(const char (&str)[N + 1]) { std::array result{}; @@ -380,7 +374,7 @@ INST(arm_SRS, "SRS", "1111100--1-0110100000101000----- }; // If a matcher has more bits in its mask it is more specific, so it should come first. std::stable_sort(list.begin(), list.end(), [](const auto& matcher1, const auto& matcher2) { - return mcl::bit::count_ones(matcher1.second) > mcl::bit::count_ones(matcher2.second); + return std::popcount(matcher1.second) > std::popcount(matcher2.second); }); for (auto const& e : list) printf("%s\n", e.inst_final); @@ -587,7 +581,7 @@ INST(v8_VLD_single, "VLD{1-4} (single)", "111101001D10nnnnddddzzN }); // If a matcher has more bits in its mask it is more specific, so it should come first. std::stable_sort(sort_begin, sort_end, [](const auto& a, const auto& b) { - return mcl::bit::count_ones(a.second) > mcl::bit::count_ones(b.second); + return std::popcount(a.second) > std::popcount(b.second); }); for (auto const& e : table) printf("%s\n", e.inst_final); @@ -1628,7 +1622,7 @@ INST(FNMSUB_float, "FNMSUB", "00011 // If a matcher has more bits in its mask it is more specific, so it should come first. std::stable_sort(list.begin(), list.end(), [](const auto& a, const auto& b) { // If a matcher has more bits in its mask it is more specific, so it should come first. - return mcl::bit::count_ones(a.second) > mcl::bit::count_ones(b.second); + return std::popcount(a.second) > std::popcount(b.second); }); // Exceptions to the above rule of thumb. std::stable_partition(list.begin(), list.end(), [&](const auto& e) { diff --git a/tools/lanczos-gen.pl b/tools/lanczos-gen.pl old mode 100644 new mode 100755 index 2e26c8c6b9..3eab5cdb10 --- a/tools/lanczos-gen.pl +++ b/tools/lanczos-gen.pl @@ -1,5 +1,5 @@ -#!/usr/bin/perl -# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +#!/usr/bin/env perl +# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project # SPDX-License-Identifier: GPL-3.0-or-later use strict; use warnings; diff --git a/tools/llvmpipe-run.sh b/tools/llvmpipe-run.sh index c3a5a85d41..f09fe87ef4 100755 --- a/tools/llvmpipe-run.sh +++ b/tools/llvmpipe-run.sh @@ -1,5 +1,6 @@ -#!/bin/sh -# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +#!/bin/sh -e + +# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project # SPDX-License-Identifier: GPL-3.0-or-later # This script basically allows you to "dirtily" use llvmpipe in any configuration diff --git a/tools/maxwell-disas/CMakeLists.txt b/tools/maxwell-disas/CMakeLists.txt new file mode 100644 index 0000000000..2595d45a35 --- /dev/null +++ b/tools/maxwell-disas/CMakeLists.txt @@ -0,0 +1,11 @@ +# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +# SPDX-License-Identifier: GPL-3.0-or-later +add_executable(maxwell-disas + main.cpp +) +target_link_libraries(maxwell-disas PRIVATE common shader_recompiler Threads::Threads) +target_include_directories(maxwell-disas PRIVATE ${CMAKE_SOURCE_DIR}/src) +if(UNIX AND NOT APPLE) + install(TARGETS maxwell-disas) +endif() +create_target_directory_groups(maxwell-disas) diff --git a/tools/maxwell-disas/file_environment.h b/tools/maxwell-disas/file_environment.h new file mode 100644 index 0000000000..cf190806e4 --- /dev/null +++ b/tools/maxwell-disas/file_environment.h @@ -0,0 +1,60 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include +#include "shader_recompiler/environment.h" + +class FileEnvironment final : public Shader::Environment { +public: + FileEnvironment() = default; + ~FileEnvironment() override = default; + FileEnvironment& operator=(FileEnvironment&&) noexcept = default; + FileEnvironment(FileEnvironment&&) noexcept = default; + FileEnvironment& operator=(const FileEnvironment&) = delete; + FileEnvironment(const FileEnvironment&) = delete; + void Deserialize(std::ifstream& file) {} + [[nodiscard]] u64 ReadInstruction(u32 address) override { + if (address < read_lowest || address > read_highest) { + std::printf("cant read %08x\n", address); + std::abort(); + } + return code[(address - read_lowest) / sizeof(u64)]; + } + [[nodiscard]] u32 ReadCbufValue(u32 cbuf_index, u32 cbuf_offset) override { return 0; } + [[nodiscard]] Shader::TextureType ReadTextureType(u32 handle) override { + auto const it{texture_types.find(handle)}; + return it->second; + } + [[nodiscard]] Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override { + auto const it{texture_pixel_formats.find(handle)}; + return it->second; + } + [[nodiscard]] bool IsTexturePixelFormatInteger(u32 handle) override { return true; } + [[nodiscard]] u32 ReadViewportTransformState() override { return viewport_transform_state; } + [[nodiscard]] u32 LocalMemorySize() const override { return local_memory_size; } + [[nodiscard]] u32 SharedMemorySize() const override { return shared_memory_size; } + [[nodiscard]] u32 TextureBoundBuffer() const override { return texture_bound; } + [[nodiscard]] std::array WorkgroupSize() const override { return workgroup_size; } + [[nodiscard]] std::optional GetReplaceConstBuffer(u32 bank, u32 offset) override { + auto const it = cbuf_replacements.find((u64(bank) << 32) | u64(offset)); + return it != cbuf_replacements.end() ? std::optional{it->second} : std::nullopt; + } + [[nodiscard]] bool HasHLEMacroState() const override { return cbuf_replacements.size() != 0; } + void Dump(u64 pipeline_hash, u64 shader_hash) override {} + std::vector code; + std::unordered_map texture_types; + std::unordered_map texture_pixel_formats; + std::unordered_map cbuf_values; + std::unordered_map cbuf_replacements; + std::array workgroup_size{}; + u32 local_memory_size{}; + u32 shared_memory_size{}; + u32 texture_bound{}; + u32 read_lowest{}; + u32 read_highest{}; + u32 initial_offset{}; + u32 viewport_transform_state = 1; +}; diff --git a/tools/maxwell-disas/generated.cpp b/tools/maxwell-disas/generated.cpp new file mode 100644 index 0000000000..687209558d --- /dev/null +++ b/tools/maxwell-disas/generated.cpp @@ -0,0 +1,641 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include +#include +#include +namespace Shader::Maxwell { +std::string DissasemblyFormat(uint64_t inst) { + std::string s{}; + if(((inst>>48)&0xfff8ULL)==0x5c58ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"FADD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x4c58ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"FADD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0x50f8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"FSWZADD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x5c68ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"FMUL "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x4c68ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"FMUL "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0x5c60ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"FMNMX "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x4c60ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"FMNMX "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0x5c90ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"RRO "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x4c90ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"RRO "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0x5080ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"MUFU "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0x5c88ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"FCHK "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x4c88ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"FCHK "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0x5c70ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"DADD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x4c70ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"DADD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0x5c80ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"DMUL "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x4c80ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"DMUL "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0x5c50ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"DMNMX "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x4c50ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"DMNMX "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0x5c38ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"IMUL "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x4c38ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"IMUL "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0x5c10ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"IADD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x4c10ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"IADD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0x5c18ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ISCADD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x4c18ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ISCADD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0x5c20ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"IMNMX "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x4c20ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"IMNMX "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0x5c00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"BFE "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x4c00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"BFE "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0x5bf0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"BFI "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x4bf0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"BFI "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0x53f0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"BFI "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0x5c28ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"SHR "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x4c28ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"SHR "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0x5c48ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"SHL "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x4c48ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"SHL "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0x5bf8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"SHF "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x5cf8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"SHF "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x5c40ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"LOP "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x4c40ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"LOP "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0x5be0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"LOP3 "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x5be0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"LOP3 "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x5c30ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"FLO "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x4c30ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"FLO "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0x5c08ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"POPC "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x4c08ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"POPC "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0x50f0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x50f0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x50f0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x50f0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x50f0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x50f0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x50f0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x50f0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x50f0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x50f0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0x50f0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0x50f0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0x50f0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0x50f0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0x50f0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0x50f0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x50f0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x50f0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x50f0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x50f0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x50f0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x50f0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x50f0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x50f0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x50f0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0x50f0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0x50f0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0x50f0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0x50f0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0x50f0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0x5ca8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"F2F "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x5ca8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"F2F "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x5ca8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"F2F "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x5ca8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"F2F "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x5ca8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"F2F "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x5ca8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"F2F "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x5ca8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"F2F "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x5ca8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"F2F "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x4ca8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"F2F "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0x4ca8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"F2F "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0x4ca8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"F2F "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0x4ca8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"F2F "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0x4ca8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"F2F "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0x4ca8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"F2F "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0x4ca8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"F2F "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0x4ca8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"F2F "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0x5cb0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"F2I "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x5cb0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"F2I "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x5cb0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"F2I "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x5cb0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"F2I "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x4cb0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"F2I "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0x4cb0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"F2I "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0x4cb0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"F2I "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0x4cb0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"F2I "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0x5cb8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"I2F "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x5cb8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"I2F "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x5cb8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"I2F "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x5cb8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"I2F "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x4cb8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"I2F "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0x4cb8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"I2F "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0x4cb8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"I2F "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0x4cb8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"I2F "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0x5ce0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"I2I "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x5ce0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"I2I "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x4ce0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"I2I "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0x4ce0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"I2I "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0x5c98ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"MOV "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x4c98ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"MOV "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0x5ca0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"SEL "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x4ca0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"SEL "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0xef10ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"SHFL "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0xef10ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"SHFL "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0xef10ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"SHFL "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0xef10ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"SHFL "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x5ce8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"P2R "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x4ce8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"P2R "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0x5cf0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"R2P "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x4cf0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"R2P "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0x5098ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"CSET "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0x5098ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"CSET "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0x50a0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"CSETP "; + if(((inst>>48)&0xfff8ULL)==0x50a0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"CSETP "; + if(((inst>>48)&0xfff8ULL)==0x5088ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"PSET "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0x5088ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"PSET "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0x5090ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"PSETP "; + if(((inst>>48)&0xfff8ULL)==0x5090ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"PSETP "; + if(((inst>>48)&0xfff8ULL)==0xeea0ULL)return s+"STP "; + if(((inst>>48)&0xfff8ULL)==0xdf58ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"TMML "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0xdf58ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"TMML "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0xdf60ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"TMML "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0xdf60ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"TMML "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0xdf48ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"TXQ "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0xdf48ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"TXQ "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0xdf48ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"TXQ "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0xdf48ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"TXQ "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0xdf50ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"TXQ "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0xdf50ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"TXQ "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0xdf50ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"TXQ "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0xdf50ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"TXQ "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0xdf40ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"TXA "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0xdf40ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"TXA "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0xf0f0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"DEPBAR "; + if(((inst>>48)&0xfff8ULL)==0xf0f0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"DEPBAR "; + if(((inst>>48)&0xfff8ULL)==0xf0f0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"DEPBAR "; + if(((inst>>48)&0xfff8ULL)==0xefa0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"AL2P "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0xefa0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"AL2P "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0xefd8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ALD "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0xefd8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ALD "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0xefd8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ALD "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0xefd8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ALD "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0xeff0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"AST "; + if(((inst>>48)&0xfff8ULL)==0xeff0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"AST "; + if(((inst>>48)&0xfff8ULL)==0xeff0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"AST "; + if(((inst>>48)&0xfff8ULL)==0xeff0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"AST "; + if(((inst>>48)&0xfff8ULL)==0xfbe0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"OUT "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0xebe0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"OUT "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0xefe8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"PIXLD "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0xefe8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"PIXLD "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0xefe8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"PIXLD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0xef90ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"LDC "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0xef90ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"LDC "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0xef90ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"LDC "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0xef90ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"LDC "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0xeed0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"LDG "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0xeed0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"LDG "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0xeec8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"LDG "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0xeec8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"LDG "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0xef40ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"LDL "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0xef40ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"LDL "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0xef48ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"LDS "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0x5bd0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"LEA "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x4bd0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"LEA "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0x5bd8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"LEA "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0xeed8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"STG "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0xeed8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"STG "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0xef50ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"STL "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0xef50ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"STL "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0xef58ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"STS "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0xef58ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"STS "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0xebf8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"RED "; + if(((inst>>48)&0xfff8ULL)==0xebf8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"RED "; + if(((inst>>48)&0xfff8ULL)==0xef80ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"CCTLL "; + if(((inst>>48)&0xfff8ULL)==0xef80ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"CCTLL "; + if(((inst>>48)&0xfff8ULL)==0xef80ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"CCTLL "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0xef80ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"CCTLL "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0xebe8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"CCTLT "; + if(((inst>>48)&0xfff8ULL)==0xebf0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"CCTLT "; + if(((inst>>48)&0xfff8ULL)==0xebf0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"CCTLT "; + if(((inst>>48)&0xfff8ULL)==0xef98ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"MEMBAR "; + if(((inst>>48)&0xfff8ULL)==0xeb10ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"SULD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0xeb18ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"SULD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0xeb00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"SULD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0xeb08ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"SULD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0xeb30ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"SUST "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0xeb38ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"SUST "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0xeb20ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"SUST "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0xeb28ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"SUST "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0xeb50ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"SURED "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0xeb58ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"SURED "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0xeb40ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"SURED "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0xeb48ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"SURED "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0xea70ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"SUATOM "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0xea60ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"SUATOM "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0xea68ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"SUATOM "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0xead0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"SUATOM "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0xeac0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"SUATOM "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0xeac8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"SUATOM "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0xf0f8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"SYNC "; + if(((inst>>48)&0xfff8ULL)==0x50b0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"NOP "; + if(((inst>>48)&0xfff8ULL)==0x50b0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"NOP "; + if(((inst>>48)&0xfff8ULL)==0xf0c8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"S2R "+"R"+std::to_string((inst&0xff)>>0x0)+" "+std::string(SpecialRegGetName((inst&0xfffffff)>>0x14)); + if(((inst>>48)&0xfff8ULL)==0x50c8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"CS2R "+"R"+std::to_string((inst&0xff)>>0x0)+" "+std::string(SpecialRegGetName((inst&0xfffffff)>>0x14)); + if(((inst>>48)&0xfff8ULL)==0xf0b8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"B2R "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0xf0b8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"B2R "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0xf0b8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"B2R "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0xf0c0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"R2B "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x50d0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"LEPC "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0xf0a8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"BAR "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0xf0a8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"BAR "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0xf0a8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"BAR "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0xf0a8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"BAR "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0xf0a8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"BAR "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0xf0a8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"BAR "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0xf0a8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"BAR "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0xf0a8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"BAR "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0xf0a8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"BAR "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0xf0a8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"BAR "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0xf0a8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"BAR "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0xf0a8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"BAR "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0xf0a8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"BAR "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0xf0a8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"BAR "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0xf0a8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"BAR "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0xf0a8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"BAR "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0xf0a8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"BAR "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff8ULL)==0x50e0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VOTE "; + if(((inst>>48)&0xfff8ULL)==0x50d8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VOTE "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0xefd0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ISBERD "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff8ULL)==0x0ULL)return s+"HFMA2 "; + if(((inst>>48)&0xfff8ULL)==0x0ULL)return s+"HSET2 "; + if(((inst>>48)&0xfff8ULL)==0x0ULL)return s+"HSET2 "; + if(((inst>>48)&0xfff8ULL)==0x0ULL)return s+"HADD2 "; + if(((inst>>48)&0xfff8ULL)==0x0ULL)return s+"HMUL2 "; + if(((inst>>48)&0xfff8ULL)==0x5d20ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"HSETP2 "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff8ULL)==0x5d20ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"HSETP2 "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfef8ULL)==0x3858ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"FADD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff0ULL)==0x5ba0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"FCMP "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff0ULL)==0x4ba0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"FCMP "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff0ULL)==0x53a0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"FCMP "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfef8ULL)==0x3868ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"FMUL "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfef8ULL)==0x3860ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"FMNMX "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff0ULL)==0x5bb0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"FSETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff0ULL)==0x5bb0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"FSETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff0ULL)==0x4bb0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"FSETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff0ULL)==0x4bb0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"FSETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfef8ULL)==0x3890ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"RRO "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfef8ULL)==0x3888ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"FCHK "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff0ULL)==0x5b70ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"DFMA "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff0ULL)==0x4b70ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"DFMA "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff0ULL)==0x5370ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"DFMA "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfef8ULL)==0x3870ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"DADD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfef8ULL)==0x3880ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"DMUL "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfef8ULL)==0x3850ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"DMNMX "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff0ULL)==0x5b80ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"DSETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff0ULL)==0x5b80ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"DSETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff0ULL)==0x4b80ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"DSETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff0ULL)==0x4b80ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"DSETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfef8ULL)==0x3838ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"IMUL "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfef8ULL)==0x3810ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"IADD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff0ULL)==0x5cc0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"IADD3 "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff0ULL)==0x4cc0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"IADD3 "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfef8ULL)==0x3818ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ISCADD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfef8ULL)==0x3820ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"IMNMX "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfef8ULL)==0x3800ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"BFE "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfef8ULL)==0x36f0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"BFI "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfef8ULL)==0x3828ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"SHR "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfef8ULL)==0x3848ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"SHL "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfef8ULL)==0x36f8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"SHF "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfef8ULL)==0x38f8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"SHF "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfef8ULL)==0x3840ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"LOP "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfef8ULL)==0x3830ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"FLO "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff0ULL)==0x5b50ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ISET "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff0ULL)==0x5b50ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ISET "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff0ULL)==0x4b50ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ISET "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff0ULL)==0x4b50ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ISET "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff0ULL)==0x5b50ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ISET "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff0ULL)==0x5b50ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ISET "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff0ULL)==0x4b50ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ISET "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff0ULL)==0x4b50ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ISET "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff0ULL)==0x5b60ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ISETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff0ULL)==0x5b60ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ISETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff0ULL)==0x4b60ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ISETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff0ULL)==0x4b60ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ISETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff0ULL)==0x5b60ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ISETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff0ULL)==0x5b60ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ISETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff0ULL)==0x4b60ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ISETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff0ULL)==0x4b60ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ISETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff0ULL)==0x5b40ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ICMP "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff0ULL)==0x4b40ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ICMP "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff0ULL)==0x5340ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ICMP "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff0ULL)==0x5b40ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ICMP "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff0ULL)==0x4b40ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ICMP "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff0ULL)==0x5340ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ICMP "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfef8ULL)==0x3808ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"POPC "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfef8ULL)==0x38a8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"F2F "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfef8ULL)==0x38a8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"F2F "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfef8ULL)==0x38a8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"F2F "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfef8ULL)==0x38a8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"F2F "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfef8ULL)==0x38a8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"F2F "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfef8ULL)==0x38a8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"F2F "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfef8ULL)==0x38a8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"F2F "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfef8ULL)==0x38a8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"F2F "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfef8ULL)==0x38b0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"F2I "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfef8ULL)==0x38b0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"F2I "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfef8ULL)==0x38b0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"F2I "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfef8ULL)==0x38b0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"F2I "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfef8ULL)==0x38b8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"I2F "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfef8ULL)==0x38b8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"I2F "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfef8ULL)==0x38b8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"I2F "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfef8ULL)==0x38e0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"I2I "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfef8ULL)==0x38e0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"I2I "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfef8ULL)==0x3898ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"MOV "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff0ULL)==0x100ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"MOV32I "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfef8ULL)==0x38a0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"SEL "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff0ULL)==0x5bc0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"PRMT "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff0ULL)==0x4bc0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"PRMT "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff0ULL)==0x53c0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"PRMT "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfef8ULL)==0x38e8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"P2R "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfef8ULL)==0x38e8ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"P2R "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfef8ULL)==0x38f0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"R2P "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfef8ULL)==0xf6e0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"OUT "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfef8ULL)==0x36d0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"LEA "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff0ULL)==0xeef0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ATOM "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff0ULL)==0xeef0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ATOM "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff0ULL)==0xeef0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ATOM "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff0ULL)==0xeef0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ATOM "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff0ULL)==0xeef0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ATOM "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff0ULL)==0xeef0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ATOM "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff0ULL)==0xee60ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ATOM "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff0ULL)==0xee70ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ATOM "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff0ULL)==0xee70ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ATOM "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff0ULL)==0xee70ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ATOM "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfff0ULL)==0xe240ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"BRA "; + if(((inst>>48)&0xfff0ULL)==0xe240ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"BRA "; + if(((inst>>48)&0xfff0ULL)==0xe250ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"BRX "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff0ULL)==0xe250ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"BRX "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff0ULL)==0xe210ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"JMP "; + if(((inst>>48)&0xfff0ULL)==0xe210ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"JMP "; + if(((inst>>48)&0xfff0ULL)==0xe200ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"JMX "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff0ULL)==0xe200ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"JMX "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff0ULL)==0xe260ULL)return s+"CAL "; + if(((inst>>48)&0xfff0ULL)==0xe260ULL)return s+"CAL "; + if(((inst>>48)&0xfff0ULL)==0xe270ULL)return s+"PRET "; + if(((inst>>48)&0xfff0ULL)==0xe270ULL)return s+"PRET "; + if(((inst>>48)&0xfff0ULL)==0xe220ULL)return s+"JCAL "; + if(((inst>>48)&0xfff0ULL)==0xe220ULL)return s+"JCAL "; + if(((inst>>48)&0xfff0ULL)==0xe290ULL)return s+"SSY "; + if(((inst>>48)&0xfff0ULL)==0xe290ULL)return s+"SSY "; + if(((inst>>48)&0xfff0ULL)==0xe280ULL)return s+"PLONGJMP "; + if(((inst>>48)&0xfff0ULL)==0xe280ULL)return s+"PLONGJMP "; + if(((inst>>48)&0xfff0ULL)==0xe2a0ULL)return s+"PBK "; + if(((inst>>48)&0xfff0ULL)==0xe2a0ULL)return s+"PBK "; + if(((inst>>48)&0xfff0ULL)==0xe2b0ULL)return s+"PCNT "; + if(((inst>>48)&0xfff0ULL)==0xe2b0ULL)return s+"PCNT "; + if(((inst>>48)&0xfff0ULL)==0xe320ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"RET "; + if(((inst>>48)&0xfff0ULL)==0xe310ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"LONGJMP "; + if(((inst>>48)&0xfff0ULL)==0xe330ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"KIL "; + if(((inst>>48)&0xfff0ULL)==0xe340ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"BRK "; + if(((inst>>48)&0xfff0ULL)==0xe350ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"CONT "; + if(((inst>>48)&0xfff0ULL)==0xe300ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"EXIT "; + if(((inst>>48)&0xfff0ULL)==0xe230ULL)return s+"PEXIT "; + if(((inst>>48)&0xfff0ULL)==0xe370ULL)return s+"SAM "; + if(((inst>>48)&0xfff0ULL)==0xe380ULL)return s+"RAM "; + if(((inst>>48)&0xfff0ULL)==0xe3a0ULL)return s+"BPT "; + if(((inst>>48)&0xfff0ULL)==0xe360ULL)return s+"RTT "; + if(((inst>>48)&0xfff0ULL)==0xe390ULL)return s+"IDE "; + if(((inst>>48)&0xfff0ULL)==0xe390ULL)return s+"IDE "; + if(((inst>>48)&0xfff0ULL)==0xe2e0ULL)return s+"SETCRSPTR "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff0ULL)==0xe2c0ULL)return s+"GETCRSPTR "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfff0ULL)==0xe2f0ULL)return s+"SETLMEMBASE "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfff0ULL)==0xe2d0ULL)return s+"GETLMEMBASE "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xfef0ULL)==0x36a0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"FCMP "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfef0ULL)==0x36b0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"FSETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfef0ULL)==0x36b0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"FSETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfef0ULL)==0x3670ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"DFMA "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfef0ULL)==0x3680ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"DSETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfef0ULL)==0x3680ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"DSETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfef0ULL)==0x38c0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"IADD3 "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfef0ULL)==0x3650ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ISET "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfef0ULL)==0x3650ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ISET "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfef0ULL)==0x3650ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ISET "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfef0ULL)==0x3650ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ISET "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfef0ULL)==0x3660ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ISETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfef0ULL)==0x3660ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ISETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfef0ULL)==0x3660ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ISETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfef0ULL)==0x3660ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ISETP "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfef0ULL)==0x3640ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ICMP "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfef0ULL)==0x3640ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ICMP "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfef0ULL)==0x36c0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"PRMT "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xffe0ULL)==0xee40ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ATOMS "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xffe0ULL)==0xee40ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ATOMS "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xffe0ULL)==0xee40ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ATOMS "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xffe0ULL)==0xee40ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ATOMS "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xffe0ULL)==0xee40ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ATOMS "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xffe0ULL)==0xee40ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ATOMS "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xffe0ULL)==0xef60ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"CCTL "; + if(((inst>>48)&0xffe0ULL)==0xef60ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"CCTL "; + if(((inst>>48)&0xffe0ULL)==0xef60ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"CCTL "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xffe0ULL)==0xef60ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"CCTL "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xffe0ULL)==0xef60ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"CCTL "; + if(((inst>>48)&0xffe0ULL)==0xef60ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"CCTL "; + if(((inst>>48)&0xffc0ULL)==0x5b00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"XMAD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xffc0ULL)==0xde80ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"TEX "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xffc0ULL)==0xde80ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"TEX "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xffc0ULL)==0xdf00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"TLD4S "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xffc0ULL)==0xdf80ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"TLD4S "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xffc0ULL)==0xdec0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"TLD4 "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xffc0ULL)==0xdec0ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"TLD4 "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xffc0ULL)==0xde00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"TXD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xffc0ULL)==0xde00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"TXD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xffc0ULL)==0xde40ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"TXD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xffc0ULL)==0xde40ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"TXD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xffc0ULL)==0xee00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ATOMS "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xffc0ULL)==0xee00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ATOMS "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xffc0ULL)==0xee00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ATOMS "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xffc0ULL)==0xee00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ATOMS "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xffc0ULL)==0xee00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ATOMS "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xffc0ULL)==0xee00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ATOMS "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xffc0ULL)==0xeb80ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"SUATOM "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xffc0ULL)==0xea00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"SUATOM "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xffc0ULL)==0xea80ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"SUATOM "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff80ULL)==0x5980ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"FFMA "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff80ULL)==0x4980ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"FFMA "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xff80ULL)==0x5180ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"FFMA "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xff80ULL)==0x5900ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"DSET "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff80ULL)==0x5900ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"DSET "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff80ULL)==0x4900ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"DSET "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xff80ULL)==0x4900ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"DSET "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xff80ULL)==0x5a00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"IMAD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff80ULL)==0x4a00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"IMAD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xff80ULL)==0x5200ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"IMAD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xff80ULL)==0x5a80ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"IMADSP "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff80ULL)==0x4a80ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"IMADSP "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xff80ULL)==0x5280ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"IMADSP "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfec0ULL)==0x3600ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"XMAD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfec0ULL)==0x3600ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"XMAD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xff80ULL)==0x5100ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"XMAD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xff80ULL)==0x5000ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VABSDIFF4 "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff80ULL)==0x5000ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VABSDIFF4 "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff80ULL)==0x5000ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VABSDIFF4 "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfe80ULL)==0x3280ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"FFMA "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xff00ULL)==0x1e00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"FMUL32I "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xff00ULL)==0x5800ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"FSET "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff00ULL)==0x5800ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"FSET "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff00ULL)==0x4800ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"FSET "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xff00ULL)==0x4800ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"FSET "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xff00ULL)==0xe000ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"IPA "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff00ULL)==0xe000ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"IPA "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff00ULL)==0xe000ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"IPA "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff00ULL)==0xe000ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"IPA "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfe80ULL)==0x3200ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"DSET "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfe80ULL)==0x3200ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"DSET "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfe80ULL)==0x3400ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"IMAD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfe80ULL)==0x3480ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"IMADSP "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xff00ULL)==0x1f00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"IMUL32I "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xff00ULL)==0x5f00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VMAD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff00ULL)==0x5f00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VMAD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff00ULL)==0x5f00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VMAD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff00ULL)==0x5f00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VMAD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff00ULL)==0x5f00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VMAD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff00ULL)==0x5f00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VMAD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff00ULL)==0x5f00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VMAD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff00ULL)==0x5f00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VMAD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff00ULL)==0x5f00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VMAD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff00ULL)==0x5f00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VMAD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xff00ULL)==0x5f00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VMAD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xff00ULL)==0x5f00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VMAD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xff00ULL)==0x5f00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VMAD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xff00ULL)==0x5f00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VMAD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xff00ULL)==0x5f00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VMAD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xff00ULL)==0x5400ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VABSDIFF "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff00ULL)==0x5400ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VABSDIFF "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff00ULL)==0x5400ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VABSDIFF "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff00ULL)==0x5400ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VABSDIFF "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff00ULL)==0x5400ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VABSDIFF "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff00ULL)==0x5400ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VABSDIFF "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff00ULL)==0x5400ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VABSDIFF "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff00ULL)==0x5400ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VABSDIFF "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff00ULL)==0x5400ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VABSDIFF "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff00ULL)==0x5400ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VABSDIFF "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xff00ULL)==0x5400ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VABSDIFF "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xff00ULL)==0x5400ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VABSDIFF "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xff00ULL)==0x5400ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VABSDIFF "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xff00ULL)==0x5400ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VABSDIFF "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xff00ULL)==0x5400ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VABSDIFF "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xff00ULL)==0x5700ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSHL "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff00ULL)==0x5700ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSHL "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff00ULL)==0x5700ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSHL "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff00ULL)==0x5700ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSHL "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff00ULL)==0x5700ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSHL "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff00ULL)==0x5700ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSHL "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff00ULL)==0x5700ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSHL "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff00ULL)==0x5700ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSHL "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff00ULL)==0x5700ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSHL "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff00ULL)==0x5700ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSHL "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xff00ULL)==0x5700ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSHL "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xff00ULL)==0x5700ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSHL "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xff00ULL)==0x5700ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSHL "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xff00ULL)==0x5700ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSHL "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xff00ULL)==0x5700ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSHL "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xff00ULL)==0x5600ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSHR "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff00ULL)==0x5600ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSHR "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff00ULL)==0x5600ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSHR "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff00ULL)==0x5600ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSHR "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff00ULL)==0x5600ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSHR "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff00ULL)==0x5600ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSHR "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff00ULL)==0x5600ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSHR "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff00ULL)==0x5600ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSHR "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff00ULL)==0x5600ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSHR "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff00ULL)==0x5600ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSHR "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xff00ULL)==0x5600ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSHR "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xff00ULL)==0x5600ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSHR "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xff00ULL)==0x5600ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSHR "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xff00ULL)==0x5600ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSHR "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xff00ULL)==0x5600ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSHR "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xff00ULL)==0xdc00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"TLD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff00ULL)==0xdd00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"TLD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff00ULL)==0xed00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ATOM "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff00ULL)==0xed00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ATOM "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff00ULL)==0xec00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ATOMS "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xff00ULL)==0xec00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ATOMS "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfe80ULL)==0x80ULL)return s+"HSET2 "; + if(((inst>>48)&0xfe80ULL)==0x80ULL)return s+"HSET2 "; + if(((inst>>48)&0xfe80ULL)==0x80ULL)return s+"HSET2 "; + if(((inst>>48)&0xfe80ULL)==0x80ULL)return s+"HSET2 "; + if(((inst>>48)&0xfe80ULL)==0x80ULL)return s+"HADD2 "; + if(((inst>>48)&0xfe80ULL)==0x80ULL)return s+"HADD2 "; + if(((inst>>48)&0xfe80ULL)==0x80ULL)return s+"HMUL2 "; + if(((inst>>48)&0xfe80ULL)==0x80ULL)return s+"HMUL2 "; + if(((inst>>48)&0xfe80ULL)==0x7e00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"HSETP2 "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfe80ULL)==0x7e00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"HSETP2 "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfe80ULL)==0x7e80ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"HSETP2 "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfe80ULL)==0x7e80ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"HSETP2 "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfe00ULL)==0x3000ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"FSET "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfe00ULL)==0x3000ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"FSET "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfe00ULL)==0x1c00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"IADD32I "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfe00ULL)==0x200ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"LOP3 "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfe00ULL)==0x4e00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"XMAD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfe00ULL)==0x3a00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VMNMX "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfe00ULL)==0x3a00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VMNMX "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfe00ULL)==0x3a00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VMNMX "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfe00ULL)==0x3a00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VMNMX "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfe00ULL)==0x3a00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VMNMX "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfe00ULL)==0x3a00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VMNMX "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfe00ULL)==0x3a00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VMNMX "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfe00ULL)==0x3a00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VMNMX "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfe00ULL)==0x3a00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VMNMX "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfe00ULL)==0x3a00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VMNMX "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfe00ULL)==0x3a00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VMNMX "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfe00ULL)==0x3a00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VMNMX "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfe00ULL)==0x3a00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VMNMX "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfe00ULL)==0x3a00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VMNMX "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfe00ULL)==0x3a00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VMNMX "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfe00ULL)==0x4000ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSET "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfe00ULL)==0x4000ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSET "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfe00ULL)==0x4000ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSET "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfe00ULL)==0x4000ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSET "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfe00ULL)==0x4000ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSET "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfe00ULL)==0x4000ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSET "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfe00ULL)==0x4000ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSET "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfe00ULL)==0x4000ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSET "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfe00ULL)==0x4000ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSET "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfe00ULL)==0x4000ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSET "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfe00ULL)==0x4000ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSET "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfe00ULL)==0x4000ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSET "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfe00ULL)==0x4000ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSET "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfe00ULL)==0x4000ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSET "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfe00ULL)==0x4000ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VSET "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfe00ULL)==0xd800ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"TEXS "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfe00ULL)==0xd800ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"TEXS "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfe00ULL)==0xd000ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"TEXS "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfe00ULL)==0xd000ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"TEXS "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfe00ULL)==0xda00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"TLDS "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfe00ULL)==0xda00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"TLDS "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfe00ULL)==0xd200ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"TLDS "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfe00ULL)==0xd200ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"TLDS "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfe00ULL)==0x0ULL)return s+"HMUL2_32I "; + if(((inst>>48)&0xfe00ULL)==0x0ULL)return s+"HADD2_32I "; + if(((inst>>48)&0xfe00ULL)==0x2800ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"HFMA2_32I "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfe00ULL)==0x2800ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"HFMA2_32I "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfc00ULL)==0xc00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"FFMA32I "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfc00ULL)==0xc00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"FFMA32I "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfc00ULL)==0x800ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"FADD32I "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfc00ULL)==0x1000ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"IMAD32I "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfc00ULL)==0x1400ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ISCADD32I "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfc00ULL)==0x400ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"LOP32I "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfc00ULL)==0x400ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"LOP3 "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfe00ULL)==0x200ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"LOP3 "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfc00ULL)==0x3c00ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"LOP3 "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfc00ULL)==0x2000ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VADD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfc00ULL)==0x2000ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VADD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfc00ULL)==0x2000ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VADD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfc00ULL)==0x2000ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VADD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfc00ULL)==0x2000ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VADD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfc00ULL)==0x2000ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VADD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfc00ULL)==0x2000ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VADD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfc00ULL)==0x2000ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VADD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfc00ULL)==0x2000ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VADD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xfc00ULL)==0x2000ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VADD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfc00ULL)==0x2000ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VADD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfc00ULL)==0x2000ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VADD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfc00ULL)==0x2000ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VADD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfc00ULL)==0x2000ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VADD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfc00ULL)==0x2000ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"VADD "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xfc00ULL)==0x1800ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"LEA "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xf880ULL)==0x0ULL)return s+"HFMA2 "; + if(((inst>>48)&0xf880ULL)==0x0ULL)return s+"HFMA2 "; + if(((inst>>48)&0xf880ULL)==0x6080ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"HFMA2 "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "; + if(((inst>>48)&0xf800ULL)==0xc000ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"TEX "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xf800ULL)==0xc000ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"TEX "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xf800ULL)==0xc800ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"TLD4 "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xf800ULL)==0xc800ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"TLD4 "+"R"+std::to_string((inst&0xff)>>0x0)+" "+"R"+std::to_string((inst&0xffff)>>0x8)+" "+"R"+std::to_string((inst&0xfffffff)>>0x14)+" "; + if(((inst>>48)&0xe000ULL)==0x8000ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"LD "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xe000ULL)==0x8000ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"LD "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xe000ULL)==0xa000ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ST "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + if(((inst>>48)&0xe000ULL)==0xa000ULL)return s+(((inst&0x7ffff)>>0x10)?"":"!")+"ST "+"R"+std::to_string((inst&0xff)>>0x0)+" "; + return "?";} +} diff --git a/tools/maxwell-disas/main.cpp b/tools/maxwell-disas/main.cpp new file mode 100644 index 0000000000..0feee75824 --- /dev/null +++ b/tools/maxwell-disas/main.cpp @@ -0,0 +1,221 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "common/assert.h" +#include +#include +#include + +enum class Opcode { +#define INST(name, cute, encode) name, +#include "shader_recompiler/frontend/maxwell/maxwell.inc" +#undef INST +}; + +consteval std::pair MaskValueFromEncoding(const char data[20]) noexcept { + u64 mask = 0, value = 0, bit = u64(1) << 63; + for (int i = 0; i < 20; ++i) + switch (data[i]) { + case '0': + mask |= bit; + bit >>= 1; + break; + case '1': + mask |= bit; + value |= bit; + bit >>= 1; + break; + case '-': + bit >>= 1; + break; + default: + break; + } + return { mask, value }; +} + +Opcode Decode(u64 insn) { +#define INST(name, cute, encode) \ + if (auto const p = MaskValueFromEncoding(encode); (insn & p.first) == p.second) \ + return Opcode::name; +#include "shader_recompiler/frontend/maxwell/maxwell.inc" +#undef INST + ASSERT_MSG(false, "Invalid insn 0x{:016x}", insn); + return Opcode::NOP; +} + +const char* NameOf(Opcode opcode) { + constexpr const char* NAME_TABLE[] = { +#define INST(name, cute, encode) cute, +#include "shader_recompiler/frontend/maxwell/maxwell.inc" +#undef INST + }; + ASSERT_MSG(size_t(opcode) < sizeof(NAME_TABLE) / sizeof(NAME_TABLE[0]), "Invalid opcode with raw value {}", int(opcode)); + return NAME_TABLE[size_t(opcode)]; +} + +namespace Shader::Maxwell { +std::string_view SpecialRegGetName(size_t i) { + switch (i) { + case 0: return "SR_LANEID"; + case 1: return "SR_CLOCK"; + case 2: return "SR_VIRTCFG"; + case 3: return "SR_VIRTID"; + case 4: return "SR_PM0"; + case 5: return "SR_PM1"; + case 6: return "SR_PM2"; + case 7: return "SR_PM3"; + case 8: return "SR_PM4"; + case 9: return "SR_PM5"; + case 10: return "SR_PM6"; + case 11: return "SR_PM7"; + case 12: return "SR_?"; + case 13: return "SR_?"; + case 14: return "SR_?"; + case 15: return "SR_ORDERING_TICKET"; + case 16: return "SR_PRIM_TYPE"; + case 17: return "SR_INVOCATION_ID"; + case 18: return "SR_Y_DIRECTION"; + case 19: return "SR_THREAD_KILL"; + case 20: return "SM_SHADER_TYPE"; + case 21: return "SR_DIRECTCBEWRITEADDRESSLOW"; + case 22: return "SR_DIRECTCBEWRITEADDRESSHIGH"; + case 23: return "SR_DIRECTCBEWRITEENABLED"; + case 24: return "SR_MACHINE_ID_0"; + case 25: return "SR_MACHINE_ID_1"; + case 26: return "SR_MACHINE_ID_2"; + case 27: return "SR_MACHINE_ID_3"; + case 28: return "SR_AFFINITY"; + case 29: return "SR_INVOCATION_INFO"; + case 30: return "SR_WSCALEFACTOR_XY"; + case 31: return "SR_WSCALEFACTOR_Z"; + case 32: return "SR_TID"; + case 33: return "SR_TID_X"; + case 34: return "SR_TID_Y"; + case 35: return "SR_TID_Z"; + case 36: return "SR_CTA_PARAM"; + case 37: return "SR_CTAID_X"; + case 38: return "SR_CTAID_Y"; + case 39: return "SR_CTAID_Z"; + case 40: return "SR_NTID"; + case 41: return "SR_CirQueueIncrMinusOne"; + case 42: return "SR_NLATC"; + case 43: return "SR_?"; + case 44: return "SR_SM_SPA_VERSION"; + case 45: return "SR_MULTIPASSSHADERINFO"; + case 46: return "SR_LWINHI"; + case 47: return "SR_SWINHI"; + case 48: return "SR_SWINLO"; + case 49: return "SR_SWINSZ"; + case 50: return "SR_SMEMSZ"; + case 51: return "SR_SMEMBANKS"; + case 52: return "SR_LWINLO"; + case 53: return "SR_LWINSZ"; + case 54: return "SR_LMEMLOSZ"; + case 55: return "SR_LMEMHIOFF"; + case 56: return "SR_EQMASK"; + case 57: return "SR_LTMASK"; + case 58: return "SR_LEMASK"; + case 59: return "SR_GTMASK"; + case 60: return "SR_GEMASK"; + case 61: return "SR_REGALLOC"; + case 62: return "SR_BARRIERALLOC"; + case 63: return "SR_?"; + case 64: return "SR_GLOBALERRORSTATUS"; + case 65: return "SR_?"; + case 66: return "SR_WARPERRORSTATUS"; + case 67: return "SR_WARPERRORSTATUSCLEAR"; + case 68: return "SR_?"; + case 69: return "SR_?"; + case 70: return "SR_?"; + case 71: return "SR_?"; + case 72: return "SR_PM_HI0"; + case 73: return "SR_PM_HI1"; + case 74: return "SR_PM_HI2"; + case 75: return "SR_PM_HI3"; + case 76: return "SR_PM_HI4"; + case 77: return "SR_PM_HI5"; + case 78: return "SR_PM_HI6"; + case 79: return "SR_PM_HI7"; + case 80: return "SR_CLOCKLO"; + case 81: return "SR_CLOCKHI"; + case 82: return "SR_GLOBALTIMERLO"; + case 83: return "SR_GLOBALTIMERHI"; + case 84: return "SR_?"; + case 85: return "SR_?"; + case 86: return "SR_?"; + case 87: return "SR_?"; + case 88: return "SR_?"; + case 89: return "SR_?"; + case 90: return "SR_?"; + case 91: return "SR_?"; + case 92: return "SR_?"; + case 93: return "SR_?"; + case 94: return "SR_?"; + case 95: return "SR_?"; + case 96: return "SR_HWTASKID"; + case 97: return "SR_CIRCULARQUEUEENTRYINDEX"; + case 98: return "SR_CIRCULARQUEUEENTRYADDRESSLOW"; + case 99: return "SR_CIRCULARQUEUEENTRYADDRESSHIGH"; + default: return "SR_??"; } +} +} +#include "generated.cpp" + +int DisasReferenceImpl(int argc, char *argv[]) { + std::vector code; + FILE *fp = fopen(argv[1], "rb"); + if (fp != NULL) { + struct stat st; + fstat(fileno(fp), &st); + auto const words = (size_t(st.st_size) / sizeof(uint64_t)); + code.resize(words + 1); + fread(code.data(), sizeof(uint64_t), words, fp); + fclose(fp); + } + for (size_t i = 0; i < code.size(); ++i) { + printf("%016lx\t%-40s\n", code[i] + , Shader::Maxwell::DissasemblyFormat(code[i]).data() + ); + } + return EXIT_SUCCESS; +} + +int DisasShaderRecompilerImpl(int argc, char *argv[]) { + std::vector code; + FILE *fp = fopen(argv[1], "rb"); + if (fp != NULL) { + struct stat st; + fstat(fileno(fp), &st); + auto const words = (size_t(st.st_size) / sizeof(u64)); + code.resize(words + 1); + fread(code.data(), sizeof(u64), words, fp); + fclose(fp); + } + + for (size_t i = 0; i < code.size(); ++i) { + auto const opcode = Decode(code[i]); + printf("%016lx\t%s\n", code[i], NameOf(opcode)); + } + return EXIT_SUCCESS; +} + +int main(int argc, char *argv[]) { + if (argc < 2) { + printf( + "usage: %s [input file] [-n/i]\n" + "Specify -n to use a disassembler that is NOT tied to the shader recompiler\n" + "aka. a reference disassembler\n" + , argv[0]); + return EXIT_FAILURE; + } + + //DumpProgram + + if (argc >= 3) { + if (::strcmp(argv[2], "-n") == 0 || ::strcmp(argv[2], "--new") == 0) { + return DisasReferenceImpl(argc, argv); + } + } + return DisasShaderRecompilerImpl(argc, argv); +} diff --git a/tools/maxwell-ir/CMakeLists.txt b/tools/maxwell-ir/CMakeLists.txt new file mode 100644 index 0000000000..45441dcae1 --- /dev/null +++ b/tools/maxwell-ir/CMakeLists.txt @@ -0,0 +1,9 @@ +# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +# SPDX-License-Identifier: GPL-3.0-or-later +add_executable(maxwell-ir main.cpp) +target_link_libraries(maxwell-ir PRIVATE common shader_recompiler Threads::Threads) +target_include_directories(maxwell-ir PRIVATE ${CMAKE_SOURCE_DIR}/src) +if(UNIX AND NOT APPLE) + install(TARGETS maxwell-ir) +endif() +create_target_directory_groups(maxwell-ir) diff --git a/tools/maxwell-ir/main.cpp b/tools/maxwell-ir/main.cpp new file mode 100644 index 0000000000..66aeaeac40 --- /dev/null +++ b/tools/maxwell-ir/main.cpp @@ -0,0 +1,59 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include +#include +#include + +#include "shader_recompiler/frontend/ir/basic_block.h" +#include "shader_recompiler/frontend/maxwell/control_flow.h" +#include "shader_recompiler/frontend/maxwell/translate_program.h" +#include "shader_recompiler/host_translate_info.h" +#include "shader_recompiler/object_pool.h" +#include "../maxwell-disas/file_environment.h" + +int IrShaderRecompilerImpl(int argc, char *argv[]) { + size_t cfg_offset = 0; + + Shader::ObjectPool inst_pool; + Shader::ObjectPool block_pool; + Shader::ObjectPool cfg_blocks; + FileEnvironment env; + + FILE *fp = fopen(argv[1], "rb"); + if (fp != NULL) { + struct stat st; + fstat(fileno(fp), &st); + auto const words = (st.st_size / sizeof(u64)); + env.code.resize(words + 1); + fread(env.code.data(), sizeof(u64), words, fp); + fclose(fp); + } + + env.read_highest = env.read_lowest + env.code.size() * sizeof(u64); + + Shader::Maxwell::Flow::CFG cfg(env, cfg_blocks, cfg_offset); + + Shader::HostTranslateInfo host_info; + host_info.support_float64 = true; + host_info.support_float16 = true; + host_info.support_int64 = true; + host_info.needs_demote_reorder = true; + host_info.support_snorm_render_buffer = true; + host_info.support_viewport_index_layer = true; + host_info.support_geometry_shader_passthrough = true; + host_info.support_conditional_barrier = true; + host_info.min_ssbo_alignment = 0; + auto program = Shader::Maxwell::TranslateProgram(inst_pool, block_pool, env, cfg, host_info); + auto const dumped_ir = Shader::IR::DumpProgram(program); + std::printf("%s\n", dumped_ir.c_str()); + return EXIT_SUCCESS; +} + +int main(int argc, char *argv[]) { + if (argc < 2) { + printf("usage: %s [input file]\n", argv[0]); + return EXIT_FAILURE; + } + return IrShaderRecompilerImpl(argc, argv); +} diff --git a/tools/maxwell-spirv/CMakeLists.txt b/tools/maxwell-spirv/CMakeLists.txt new file mode 100644 index 0000000000..819373712e --- /dev/null +++ b/tools/maxwell-spirv/CMakeLists.txt @@ -0,0 +1,13 @@ +# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +# SPDX-License-Identifier: GPL-3.0-or-later +add_executable(maxwell-spirv + main.cpp + spirv_recompiler_impl.cpp + spirv_reference_impl.cpp +) +target_link_libraries(maxwell-spirv PRIVATE common shader_recompiler Threads::Threads) +target_include_directories(maxwell-spirv PRIVATE ${CMAKE_SOURCE_DIR}/src) +if(UNIX AND NOT APPLE) + install(TARGETS maxwell-spirv) +endif() +create_target_directory_groups(maxwell-spirv) diff --git a/tools/maxwell-spirv/main.cpp b/tools/maxwell-spirv/main.cpp new file mode 100644 index 0000000000..f9245d436e --- /dev/null +++ b/tools/maxwell-spirv/main.cpp @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include +#include +#include + +int SpirvReferenceImpl(int argc, char *argv[]); +int SpirvShaderRecompilerImpl(int argc, char *argv[]); + +int main(int argc, char *argv[]) { + if (argc < 2) { + printf("usage: %s [input file]\n" + "Specify -n to use a recompiler that is NOT tied to the shader recompiler\n" + "aka. a reference recompiler\n" + "RAW SPIRV CODE WILL BE SENT TO STDOUT!\n", argv[0]); + return EXIT_FAILURE; + } + if (argc >= 3) { + if (::strcmp(argv[2], "-n") == 0 + || ::strcmp(argv[2], "--new") == 0) { + return SpirvReferenceImpl(argc, argv); + } + } + return SpirvShaderRecompilerImpl(argc, argv); +} diff --git a/tools/maxwell-spirv/spirv_recompiler_impl.cpp b/tools/maxwell-spirv/spirv_recompiler_impl.cpp new file mode 100644 index 0000000000..55830abe39 --- /dev/null +++ b/tools/maxwell-spirv/spirv_recompiler_impl.cpp @@ -0,0 +1,67 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include +#include +#include "shader_recompiler/backend/spirv/emit_spirv.h" +#include "shader_recompiler/environment.h" +#include "shader_recompiler/frontend/maxwell/control_flow.h" +#include "shader_recompiler/frontend/maxwell/translate_program.h" +#include "shader_recompiler/host_translate_info.h" +#include "shader_recompiler/object_pool.h" +#include "shader_recompiler/profile.h" +#include "shader_recompiler/runtime_info.h" + +#include "../maxwell-disas/file_environment.h" + +int SpirvShaderRecompilerImpl(int argc, char *argv[]) { + if (argc != 2) { + printf("usage: %s [input file] [-n]\n" + "RAW SPIRV CODE WILL BE SENT TO STDOUT!\n", argv[0]); + return EXIT_FAILURE; + } + + size_t cfg_offset = 0; + + Shader::ObjectPool inst_pool; + Shader::ObjectPool block_pool; + Shader::ObjectPool cfg_blocks; + FileEnvironment env; + + FILE *fp = fopen(argv[1], "rb"); + if (fp != NULL) { + struct stat st; + fstat(fileno(fp), &st); + auto const words = (st.st_size / sizeof(u64)); + env.code.resize(words + 1); + fread(env.code.data(), sizeof(u64), words, fp); + fclose(fp); + } + + env.read_highest = env.read_lowest + env.code.size() * sizeof(u64); + + Shader::Maxwell::Flow::CFG cfg(env, cfg_blocks, cfg_offset); + + Shader::HostTranslateInfo host_info; + host_info.support_float64 = true; + host_info.support_float16 = true; + host_info.support_int64 = true; + host_info.needs_demote_reorder = true; + host_info.support_snorm_render_buffer = true; + host_info.support_viewport_index_layer = true; + host_info.support_geometry_shader_passthrough = true; + host_info.support_conditional_barrier = true; + host_info.min_ssbo_alignment = 0; + auto program = Shader::Maxwell::TranslateProgram(inst_pool, block_pool, env, cfg, host_info); + + // IR::Program TranslateProgram(ObjectPool& inst_pool, ObjectPool& block_pool, + // Environment& env, Flow::CFG& cfg, const HostTranslateInfo& host_info) + // std::vector EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_info, + // IR::Program& program, Bindings& bindings, bool optimize) + Shader::Profile profile{}; + Shader::RuntimeInfo runtime_info; + auto const spirv_pgm = Shader::Backend::SPIRV::EmitSPIRV(profile, program, true); + fwrite(spirv_pgm.data(), sizeof(u64), spirv_pgm.size(), stdout); + + return EXIT_SUCCESS; +} diff --git a/tools/maxwell-spirv/spirv_reference_impl.cpp b/tools/maxwell-spirv/spirv_reference_impl.cpp new file mode 100644 index 0000000000..db4d0994e4 --- /dev/null +++ b/tools/maxwell-spirv/spirv_reference_impl.cpp @@ -0,0 +1,15 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "common/bit_field.h" +#include "common/common_types.h" +#include "spirv/unified1/spirv.hpp11" +#include +#include +#include +#include + +int ReferenceImpl(int argc, char *argv[]) { + //todo + return EXIT_SUCCESS; +} diff --git a/tools/optimize-assets.sh b/tools/optimize-assets.sh index 70fa2375a5..a9b2888648 100755 --- a/tools/optimize-assets.sh +++ b/tools/optimize-assets.sh @@ -1,6 +1,6 @@ #!/bin/sh -e -# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project # SPDX-License-Identifier: GPL-3.0-or-later which optipng || exit diff --git a/tools/stale-translations.sh b/tools/stale-translations.sh index 3a9362a252..63ea87b19a 100755 --- a/tools/stale-translations.sh +++ b/tools/stale-translations.sh @@ -1,6 +1,6 @@ #!/bin/sh -e -# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project # SPDX-License-Identifier: GPL-3.0-or-later ANDROID=src/android/app/src/main diff --git a/tools/svc_generator.py b/tools/svc_generator.py index d00fe0ba63..9a99c3ba51 100755 --- a/tools/svc_generator.py +++ b/tools/svc_generator.py @@ -1,5 +1,5 @@ -#!/usr/bin/python3 -# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +#!/usr/bin/env python3 +# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project # SPDX-License-Identifier: GPL-3.0-or-later # SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project # SPDX-License-Identifier: GPL-2.0-or-later @@ -212,7 +212,7 @@ def emit_size_check(): if type != "void": lines.append(f"static_assert(sizeof({type}) == {size});") - return "\n".join(lines) + return f"\n{lines}" # Replaces a type with an arch-specific one, if it exists. @@ -423,7 +423,7 @@ def emit_lines(lines, indent=' '): output_lines.append(line) first = False - return "\n".join(output_lines) + return f"\n{output_lines}" # Emit a C++ function to wrap a guest SVC. @@ -601,7 +601,7 @@ def emit_call(bitness, names, suffix): lines.append(f"{indent}}}") lines.append("}") - return "\n".join(lines) + return f"\n{lines}" def build_fn_declaration(return_type, name, arguments): @@ -623,7 +623,7 @@ def build_enum_declarations(): lines.append(f"{indent}{name} = {hex(imm)},") lines.append("};") - return "\n".join(lines) + return f"\n{lines}" def main(): @@ -665,11 +665,11 @@ def main(): with open("src/core/hle/kernel/svc.h", "w") as f: f.write(COPYRIGHT) f.write(PROLOGUE_H) - f.write("\n".join(svc_fw_declarations)) + f.write(f"\n{svc_fw_declarations}") f.write("\n\n") - f.write("\n".join(arch_fw_declarations[BIT_32])) + f.write(f"\n{arch_fw_declarations[BIT_32]}") f.write("\n\n") - f.write("\n".join(arch_fw_declarations[BIT_64])) + f.write(f"\n{arch_fw_declarations[BIT_64]}") f.write("\n\n") f.write(enum_decls) f.write(EPILOGUE_H) @@ -679,7 +679,7 @@ def main(): f.write(PROLOGUE_CPP) f.write(emit_size_check()) f.write("\n\n") - f.write("\n\n".join(wrapper_fns)) + f.write(f"\n\n{wrapper_fns}") f.write("\n\n") f.write(call_32) f.write("\n\n") diff --git a/tools/translations/qt-source.sh b/tools/translations/qt-source.sh index 89c881d8c0..707ec2783c 100755 --- a/tools/translations/qt-source.sh +++ b/tools/translations/qt-source.sh @@ -1,6 +1,6 @@ -#!/bin/sh +#!/bin/sh -e -# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project # SPDX-License-Identifier: GPL-3.0-or-later SOURCES=$(find src/yuzu src/qt_common -type f \( -name "*.ui" -o -name "*.cpp" -o -name "*.h" -o -name "*.plist" \)) diff --git a/tools/translations/update-translations.sh b/tools/translations/update-translations.sh index 8bcd79142f..6ed740140d 100755 --- a/tools/translations/update-translations.sh +++ b/tools/translations/update-translations.sh @@ -1,6 +1,6 @@ -#!/bin/sh +#!/bin/sh -e -# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project # SPDX-License-Identifier: GPL-3.0-or-later command -v tx-cli && COMMAND=tx-cli diff --git a/tools/unused-strings.sh b/tools/unused-strings.sh index 1165a203aa..10f6a63565 100755 --- a/tools/unused-strings.sh +++ b/tools/unused-strings.sh @@ -1,6 +1,6 @@ #!/bin/sh -e -# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project # SPDX-License-Identifier: GPL-3.0-or-later ANDROID=src/android/app/src/main diff --git a/tools/windows/install-vulkan-sdk.sh b/tools/windows/install-vulkan-sdk.sh index 0f136748cf..2151363793 100644 --- a/tools/windows/install-vulkan-sdk.sh +++ b/tools/windows/install-vulkan-sdk.sh @@ -1,4 +1,4 @@ -#!/usr/bin/sh +#!/bin/sh -e # SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project # SPDX-License-Identifier: GPL-3.0-or-later