Compare commits

..

5 Commits

Author SHA1 Message Date
lizzie d8430cbb0f Trigger Build 2026-07-09 23:08:57 +00:00
lizzie 71a9a93a93 fx 2026-07-09 23:08:57 +00:00
lizzie b70341211b brain no work 2026-07-09 23:08:57 +00:00
lizzie dab26a3eae implodes 2026-07-09 23:08:57 +00:00
lizzie e0119df3a1 [audio_core/dsp] nuke libopus, use ffmpeg for opus decoding
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-07-09 23:08:57 +00:00
168 changed files with 22814 additions and 19460 deletions
-5
View File
@@ -1,5 +0,0 @@
- [ ] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [ ] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [ ] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.
-------------------
@@ -1,28 +0,0 @@
From cc15da16e533b2a801934eab2dfeaf3c3949a1dc Mon Sep 17 00:00:00 2001
From: crueter <crueter@eden-emu.dev>
Date: Mon, 8 Sep 2025 12:28:55 -0400
Subject: [PATCH] [cmake] disable NEON runtime check on clang-cl
When enabling runtime NEON checking for clang-cl, the linker would error out with `undefined symbol: __emit`, since clang doesn't actually implement this instruction. Therefore it makes sense to disable the runtime check by default on this platform, until either this is fixed or a clang-cl compatible intrinsic check is added (I don't have enough knowledge of MSVC to do this)
---
cmake/OpusConfig.cmake | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/cmake/OpusConfig.cmake b/cmake/OpusConfig.cmake
index e9319fbad..d0f459e88 100644
--- a/cmake/OpusConfig.cmake
+++ b/cmake/OpusConfig.cmake
@@ -71,7 +71,12 @@ elseif(OPUS_CPU_ARM AND NOT OPUS_DISABLE_INTRINSICS)
opus_detect_neon(COMPILER_SUPPORT_NEON)
if(COMPILER_SUPPORT_NEON)
option(OPUS_USE_NEON "Option to enable NEON" ON)
- option(OPUS_MAY_HAVE_NEON "Does runtime check for neon support" ON)
+ if (MSVC AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
+ set(NEON_RUNTIME_CHECK_DEFAULT OFF)
+ else()
+ set(NEON_RUNTIME_CHECK_DEFAULT ON)
+ endif()
+ option(OPUS_MAY_HAVE_NEON "Does runtime check for neon support" ${NEON_RUNTIME_CHECK_DEFAULT})
option(OPUS_PRESUME_NEON "Assume target CPU has NEON support" OFF)
if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
set(OPUS_PRESUME_NEON ON)
-153
View File
@@ -1,153 +0,0 @@
From bf455b67b4eaa446ffae5d25410b141b7b1b1082 Mon Sep 17 00:00:00 2001
From: crueter <crueter@eden-emu.dev>
Date: Mon, 8 Sep 2025 12:08:20 -0400
Subject: [PATCH] [cmake] `OPUS_INSTALL` option; only default install if root
project
Signed-off-by: crueter <crueter@eden-emu.dev>
---
CMakeLists.txt | 112 ++++++++++++++++++++++++++++---------------------
1 file changed, 64 insertions(+), 48 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index fcf034b19..08b5e16f8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -4,6 +4,13 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
include(OpusPackageVersion)
get_package_version(PACKAGE_VERSION PROJECT_VERSION)
+# root project detection
+if(DEFINED PROJECT_NAME)
+ set(root_project OFF)
+else()
+ set(root_project ON)
+endif()
+
project(Opus LANGUAGES C VERSION ${PROJECT_VERSION})
include(OpusFunctions)
@@ -83,12 +90,16 @@ set(OPUS_DNN_FLOAT_DEBUG_HELP_STR "Run DNN computations as float for debugging p
option(OPUS_DNN_FLOAT_DEBUG ${OPUS_DNN_FLOAT_DEBUG_HELP_STR} OFF)
add_feature_info(OPUS_DNN_FLOAT_DEBUG OPUS_DNN_FLOAT_DEBUG ${OPUS_DNN_FLOAT_DEBUG_HELP_STR})
+set(OPUS_INSTALL_HELP_STR "Install Opus targets")
+option(OPUS_INSTALL ${OPUS_INSTALL_HELP_STR} ${root_project})
+add_feature_info(OPUS_INSTALL OPUS_INSTALL ${OPUS_INSTALL_HELP_STR})
+
set(OPUS_INSTALL_PKG_CONFIG_MODULE_HELP_STR "install pkg-config module.")
-option(OPUS_INSTALL_PKG_CONFIG_MODULE ${OPUS_INSTALL_PKG_CONFIG_MODULE_HELP_STR} ON)
+option(OPUS_INSTALL_PKG_CONFIG_MODULE ${OPUS_INSTALL_PKG_CONFIG_MODULE_HELP_STR} ${OPUS_INSTALL})
add_feature_info(OPUS_INSTALL_PKG_CONFIG_MODULE OPUS_INSTALL_PKG_CONFIG_MODULE ${OPUS_INSTALL_PKG_CONFIG_MODULE_HELP_STR})
set(OPUS_INSTALL_CMAKE_CONFIG_MODULE_HELP_STR "install CMake package config module.")
-option(OPUS_INSTALL_CMAKE_CONFIG_MODULE ${OPUS_INSTALL_CMAKE_CONFIG_MODULE_HELP_STR} ON)
+option(OPUS_INSTALL_CMAKE_CONFIG_MODULE ${OPUS_INSTALL_CMAKE_CONFIG_MODULE_HELP_STR} ${OPUS_INSTALL})
add_feature_info(OPUS_INSTALL_CMAKE_CONFIG_MODULE OPUS_INSTALL_CMAKE_CONFIG_MODULE ${OPUS_INSTALL_CMAKE_CONFIG_MODULE_HELP_STR})
set(OPUS_DRED_HELP_STR "enable DRED.")
@@ -613,53 +624,58 @@ if(OPUS_BUILD_FRAMEWORK)
OUTPUT_NAME Opus)
endif()
-install(TARGETS opus
- EXPORT OpusTargets
- ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
- LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
- RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
- FRAMEWORK DESTINATION ${CMAKE_INSTALL_PREFIX}
- PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/opus)
-
-if(OPUS_INSTALL_PKG_CONFIG_MODULE)
- set(prefix ${CMAKE_INSTALL_PREFIX})
- set(exec_prefix ${CMAKE_INSTALL_PREFIX})
- set(libdir ${CMAKE_INSTALL_FULL_LIBDIR})
- set(includedir ${CMAKE_INSTALL_FULL_INCLUDEDIR})
- set(VERSION ${PACKAGE_VERSION})
- if(HAVE_LIBM)
- set(LIBM "-lm")
+if (OPUS_INSTALL)
+ install(TARGETS opus
+ EXPORT OpusTargets
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ FRAMEWORK DESTINATION ${CMAKE_INSTALL_PREFIX}
+ PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/opus)
+
+ if(OPUS_INSTALL_PKG_CONFIG_MODULE)
+ set(prefix ${CMAKE_INSTALL_PREFIX})
+ set(exec_prefix ${CMAKE_INSTALL_PREFIX})
+ set(libdir ${CMAKE_INSTALL_FULL_LIBDIR})
+ set(includedir ${CMAKE_INSTALL_FULL_INCLUDEDIR})
+ set(VERSION ${PACKAGE_VERSION})
+ if(HAVE_LIBM)
+ set(LIBM "-lm")
+ endif()
+ configure_file(opus.pc.in opus.pc)
+ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/opus.pc
+ DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
+ endif()
+
+ if(OPUS_INSTALL_CMAKE_CONFIG_MODULE)
+ set(CPACK_GENERATOR TGZ)
+ include(CPack)
+ set(CMAKE_INSTALL_PACKAGEDIR ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME})
+ install(EXPORT OpusTargets
+ NAMESPACE Opus::
+ DESTINATION ${CMAKE_INSTALL_PACKAGEDIR})
+
+ include(CMakePackageConfigHelpers)
+
+ set(INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_INCLUDEDIR})
+ configure_package_config_file(
+ ${PROJECT_SOURCE_DIR}/cmake/OpusConfig.cmake.in
+ OpusConfig.cmake
+ INSTALL_DESTINATION
+ ${CMAKE_INSTALL_PACKAGEDIR}
+ PATH_VARS
+ INCLUDE_INSTALL_DIR
+ INSTALL_PREFIX
+ ${CMAKE_INSTALL_PREFIX})
+
+ write_basic_package_version_file(OpusConfigVersion.cmake
+ VERSION ${PROJECT_VERSION}
+ COMPATIBILITY SameMajorVersion)
+
+ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/OpusConfig.cmake
+ ${CMAKE_CURRENT_BINARY_DIR}/OpusConfigVersion.cmake
+ DESTINATION ${CMAKE_INSTALL_PACKAGEDIR})
endif()
- configure_file(opus.pc.in opus.pc)
- install(FILES ${CMAKE_CURRENT_BINARY_DIR}/opus.pc
- DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
-endif()
-
-if(OPUS_INSTALL_CMAKE_CONFIG_MODULE)
- set(CPACK_GENERATOR TGZ)
- include(CPack)
- set(CMAKE_INSTALL_PACKAGEDIR ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME})
- install(EXPORT OpusTargets
- NAMESPACE Opus::
- DESTINATION ${CMAKE_INSTALL_PACKAGEDIR})
-
- include(CMakePackageConfigHelpers)
-
- set(INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_INCLUDEDIR})
- configure_package_config_file(${PROJECT_SOURCE_DIR}/cmake/OpusConfig.cmake.in
- OpusConfig.cmake
- INSTALL_DESTINATION
- ${CMAKE_INSTALL_PACKAGEDIR}
- PATH_VARS
- INCLUDE_INSTALL_DIR
- INSTALL_PREFIX
- ${CMAKE_INSTALL_PREFIX})
- write_basic_package_version_file(OpusConfigVersion.cmake
- VERSION ${PROJECT_VERSION}
- COMPATIBILITY SameMajorVersion)
- install(FILES ${CMAKE_CURRENT_BINARY_DIR}/OpusConfig.cmake
- ${CMAKE_CURRENT_BINARY_DIR}/OpusConfigVersion.cmake
- DESTINATION ${CMAKE_INSTALL_PACKAGEDIR})
endif()
if(OPUS_BUILD_PROGRAMS)
+5
View File
@@ -112,6 +112,11 @@ Files: src/yuzu/*.ui
Copyright: 2018-2022 yuzu Emulator Project
License: GPL-2.0-or-later
Files: src/yuzu/compatdb.ui
src/yuzu/main.ui
Copyright: 2014-2017 Citra Emulator Project
License: GPL-2.0-or-later
Files: src/yuzu/loading_screen.ui
Copyright: 2019 James Rowe <jroweboy@gmail.com>
License: GPL-2.0-or-later
+19 -15
View File
@@ -281,6 +281,25 @@ if(EXISTS ${PROJECT_SOURCE_DIR}/hooks/pre-commit AND NOT EXISTS ${PROJECT_SOURCE
endif()
endif()
set(compat_base dist/compatibility_list/compatibility_list)
set(compat_qrc ${compat_base}.qrc)
set(compat_json ${compat_base}.json)
configure_file(${PROJECT_SOURCE_DIR}/${compat_qrc}
${PROJECT_BINARY_DIR}/${compat_qrc}
COPYONLY)
if (EXISTS ${PROJECT_SOURCE_DIR}/${compat_json})
configure_file("${PROJECT_SOURCE_DIR}/${compat_json}"
"${PROJECT_BINARY_DIR}/${compat_json}"
COPYONLY)
endif()
# TODO: Compat list download
if (NOT EXISTS ${PROJECT_BINARY_DIR}/${compat_json})
file(WRITE ${PROJECT_BINARY_DIR}/${compat_json} "")
endif()
if (ARCHITECTURE_arm64 AND (ANDROID OR PLATFORM_LINUX))
set(HAS_NCE 1)
add_compile_definitions(HAS_NCE=1)
@@ -445,21 +464,6 @@ if (NOT YUZU_STATIC_ROOM)
if (ZLIB_ADDED)
add_library(ZLIB::ZLIB ALIAS zlibstatic)
endif()
# Opus
AddJsonPackage(opus)
if (Opus_ADDED)
if (MSVC AND CXX_CLANG)
target_compile_options(opus PRIVATE
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-implicit-function-declaration>
)
endif()
endif()
if (NOT TARGET Opus::opus)
add_library(Opus::opus ALIAS opus)
endif()
endif()
if(NOT TARGET Boost::headers)
+89 -35
View File
@@ -95,7 +95,7 @@ macro(echo)
execute_process(COMMAND ${CMAKE_COMMAND} -E echo
"${message}")
else()
message(DEBUG "${message}")
message(STATUS "${message}")
endif()
endmacro()
@@ -120,6 +120,47 @@ macro(sleep time)
execute_process(COMMAND ${CMAKE_COMMAND} -E sleep ${time})
endmacro()
# Analogous to GNU mktemp, with fallbacks
function(mktempdir out)
# shell out to system mktemp if available
find_program(MKTEMP_EXECUTABLE mktemp)
if (MKTEMP_EXECUTABLE)
execute_process(COMMAND mktemp -d
OUTPUT_VARIABLE dir
OUTPUT_STRIP_TRAILING_WHITESPACE
RESULT_VARIABLE ret)
if (ret EQUAL 0)
set(${out} "${dir}" PARENT_SCOPE)
return()
endif()
endif()
string(RANDOM LENGTH 10 rand_str)
set(tmp_str "tmp.${rand_str}")
# create something in /tmp if it exists
if(EXISTS "/tmp" AND IS_DIRECTORY "/tmp")
set(dir "/tmp/${tmp_str}")
file(MAKE_DIRECTORY "${dir}" RESULT res)
if (res EQUAL 0)
set(${out} "${dir}" PARENT_SCOPE)
return()
endif()
endif()
# tmpdir does not exist, extremely legacy mode
set(dir "${CMAKE_CURRENT_LIST_DIR}/.tmp/${tmp_str}")
file(MAKE_DIRECTORY "${dir}" RESULT res)
if (res EQUAL 0)
set(${out} "${dir}" PARENT_SCOPE)
return()
endif()
fatal("Fatal: Could not create temporary directory. "
"Check write permissions to the current directory")
endfunction()
# Get a package's effective URL.
function(get_package_url)
set(oneValueArgs
@@ -177,7 +218,6 @@ endfunction()
# Download a URL to file, with a sha512 hash
# And retry 5 times
function(cpm_download url file)
echo("Downloading ${url} to ${file}")
list(LENGTH ARGN argn_len)
if(argn_len GREATER 0)
list(GET ARGN 0 hash)
@@ -189,7 +229,8 @@ function(cpm_download url file)
foreach(i RANGE 5)
file(DOWNLOAD ${url} ${file}
${args}
STATUS ret)
STATUS ret
LOG log)
list(GET ret 0 code)
if (code EQUAL 0)
@@ -298,47 +339,60 @@ function(fetch_package)
return()
endif()
# Temporary directory.
mktempdir(TMP)
# Get filename from URL
get_filename_component(base_filename ${ARG_URL} NAME)
# Download
set(file ${TMP}/${base_filename})
cpm_download(${ARG_URL} ${file} ${ARG_HASH})
message(DEBUG "Downloaded ${base_filename}")
# Extract the downloaded archive
# TODO: Moar error handling
set(dir ${TMP}/${base_filename}-extracted)
file(MAKE_DIRECTORY ${dir})
file(ARCHIVE_EXTRACT
INPUT ${file}
DESTINATION ${dir})
# This is copied near-verbatim from ExternalProject/extractfile.cmake.in
# If there's just one subdirectory and nothing else, move it
file(GLOB contents "${dir}/*")
list(REMOVE_ITEM contents "${dir}/.DS_Store")
list(LENGTH contents n)
# If n == 1 and contents points to a directory, this is a GitHub-style pack
# In this case contents points to the subdir which will get renamed
# If not, contents will point to the parent dir which will get renamed
if (NOT n EQUAL 1 OR NOT IS_DIRECTORY "${contents}")
set(contents "${dir}")
endif()
file(REAL_PATH "${contents}" contents_abs)
# paths
cmake_path(ABSOLUTE_PATH ARG_PATH
NORMALIZE
OUTPUT_VARIABLE abs_path)
cmake_path(GET abs_path PARENT_PATH path_parent)
file(MAKE_DIRECTORY ${path_parent})
cmake_path(GET abs_path FILENAME path_name)
# Get filename from URL
get_filename_component(base_filename ${ARG_URL} NAME)
# rename tmp dir
set(tmp_renamed "${TMP}/${path_name}")
file(RENAME "${contents_abs}" "${tmp_renamed}")
# Download
set(file ${path_parent}/${base_filename})
cpm_download(${ARG_URL} ${file} ${ARG_HASH})
echo("Downloaded ${base_filename}")
# Extract
echo("Extracting ${file}...")
file(ARCHIVE_EXTRACT
INPUT ${file}
DESTINATION ${abs_path})
# This is copied near-verbatim from ExternalProject/extractfile.cmake.in
# If there's just one subdirectory and nothing else, move it
file(GLOB contents "${abs_path}/*")
list(REMOVE_ITEM contents "${abs_path}/.DS_Store")
list(LENGTH contents n)
# If n == 1 and contents points to a directory, this is a GitHub-style pack
# In this case contents points to the subdir which will get renamed
# If not, contents will point to the parent dir which will get renamed
if (n EQUAL 1 AND IS_DIRECTORY "${contents}")
set(temp_path "${abs_path}_tmp")
file(RENAME "${contents}" "${temp_path}")
file(REMOVE_RECURSE "${abs_path}")
file(RENAME "${temp_path}" "${abs_path}")
endif()
# now copy
# TODO: Error handling beyond what cmake does????
file(COPY ${tmp_renamed} DESTINATION ${path_parent})
# TODO: only echo this in script mode
echo("Extracted to ${abs_path}")
message(DEBUG "Extracted to ${abs_path}")
# Apply patches
apply_patches("${ARG_PATCHES}" "${abs_path}")
@@ -347,7 +401,7 @@ function(fetch_package)
file(WRITE "${abs_path}/.cpm_patch_key" ${ARG_PATCH_KEY})
# done! :)
file(REMOVE_RECURSE ${file})
file(REMOVE_RECURSE ${TMP})
endfunction()
# compute a hash of all patch file contents
-22
View File
@@ -1,22 +0,0 @@
# 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-2.0-or-later
find_package(PkgConfig QUIET)
pkg_search_module(OPUS QUIET IMPORTED_TARGET opus)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Opus
REQUIRED_VARS OPUS_LINK_LIBRARIES
VERSION_VAR OPUS_VERSION
)
if (PLATFORM_MSYS)
FixMsysPath(PkgConfig::OPUS)
endif()
if (Opus_FOUND AND NOT TARGET Opus::opus)
add_library(Opus::opus ALIAS PkgConfig::OPUS)
endif()
-2
View File
@@ -2,8 +2,6 @@
# 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
-->
-15
View File
@@ -218,21 +218,6 @@
"repo": "jimmy-park/openssl-cmake",
"version": "3.6.2"
},
"opus": {
"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"
],
"repo": "xiph/opus",
"version": "a3f0ec02b3"
},
"quazip": {
"hash": "609c240c7f029ac26a37d8fbab51bc16284e05e128b78b9b9c0e95d083538c36047a67d682759ac990e4adb0eeb90f04f1ea7fe2253bbda7e7e3bcce32e53dd8",
"min_version": "1.3",
+354
View File
@@ -0,0 +1,354 @@
[
{
"compatibility": 0,
"directory": "the-legend-of-zelda-breath-of-the-wild",
"releases": [
{"id": "01007EF00011E000"}
],
"title": "The Legend of Zelda: Breath of the Wild"
},
{
"compatibility": 1,
"directory": "super-mario-odyssey",
"releases": [
{"id": "0100000000010000"}
],
"title": "Super Mario Odyssey"
},
{
"compatibility": 0,
"directory": "animal-crossing-new-horizons",
"releases": [
{"id": "01006F8002326000"}
],
"title": "Animal Crossing: New Horizons"
},
{
"compatibility": 1,
"directory": "pokemon-legends-z-a",
"releases": [
{"id": "0100F43008C44000"}
],
"title": "Pokémon Legends: Z-A"
},
{
"compatibility": 1,
"directory": "the-legend-of-zelda-tears-of-the-kingdom",
"releases": [
{"id": "0100F2C0115B6000"}
],
"title": "The Legend of Zelda: Tears of the Kingdom"
},
{
"compatibility": 0,
"directory": "super-mario-galaxy",
"releases": [
{"id": "010099C022B96000"}
],
"title": "Super Mario Galaxy"
},
{
"compatibility": 3,
"directory": "star-wars-republic-commando",
"releases": [
{"id": "0100FA10115F8000"}
],
"title": "Star Wars: Republic Commando"
},
{
"compatibility": 0,
"directory": "doki-doki-literature-club-plus",
"releases": [
{"id": "010086901543E000"}
],
"title": "Doki Doki Literature Club Plus"
},
{
"compatibility": 1,
"directory": "pokemon-scarlet",
"releases": [
{"id": "0100A3D008C5C000"}
],
"title": "Pokémon Scarlet"
},
{
"compatibility": 1,
"directory": "pokemon-violet",
"releases": [
{"id": "01008F6008C5E000"}
],
"title": "Pokémon Violet"
},
{
"compatibility": 2,
"directory": "pokemon-legends-arceus",
"releases": [
{"id": "01001E300D162000"}
],
"title": "Pokémon Legends: Arceus"
},
{
"compatibility": 0,
"directory": "splatoon-2",
"releases": [
{"id": "01003BC0000A0000"}
],
"title": "Splatoon 2"
},
{
"compatibility": 1,
"directory": "super-smash-bros-ultimate",
"releases": [
{"id": "01006A800016E000"}
],
"title": "Super Smash Bros. Ultimate"
},
{
"compatibility": 0,
"directory": "mario-kart-8-deluxe",
"releases": [
{"id": "0100152000022000"}
],
"title": "Mario Kart 8 Deluxe"
},
{
"compatibility": 0,
"directory": "splatoon-3",
"releases": [
{"id": "0100C2500FC20000"}
],
"title": "Splatoon 3"
},
{
"compatibility": 0,
"directory": "new-super-mario-bros-u-deluxe",
"releases": [
{"id": "0100EA80032EA000"}
],
"title": "New Super Mario Bros. U Deluxe"
},
{
"compatibility": 0,
"directory": "hyrule-warriors-age-of-calamity",
"releases": [
{"id": "01002B00111A2000"}
],
"title": "Hyrule Warriors: Age of Calamity"
},
{
"compatibility": 2,
"directory": "luigis-mansion-3",
"releases": [
{"id": "0100DCA0064A6000"}
],
"title": "Luigi's Mansion 3"
},
{
"compatibility": 2,
"directory": "pokemon-brilliant-diamond",
"releases": [
{"id": "0100000011D90000"}
],
"title": "Pokémon Brilliant Diamond"
},
{
"compatibility": 2,
"directory": "pokemon-shining-pearl",
"releases": [
{"id": "010018E011D92000"}
],
"title": "Pokémon Shining Pearl"
},
{
"compatibility": 1,
"directory": "super-mario-3d-world-bowsers-fury",
"releases": [
{"id": "010028600EBDA000"}
],
"title": "Super Mario 3D World + Bowser's Fury"
},
{
"compatibility": 0,
"directory": "the-legend-of-zelda-links-awakening",
"releases": [
{"id": "01006BB00C6F0000"}
],
"title": "The Legend of Zelda: Link's Awakening"
},
{
"compatibility": 1,
"directory": "fire-emblem-three-houses",
"releases": [
{"id": "010055D009F78000"}
],
"title": "Fire Emblem: Three Houses"
},
{
"compatibility": 2,
"directory": "metroid-dread",
"releases": [
{"id": "010093801237C000"}
],
"title": "Metroid Dread"
},
{
"compatibility": 0,
"directory": "paper-mario-the-origami-king",
"releases": [
{"id": "0100A3900C3E2000"}
],
"title": "Paper Mario: The Origami King"
},
{
"compatibility": 1,
"directory": "xenoblade-chronicles-definitive-edition",
"releases": [
{"id": "0100FF500E34A000"}
],
"title": "Xenoblade Chronicles: Definitive Edition"
},
{
"compatibility": 2,
"directory": "xenoblade-chronicles-3",
"releases": [
{"id": "010074F013262000"}
],
"title": "Xenoblade Chronicles 3"
},
{
"compatibility": 1,
"directory": "pikmin-3-deluxe",
"releases": [
{"id": "0100F8600D4B0000"}
],
"title": "Pikmin 3 Deluxe"
},
{
"compatibility": 0,
"directory": "donkey-kong-country-tropical-freeze",
"releases": [
{"id": "0100C1F0054B6000"}
],
"title": "Donkey Kong Country: Tropical Freeze"
},
{
"compatibility": 1,
"directory": "kirby-and-the-forgotten-land",
"releases": [
{"id": "01004D300C5AE000"}
],
"title": "Kirby and the Forgotten Land"
},
{
"compatibility": 2,
"directory": "mario-party-superstars",
"releases": [
{"id": "01006B400D8B2000"}
],
"title": "Mario Party Superstars"
},
{
"compatibility": 0,
"directory": "clubhouse-games-51-worldwide-classics",
"releases": [
{"id": "0100F8600D4B0000"}
],
"title": "Clubhouse Games: 51 Worldwide Classics"
},
{
"compatibility": 1,
"directory": "ring-fit-adventure",
"releases": [
{"id": "01006B300BAF8000"}
],
"title": "Ring Fit Adventure"
},
{
"compatibility": 2,
"directory": "arms",
"releases": [
{"id": "01009B500007C000"}
],
"title": "ARMS"
},
{
"compatibility": 0,
"directory": "super-mario-maker-2",
"releases": [
{"id": "01009B90006DC000"}
],
"title": "Super Mario Maker 2"
},
{
"compatibility": 0,
"directory": "pokemon-lets-go-pikachu",
"releases": [
{"id": "010003F003A34000"}
],
"title": "Pokémon: Let's Go, Pikachu!"
},
{
"compatibility": 1,
"directory": "pokemon-lets-go-eevee",
"releases": [
{"id": "0100187003A36000"}
],
"title": "Pokémon: Let's Go, Eevee!"
},
{
"compatibility": 2,
"directory": "pokemon-sword",
"releases": [
{"id": "0100ABF008968000"}
],
"title": "Pokémon Sword"
},
{
"compatibility": 2,
"directory": "pokemon-shield",
"releases": [
{"id": "01008DB008C2C000"}
],
"title": "Pokémon Shield"
},
{
"compatibility": 1,
"directory": "new-pokemon-snap",
"releases": [
{"id": "0100F4300C182000"}
],
"title": "New Pokémon Snap"
},
{
"compatibility": 0,
"directory": "mario-golf-super-rush",
"releases": [
{"id": "0100C9C00E25C000"}
],
"title": "Mario Golf: Super Rush"
},
{
"compatibility": 1,
"directory": "mario-tennis-aces",
"releases": [
{"id": "0100BDE00862A000"}
],
"title": "Mario Tennis Aces"
},
{
"compatibility": 2,
"directory": "wario-ware-get-it-together",
"releases": [
{"id": "0100563010F22000"}
],
"title": "WarioWare: Get It Together!"
},
{
"compatibility": 0,
"directory": "big-brain-academy-brain-vs-brain",
"releases": [
{"id": "0100190010F24000"}
],
"title": "Big Brain Academy: Brain vs. Brain"
}
]
+5
View File
@@ -0,0 +1,5 @@
<RCC>
<qresource prefix="compatibility_list">
<file>compatibility_list.json</file>
</qresource>
</RCC>
+765 -613
View File
File diff suppressed because it is too large Load Diff
+768 -611
View File
File diff suppressed because it is too large Load Diff
+753 -596
View File
File diff suppressed because it is too large Load Diff
+761 -604
View File
File diff suppressed because it is too large Load Diff
+751 -591
View File
File diff suppressed because it is too large Load Diff
+753 -596
View File
File diff suppressed because it is too large Load Diff
+753 -591
View File
File diff suppressed because it is too large Load Diff
+753 -596
View File
File diff suppressed because it is too large Load Diff
+746 -589
View File
File diff suppressed because it is too large Load Diff
+753 -596
View File
File diff suppressed because it is too large Load Diff
+744 -587
View File
File diff suppressed because it is too large Load Diff
+749 -590
View File
File diff suppressed because it is too large Load Diff
+753 -596
View File
File diff suppressed because it is too large Load Diff
+787 -635
View File
File diff suppressed because it is too large Load Diff
+754 -597
View File
File diff suppressed because it is too large Load Diff
+753 -596
View File
File diff suppressed because it is too large Load Diff
+749 -590
View File
File diff suppressed because it is too large Load Diff
+750 -590
View File
File diff suppressed because it is too large Load Diff
+753 -596
View File
File diff suppressed because it is too large Load Diff
+750 -590
View File
File diff suppressed because it is too large Load Diff
+749 -590
View File
File diff suppressed because it is too large Load Diff
+747 -590
View File
File diff suppressed because it is too large Load Diff
+750 -590
View File
File diff suppressed because it is too large Load Diff
+753 -596
View File
File diff suppressed because it is too large Load Diff
+753 -596
View File
File diff suppressed because it is too large Load Diff
+751 -591
View File
File diff suppressed because it is too large Load Diff
+753 -596
View File
File diff suppressed because it is too large Load Diff
+9 -10
View File
@@ -60,7 +60,6 @@ All other dependencies will be downloaded and built by [CPM](https://github.com/
* [ZLIB](https://www.zlib.net/) 1.2+
* [zstd](https://facebook.github.io/zstd/) 1.5+
* [enet](http://enet.bespin.org/) 1.3+
* [Opus](https://opus-codec.org/) 1.3+
Vulkan 1.3.274+ is also needed:
@@ -122,7 +121,7 @@ sudo emerge -a \
dev-libs/unordered_dense dev-libs/boost dev-libs/openssl dev-libs/discord-rpc \
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-gfx/renderdoc media-libs/libva media-video/ffmpeg \
media-libs/VulkanMemoryAllocator media-libs/libsdl3 media-libs/cubeb \
net-libs/enet \
sys-libs/zlib \
@@ -154,7 +153,7 @@ Required USE flags:
<summary>Arch Linux</summary>
```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 sdl3 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 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.
@@ -167,7 +166,7 @@ sudo pacman -Syu --needed base-devel boost catch2 cmake enet ffmpeg fmt git glsl
<summary>Ubuntu, Debian, Mint Linux</summary>
```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 libsdl3-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 libasound2t64 vulkan-utility-libraries-dev
```
* Ubuntu 26.04, Linux Mint 22.3, or Debian 13 or later is required.
@@ -214,7 +213,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 sdl3-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 jq patch
```
</details>
@@ -260,7 +259,7 @@ brew install molten-vk
<details>
<summary>FreeBSD</summary>
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`
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 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.
@@ -273,7 +272,7 @@ If using FreeBSD 12 or prior, use `devel/pkg-config` instead.
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
pkgin install git cmake boost fmtlib SDL3 catch2 libjwt spirv-headers spirv-tools ffmpeg7 libva nlohmann-json jq qt6 cpp-httplib lz4 vulkan-headers nasm autoconf enet pkg-config libusb1 libcxx frozen
```
[Caveats](./Caveats.md#netbsd).
@@ -304,7 +303,7 @@ pkg install gcc14 git cmake unzip nasm autoconf bash pkgconf ffmpeg glslang gmak
<summary>OpenIndiana</summary>
```sh
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
sudo pkg install git cmake qt6 boost glslang libzip library/lz4 libusb-1 nlohmann-json openssl sdl3 zlib compress/zstd unzip pkg-config nasm autoconf mesa library/libdrm header-drm developer/fmt
```
[Caveats](./Caveats.md#openindiana).
@@ -328,7 +327,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 SDL3"
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 libusb unordered_dense openssl SDL3"
# Either x86_64 or clang-aarch64 (Windows on ARM)
packages="$BASE"
for pkg in $MINGW; do
@@ -354,7 +353,7 @@ pacman -Syuu --needed --noconfirm $packages
<summary>HaikuOS</summary>
```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 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
pkgman install git cmake patch libfmt_devel nlohmann_json lz4_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).
+1 -1
View File
@@ -12,7 +12,7 @@ pkgs.mkShellNoCC {
git cmake clang gnumake patch jq pkg-config
# libraries
openssl boost fmt nlohmann_json lz4 zlib zstd
enet libopus vulkan-headers vulkan-utility-libraries
enet vulkan-headers vulkan-utility-libraries
spirv-tools spirv-headers vulkan-loader unzip
glslang python3 httplib cpp-jwt ffmpeg-headless
libusb1 cubeb
@@ -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"),
@@ -27,7 +27,6 @@ enum class IntSetting(override val key: String) : AbstractIntSetting {
RENDERER_DYNA_STATE("dyna_state"),
DMA_ACCURACY("dma_accuracy"),
GPU_FENCE_BEHAVIOR("gpu_fence_behavior"),
FRAME_PACING_MODE("frame_pacing_mode"),
AUDIO_OUTPUT_ENGINE("output_engine"),
MAX_ANISOTROPY("max_anisotropy"),
@@ -669,15 +669,6 @@ abstract class SettingsItem(
valuesId = R.array.dmaAccuracyValues
)
)
put(
SingleChoiceSetting(
IntSetting.GPU_FENCE_BEHAVIOR,
titleId = R.string.gpu_fence_behavior,
descriptionId = R.string.gpu_fence_behavior_description,
choicesId = R.array.gpuFenceBehaviorNames,
valuesId = R.array.gpuFenceBehaviorValues
)
)
put(
SwitchSetting(
BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS,
@@ -766,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,
@@ -283,7 +283,6 @@ class SettingsFragmentPresenter(
add(IntSetting.RENDERER_ACCURACY.key)
add(IntSetting.DMA_ACCURACY.key)
add(IntSetting.GPU_FENCE_BEHAVIOR.key)
add(IntSetting.MAX_ANISOTROPY.key)
add(IntSetting.RENDERER_VRAM_USAGE_MODE.key)
add(IntSetting.RENDERER_ASTC_DECODE_METHOD.key)
@@ -300,6 +299,7 @@ class SettingsFragmentPresenter(
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)
@@ -79,13 +79,6 @@ class LicensesFragment : Fragment() {
R.string.license_ffmpeg_copyright,
R.string.license_ffmpeg_text
),
License(
R.string.license_opus,
R.string.license_opus_description,
R.string.license_opus_link,
R.string.license_opus_copyright,
R.string.license_opus_text
),
License(
R.string.license_sirit,
R.string.license_sirit_description,
@@ -476,8 +476,6 @@
<string name="renderer_accuracy_description">يتحكم في وضع محاكاة وحدة معالجة الرسومات. تعمل معظم الألعاب بشكل جيد مع وضعي سريع أو متوازن، لكن الوضع الدقيق لا يزال مطلوبًا لبعض الألعاب. تميل الجسيمات إلى العرض بشكل صحيح فقط عند استخدام الوضع الدقيق.</string>
<string name="dma_accuracy">دقة DMA</string>
<string name="dma_accuracy_description">يتحكم في دقة DMA. يمكن أن تؤدي الدقة الآمنة إلى حل المشكلات في بعض الألعاب، ولكنها قد تؤثر أيضًا على الأداء في بعض الحالات. إذا لم تكن متأكدًا، فاترك هذا الخيار على الإعداد الافتراضي.</string>
<string name="gpu_fence_behavior">سلوك حاجز وحدة معالجة الرسومات</string>
<string name="gpu_fence_behavior_description">يتحكم في سلوك تزامن حاجز وحدة معالجة الرسوميات. الخيار الفوري هو الأسرع، لكنه قد يسبب بعض المشاكل. الخيار المتوازن يقدم توافقًا أفضل وقد يصلح مشاكل في بعض الألعاب. الخيار الدقيق يحسن التوافق أكثر لكنه قد يقلل الأداء قليلاً. الخيار الصارم هو الأبطأ، لكنه قد يصلح المشاكل التي تتطلب تزامنًا أكثر صرامة. الإعداد الافتراضي يتبع إعداد دقة وحدة معالجة الرسوميات.</string>
<string name="anisotropic_filtering">تصفية متباينة الخواص</string>
<string name="anisotropic_filtering_description">يحسن جودة الأنسجة عند عرضها بزوايا مائلة</string>
<string name="vram_usage_mode">وضع استخدام ذاكرة VRAM</string>
@@ -499,8 +497,6 @@
<string name="renderer_reactive_flushing_description">يحسن دقة العرض في بعض الألعاب على حساب الأداء.</string>
<string name="enable_buffer_history">تمكين سجل التخزين المؤقت</string>
<string name="enable_buffer_history_description">يُتيح هذا الخيار الوصول إلى حالات التخزين المؤقت السابقة. وقد يُحسّن جودة العرض وثبات الأداء في بعض الألعاب.</string>
<string name="enable_gpu_buffer_readback">تفعيل قراءة مخزن وحدة معالجة الرسومات</string>
<string name="enable_gpu_buffer_readback_description">يحافظ هذا النظام على بيانات المخزن المؤقت المُعدّلة بواسطة وحدة معالجة الرسومات عن طريق قراءتها مرة أخرى قبل التحميل. تتطلب بعض الألعاب ذلك لعرض بعض التأثيرات بشكل صحيح. قد يُسبب ذلك مشاكل إذا لم يتمكن الجهاز من التعامل مع عبء العمل الإضافي.</string>
<string name="use_optimized_vertex_buffers">مخازن الرؤوس المُحسّنة</string>
<string name="use_optimized_vertex_buffers_description">يُتيح ربطًا مُحسَّنًا لمخازن الرؤوس لتحسين الأداء. يتطلب برامج تشغيل Mesa 26.0+ Turnip/ برامج تشغيل QCOM. قد يتعطل على برامج تشغيل Turnip القديمة (25.3 وما دون).</string>
@@ -510,6 +506,8 @@
<string name="fast_gpu_time_description">يُجبر هذا الخيار معظم الألعاب على العمل بأعلى دقة عرض أصلية. استخدم 256 للحصول على أقصى أداء و512 للحصول على أعلى جودة رسومات.</string>
<string name="skip_cpu_inner_invalidation">تخطي إبطال صلاحية وحدة المعالجة المركزية الداخلية</string>
<string name="skip_cpu_inner_invalidation_description">يتخطى بعض عمليات إبطال ذاكرة التخزين المؤقتة من جانب وحدة المعالجة المركزية أثناء تحديثات الذاكرة، مما يقلل من استخدام وحدة المعالجة المركزية ويحسن أداءها. قد يتسبب ذلك في حدوث أعطال أو تعطل في بعض الألعاب.</string>
<string name="antiflicker">مضاد الوميض</string>
<string name="antiflicker_description">يُجبر هذا الوضع وظائف وحدة معالجة الرسومات على الانتظار حتى يتم إرسال العمل إليها. استخدمه مع وضع وحدة معالجة الرسومات السريع لتجنب الوميض مع تأثير أقل على الأداء.</string>
<string name="fix_bloom_effects">إصلاح تأثيرات التوهج</string>
<string name="fix_bloom_effects_description">يقلل من ضبابية التوهج في LA/EOW (Adreno A6XX - A7XX/ Turnip)، ويزيل التوهج في Burnout. تحذير: قد يسبب تشوهات رسومية في ألعاب أخرى.</string>
<string name="emulate_bgr565">محاكاة BGR565</string>
@@ -575,12 +573,6 @@
<string name="gpu_log_level_description">مستوى التفاصيل لسجلات وحدة معالجة الرسومات (كلما زاد المستوى، زادت التفاصيل وزادت التكاليف الإضافية)</string>
<string name="gpu_log_vulkan_calls">تسجيل استدعاءات واجهة برمجة تطبيقات Vulkan</string>
<string name="gpu_log_vulkan_calls_description">تتبع جميع استدعاءات واجهة برمجة تطبيقات Vulkan في المخزن المؤقت الحلقي</string>
<string name="gpu_log_shader_dumps">تفريغ مظللات SPIR-V</string>
<string name="gpu_log_shader_dumps_description">احفظ ملفات SPIR-V الثنائية المُعاد تجميعها (.spv) في مجلد التفريغ. افحصها باستخدام spirv-dis/spirv-cross/spirv-val.</string>
<string name="dump_guest_shaders">تظليلات ضيف التفريغ (ماكسويل)</string>
<string name="dump_guest_shaders_description">احفظ ملفات بايت كود برنامج التظليل الضيف الخاص بـ «ماكسويل» (*.ash) في مجلد «dump». افحصها باستخدام nvdisasm.</string>
<string name="dump_macros">تفريغ ماكرو ماكسويل</string>
<string name="dump_macros_description">احفظ ملفات برامج ماكرو ماكسويل (*.macro) في مجلد «dump». افحصها باستخدام برنامج «envydis».</string>
<string name="gpu_log_memory_tracking">تتبع ذاكرة وحدة معالجة الرسومات</string>
<string name="gpu_log_memory_tracking_description">مراقبة تخصيصات ذاكرة وحدة معالجة الرسومات وإلغاء تخصيصها</string>
<string name="gpu_log_driver_debug">معلومات تصحيح أخطاء برنامج التشغيل</string>
@@ -1000,6 +992,7 @@
<!-- Renderer Accuracy -->
<string name="renderer_accuracy_low">سريع</string>
<string name="renderer_accuracy_medium">متوازن</string>
<string name="renderer_accuracy_high">دقيق</string>
<!-- DMA Accuracy -->
@@ -1007,13 +1000,6 @@
<string name="dma_accuracy_unsafe">غير آمن</string>
<string name="dma_accuracy_safe">آمن</string>
<!-- GPU Fence Behavior -->
<string name="gpu_fence_behavior_default">افتراضي</string>
<string name="gpu_fence_behavior_immediate">فوري</string>
<string name="gpu_fence_behavior_balanced">متوازن</string>
<string name="gpu_fence_behavior_accurate">دقيق</string>
<string name="gpu_fence_behavior_strict">صارم</string>
<string name="vram_usage_conservative">محافظ</string>
<string name="vram_usage_aggressive">عدواني</string>
@@ -707,6 +707,7 @@
<!-- Renderer Accuracy -->
<string name="renderer_accuracy_low">Rychlý</string>
<string name="renderer_accuracy_medium">Vyvážený</string>
<string name="renderer_accuracy_high">Přesný</string>
<!-- DMA Accuracy -->
@@ -927,6 +927,7 @@ Wirklich fortfahren?</string>
<!-- Renderer Accuracy -->
<string name="renderer_accuracy_low">Schnell</string>
<string name="renderer_accuracy_medium">Ausgeglichen</string>
<string name="renderer_accuracy_high">Genau</string>
<!-- DMA Accuracy -->
@@ -470,7 +470,6 @@
<string name="renderer_accuracy_description">Controla el modo de la emulación de la GPU. La mayoría de los juegos se renderizan correctamente en los modos Rápido o Equilibrado, pero algunos requieren Preciso. Las partículas tienden a renderizarse correctamente solo con el modo Preciso.</string>
<string name="dma_accuracy">Precisión de DMA</string>
<string name="dma_accuracy_description">Controla la precisión de DMA. La precisión segura puede solucionar problemas en algunos juegos, pero también puede afectar al rendimiento en algunos casos. Si no está seguro, déjelo en Predeterminado.</string>
<string name="gpu_fence_behavior">Comportamiento de vallado de la GPU</string>
<string name="anisotropic_filtering">Filtrado anisotrópico</string>
<string name="anisotropic_filtering_description">Mejora la calidad de las texturas al ser observadas desde ángulos oblicuos</string>
<string name="vram_usage_mode">Modo de uso de VRAM</string>
@@ -503,6 +502,8 @@
<string name="fast_gpu_time_description">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.</string>
<string name="skip_cpu_inner_invalidation">Omitir invalidación interna de la CPU</string>
<string name="skip_cpu_inner_invalidation_description">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.</string>
<string name="antiflicker">Antiparpadeo</string>
<string name="antiflicker_description">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.</string>
<string name="fix_bloom_effects">Arreglar los efectos de resplandor</string>
<string name="fix_bloom_effects_description">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.</string>
<string name="emulate_bgr565">Emular BGR565</string>
@@ -993,6 +994,7 @@
<!-- Renderer Accuracy -->
<string name="renderer_accuracy_low">Rápido</string>
<string name="renderer_accuracy_medium">Equilibrado</string>
<string name="renderer_accuracy_high">Preciso</string>
<!-- DMA Accuracy -->
@@ -1000,13 +1002,6 @@
<string name="dma_accuracy_unsafe">Inseguro</string>
<string name="dma_accuracy_safe">Seguro</string>
<!-- GPU Fence Behavior -->
<string name="gpu_fence_behavior_default">Predeterminado</string>
<string name="gpu_fence_behavior_immediate">Inmediato</string>
<string name="gpu_fence_behavior_balanced">Equilibrado</string>
<string name="gpu_fence_behavior_accurate">Preciso</string>
<string name="gpu_fence_behavior_strict">Estricto</string>
<string name="vram_usage_conservative">Conservador</string>
<string name="vram_usage_aggressive">Agresivo</string>
@@ -939,6 +939,7 @@
<!-- Renderer Accuracy -->
<string name="renderer_accuracy_low">Rapide</string>
<string name="renderer_accuracy_medium">Moyen</string>
<string name="renderer_accuracy_high">Précis</string>
<!-- DMA Accuracy -->
@@ -893,6 +893,7 @@
<!-- Renderer Accuracy -->
<string name="renderer_accuracy_low">Szybkie</string>
<string name="renderer_accuracy_medium">Zrównoważony</string>
<string name="renderer_accuracy_high">Dokładny</string>
<!-- DMA Accuracy -->
@@ -840,6 +840,7 @@
<string name="renderer_none">Nenhum</string>
<string name="renderer_accuracy_medium">Média</string>
<string name="renderer_accuracy_high">Alta</string>
<!-- DMA Accuracy -->
@@ -499,6 +499,8 @@
<string name="fast_gpu_time_description">Принудительно запускает большинство игр в их максимальном нативном разрешении. Используйте значение 256 для максимальной производительности и 512 для максимального качества графики.</string>
<string name="skip_cpu_inner_invalidation">Пропустить внутреннюю инвалидацию ЦП</string>
<string name="skip_cpu_inner_invalidation_description">Пропускает некоторые инвалидации кэша на стороне ЦП при обновлениях памяти, уменьшая нагрузку на процессор и повышая производительность. Может вызывать сбои в некоторых играх.</string>
<string name="antiflicker">Анти-мерцание</string>
<string name="antiflicker_description">Принудительно заставляет обратные вызовы ГПУ-фильтра ожидать выполнения отправленных задач на ГПУ. Используйте с Быстрым режимом ГПУ, что бы избежать мерцаний с меньшим влиянием на производительность.</string>
<string name="fix_bloom_effects">Исправить эффекты размытия</string>
<string name="fix_bloom_effects_description">Частично убирает размытие в LA/EOW (Adreno A6XX - A7XX/ Turnip), полностью отключает его в Burnout. Внимание: может вызывать графические артефакты в других играх.</string>
<string name="emulate_bgr565">Эмулировать BGR565</string>
@@ -983,6 +985,7 @@
<!-- Renderer Accuracy -->
<string name="renderer_accuracy_low">Быстрый</string>
<string name="renderer_accuracy_medium">Сбалансированный</string>
<string name="renderer_accuracy_high">Точный</string>
<!-- DMA Accuracy -->
@@ -502,6 +502,8 @@
<string name="fast_gpu_time_description">Примушує більшість ігор працювати на їхній максимальній нативній роздільності. Використовуйте 256 для максимальної продуктивності та 512 для найкращої якості.</string>
<string name="skip_cpu_inner_invalidation">Пропустити внутрішнє інвалідування CPU</string>
<string name="skip_cpu_inner_invalidation_description">Пропускає деякі інвалідації кешу на стороні CPU під час оновлення пам\'яті, зменшуючи навантаження на процесор і покращуючи продуктивність. Може спричинити збої в деяких іграх.</string>
<string name="antiflicker">Антимерехтіння</string>
<string name="antiflicker_description">Змушує механізм синхронізації чекати, доки ГП завершить подані завдання. Використовуйте з режимом ГП «Швидко», щоб уникнути мерехтіння з меншими втратами продуктивності.</string>
<string name="fix_bloom_effects">Виправити ефекти світіння</string>
<string name="fix_bloom_effects_description">Зменшує розмиття світіння в LA/EOW (Adreno A6XXA7XX / Turnip), прибирає світіння в Burnout. Увага: може спричинити графічні артефакти в інших іграх.</string>
<string name="emulate_bgr565">Емулювати BGR565</string>
@@ -986,6 +988,7 @@
<!-- Renderer Accuracy -->
<string name="renderer_accuracy_low">Швидко</string>
<string name="renderer_accuracy_medium">Збалансовано</string>
<string name="renderer_accuracy_high">Точно</string>
<!-- DMA Accuracy -->
@@ -67,7 +67,7 @@
<string name="show_shaders_building">显示着色器编译信息</string>
<string name="show_shaders_building_description">显示当前正在编译的着色器数量</string>
<string name="pipeline_worker_cores">管线工作线程</string>
<string name="pipeline_worker_cores_description">管理用于构建 Vulkan 管线的核心数量,较高的值可提升管线编译性能,但温度也会随之升高。</string>
<string name="pipeline_worker_cores_description">管理用于构建 Vulkan 管线的核心数量,数值越高则管线编译性能越好,但温度也会随之升高。</string>
<string name="overlay_position">叠加层位置</string>
<string name="overlay_position_description">选择叠加层在屏幕上显示的位置</string>
<string name="overlay_position_top_left">左上</string>
@@ -185,7 +185,7 @@
<string name="multiplayer_preferred_game_name">首选游戏</string>
<string name="multiplayer_lobby_type">游戏大厅类型</string>
<string name="multiplayer_room_name_error">长度需为3-20个字符</string>
<string name="multiplayer_required">需要</string>
<string name="multiplayer_required">必填</string>
<string name="multiplayer_token_required">需要Web令牌,请前往高级设置 -> 系统 -> 网络</string>
<string name="multiplayer_ip_error">IP格式无效</string>
<string name="multiplayer_username_error">必须为4至20个字符,且仅包含字母、数字、点号、连字符、下划线和空格</string>
@@ -287,7 +287,7 @@
<string name="warning_skip">跳过</string>
<string name="warning_cancel">取消</string>
<string name="install_amiibo_keys">安装 Amiibo 密钥文件</string>
<string name="install_amiibo_keys_description">在遊戏中使用 Amiibo 时需</string>
<string name="install_amiibo_keys_description">在遊戏中使用 Amiibo 时</string>
<string name="gpu_driver_fetcher">GPU驱动获取器</string>
<string name="gpu_driver_manager">GPU 驱动管理器</string>
<string name="install_gpu_driver_description">安装替代的驱动程序以获得更好的性能和精度</string>
@@ -463,13 +463,11 @@
<string name="advanced">高级</string>
<string name="renderer_accuracy">GPU 模式</string>
<string name="renderer_accuracy_description">控制 GPU 模拟模式。大多数游戏在“快速”或“衡”模式下都能获得良好的渲染,但有些游戏仍需使用“精确”模式。粒子效果通常只有在“精确”模式下才能正确渲染。</string>
<string name="renderer_accuracy_description">控制 GPU 模拟模式。大多数游戏在“快速”或“衡”模式下都能正常渲染,但有些游戏仍需使用“精确”模式。粒子效果通常只有在“精确”模式下才能正确渲染。</string>
<string name="dma_accuracy">DMA 精度</string>
<string name="dma_accuracy_description">控制 DMA 的精准度。安全精度可以修复存在于某些游戏中的问题,但在某些情况下也会对性能造成影响。如不确定,请保持“默认”。</string>
<string name="gpu_fence_behavior">GPU 围栏行为</string>
<string name="gpu_fence_behavior_description">控制 GPU 围栏同步行为。“即时”是速度最快的选项,但可能会引入一些问题。“均衡”提供了更好的兼容性,可能修复某些游戏中的问题。“精确”在牺牲部分性能的前提下进一步提升兼容性。“严格”是速度最慢的选项,但可以修复那些要求更严格同步的问题。默认遵循 GPU “精确”设定。</string>
<string name="anisotropic_filtering">各向异性过滤</string>
<string name="anisotropic_filtering_description">升斜视角下的纹理质量</string>
<string name="anisotropic_filtering_description">高斜角的纹理质量</string>
<string name="vram_usage_mode">显存使用模式</string>
<string name="vram_usage_mode_description">控制显存分配与释放策略</string>
<string name="accelerate_astc">ASTC解码方式</string>
@@ -490,7 +488,7 @@
<string name="enable_buffer_history">启用缓冲区历史</string>
<string name="enable_buffer_history_description">启用对先前缓冲区状态的访问。此选项可在某些游戏中提升渲染质量并保持性能的一致性。</string>
<string name="enable_gpu_buffer_readback">启用 GPU 缓冲区回读</string>
<string name="enable_gpu_buffer_readback_description">在上传前回读经由 GPU 修改过的缓冲区数据,以将其保留。一些游戏会用到这项设定以正确渲染某些效果。如果硬件无法处理额外的工作负载,则可能会导致问题。</string>
<string name="enable_gpu_buffer_readback_description">在上传前回读经由 GPU 修改过的缓冲区数据,以将其保留。一些游戏需要这样做才能正确渲染某些效果。如果硬件无法处理额外的工作负载,则可能会导致问题。</string>
<string name="use_optimized_vertex_buffers">优化顶点缓冲区</string>
<string name="use_optimized_vertex_buffers_description">启用经过优化的顶点缓冲区绑定以提升性能。需要 Mesa 26.0 及以上版本的 Turnip 或 QCOM 驱动程序。若使用较旧版本的 Turnip 驱动 (25.3 及以下版本) 则会导致崩溃。</string>
@@ -500,6 +498,8 @@
<string name="fast_gpu_time_description">强制大多数游戏以其最高原生分辨率运行。设置为 256 可获得最佳性能,设置为 512 可获得最佳画面保真度。</string>
<string name="skip_cpu_inner_invalidation">跳过CPU内部无效化</string>
<string name="skip_cpu_inner_invalidation_description">在更新内存时跳过某些 CPU 端的缓存失效操作,从而降低 CPU 占用率并提升性能。可能会在某些游戏中引发故障点或崩溃。</string>
<string name="antiflicker">防闪烁</string>
<string name="antiflicker_description">强制 GPU 围栏回调等待已提交的 GPU 任务。配合“快速 GPU 模式”一起使用,以牺牲少量性能为代价来避免画面闪烁现象。</string>
<string name="fix_bloom_effects">修复 Bloom 效果</string>
<string name="fix_bloom_effects_description">减少《智慧的再现》和《众神的三角力量2》(Adreno A6XX - A7XX/ Turnip)中的 bloom 模糊,并移除《Burnout》中的 bloom 效果。警告:可能会导致在其他游戏中出现图形异常。</string>
<string name="emulate_bgr565">模拟 BGR565</string>
@@ -513,7 +513,7 @@
<string name="gpu_unswizzle_enable">启用 GPU Unswizzle</string>
<string name="gpu_unswizzle_disabled">禁用</string>
<string name="gpu_unswizzle_texture_size">GPU Unswizzle 最大纹理尺寸</string>
<string name="gpu_unswizzle_texture_size_description">设置基于 GPU 的纹理 unswizzling 的最大尺寸(MB)。虽然 GPU 处理中等和大型纹理的速度更快,但对于非常小的纹理,CPU 可能更为高效。通过调节此项设置,以平衡GPU 加速与 CPU 开销。</string>
<string name="gpu_unswizzle_texture_size_description">设置基于 GPU 的纹理 unswizzling 的最大尺寸(MB)。虽然 GPU 处理中等和大型纹理的速度更快,但对于非常小的纹理,CPU 可能更为高效。通过调节此项设置,以尝试在 GPU 加速与 CPU 开销之间找到平衡</string>
<string name="gpu_unswizzle_stream_size">GPU Unswizzle 流大小</string>
<string name="gpu_unswizzle_stream_size_description">设置用于 unswizzling 大型纹理时的每帧数据限制。较高的数值可以加速纹理的加载过程,但会带来更高的帧延迟。而较低的数值则可以降低 GPU 的开销,但也可能会导致可见的纹理闪现。</string>
<string name="gpu_unswizzle_chunk_size">GPU Unswizzle 块大小</string>
@@ -990,6 +990,7 @@
<!-- Renderer Accuracy -->
<string name="renderer_accuracy_low">快速</string>
<string name="renderer_accuracy_medium">平衡</string>
<string name="renderer_accuracy_high">精确</string>
<!-- DMA Accuracy -->
@@ -997,13 +998,6 @@
<string name="dma_accuracy_unsafe">不安全</string>
<string name="dma_accuracy_safe">安全</string>
<!-- GPU Fence Behavior -->
<string name="gpu_fence_behavior_default">默认</string>
<string name="gpu_fence_behavior_immediate">即时</string>
<string name="gpu_fence_behavior_balanced">均衡</string>
<string name="gpu_fence_behavior_accurate">精确</string>
<string name="gpu_fence_behavior_strict">严格</string>
<string name="vram_usage_conservative">保守式</string>
<string name="vram_usage_aggressive">主动式</string>
@@ -1027,7 +1021,7 @@
<string name="ratio_stretch">拉伸窗口</string>
<!-- CPU Accuracy -->
<string name="cpu_accuracy_accurate"></string>
<string name="cpu_accuracy_accurate"></string>
<string name="cpu_accuracy_unsafe">不安全</string>
<string name="cpu_accuracy_paranoid">极致</string>
<string name="cpu_accuracy_debugging">调试</string>
@@ -843,6 +843,7 @@
<string name="renderer_none"></string>
<string name="renderer_accuracy_medium">平衡</string>
<string name="renderer_accuracy_high">準確</string>
<!-- DMA Accuracy -->
+2 -15
View File
@@ -103,12 +103,14 @@
<string-array name="rendererAccuracyNames">
<item>@string/renderer_accuracy_low</item>
<item>@string/renderer_accuracy_medium</item>
<item>@string/renderer_accuracy_high</item>
</string-array>
<integer-array name="rendererAccuracyValues">
<item>0</item>
<item>1</item>
<item>2</item>
</integer-array>
<!-- VRAM USAGE MODE CHOICES -->
@@ -520,21 +522,6 @@
<item>2</item>
</integer-array>
<string-array name="gpuFenceBehaviorNames">
<item>@string/gpu_fence_behavior_default</item>
<item>@string/gpu_fence_behavior_immediate</item>
<item>@string/gpu_fence_behavior_balanced</item>
<item>@string/gpu_fence_behavior_accurate</item>
<item>@string/gpu_fence_behavior_strict</item>
</string-array>
<integer-array name="gpuFenceBehaviorValues">
<item>0</item>
<item>1</item>
<item>2</item>
<item>3</item>
<item>4</item>
</integer-array>
<string-array name="appletEntries">
<item>@string/applet_hle</item>
@@ -479,11 +479,9 @@
<string name="advanced">Advanced</string>
<string name="renderer_accuracy">GPU Mode</string>
<string name="renderer_accuracy_description">Controls the GPU emulation mode. Most games render fine with Fast, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode.</string>
<string name="renderer_accuracy_description">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.</string>
<string name="dma_accuracy">DMA Accuracy</string>
<string name="dma_accuracy_description">Controls the DMA precision accuracy. Safe precision can fix issues in some games, but it can also impact performance in some cases. If unsure, leave this on Default.</string>
<string name="gpu_fence_behavior">GPU Fence Behavior</string>
<string name="gpu_fence_behavior_description">Controls the GPU fence synchronization behavior. Immediate is the fastest option, but can introduce some issues. Balanced offers better compatibility and may fix issues in some games. Accurate further improves compatibility at the cost of some performance. Strict is the slowest option, but can fix issues that require stricter synchronization. Default follows the GPU Accuracy setting.</string>
<string name="anisotropic_filtering">Anisotropic filtering</string>
<string name="anisotropic_filtering_description">Improves the quality of textures when viewed at oblique angles</string>
<string name="vram_usage_mode">VRAM Usage Mode</string>
@@ -516,6 +514,8 @@
<string name="fast_gpu_time_description">Forces most games to run at their highest native resolution. Use 256 for maximal performance and 512 for maximal graphics fidelity.</string>
<string name="skip_cpu_inner_invalidation">Skip CPU Inner Invalidation</string>
<string name="skip_cpu_inner_invalidation_description">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.</string>
<string name="antiflicker">Anti-Flicker</string>
<string name="antiflicker_description">Forces GPU fence callbacks to wait for submitted GPU work. Use with Fast GPU mode, to avoid flicker with lower performance impact.</string>
<string name="fix_bloom_effects">Fix Bloom Effects</string>
<string name="fix_bloom_effects_description">Reduces bloom blur in LA/EOW (Adreno A6XX - A7XX/ Turnip), removes bloom in Burnout. Warning: may cause graphical artifacts in other games.</string>
<string name="emulate_bgr565">Emulate BGR565</string>
@@ -1039,6 +1039,7 @@
<!-- Renderer Accuracy -->
<string name="renderer_accuracy_low">Fast</string>
<string name="renderer_accuracy_medium">Balanced</string>
<string name="renderer_accuracy_high">Accurate</string>
<!-- DMA Accuracy -->
@@ -1046,13 +1047,6 @@
<string name="dma_accuracy_unsafe">Unsafe</string>
<string name="dma_accuracy_safe">Safe</string>
<!-- GPU Fence Behavior -->
<string name="gpu_fence_behavior_default">Default</string>
<string name="gpu_fence_behavior_immediate">Immediate</string>
<string name="gpu_fence_behavior_balanced">Balanced</string>
<string name="gpu_fence_behavior_accurate">Accurate</string>
<string name="gpu_fence_behavior_strict">Strict</string>
<!-- ASTC Decoding Method Choices -->
<string name="accelerate_astc_cpu" translatable="false">CPU</string>
<string name="accelerate_astc_gpu" translatable="false">GPU</string>
@@ -1700,51 +1694,6 @@ RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
</string>
<string name="license_opus" translatable="false">Opus</string>
<string name="license_opus_description" translatable="false">Modern audio compression for the internet</string>
<string name="license_opus_link" translatable="false">https://github.com/xiph/opus</string>
<string name="license_opus_copyright" translatable="false">Copyright 20012011 Xiph.Org, Skype Limited, Octasic, Jean-Marc Valin, Timothy B. Terriberry, CSIRO, Gregory Maxwell, Mark Borgerding, Erik de Castro Lopo</string>
<string name="license_opus_text" translatable="false">
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:\n\n
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.\n\n
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.\n\n
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.\n\n
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS\'\' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n
Opus is subject to the royalty-free patent licenses which are
specified at:\n\n
Xiph.Org Foundation:
https://datatracker.ietf.org/ipr/1524/ \n\n
Microsoft Corporation:
https://datatracker.ietf.org/ipr/1914/ \n\n
Broadcom Corporation:
https://datatracker.ietf.org/ipr/1526/
</string>
<string name="license_sirit" translatable="false">Sirit</string>
<string name="license_sirit_description" translatable="false">A runtime SPIR-V assembler</string>
+8 -2
View File
@@ -226,8 +226,14 @@ else()
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-sign-conversion>)
endif()
target_include_directories(audio_core PRIVATE ${OPUS_INCLUDE_DIRS})
target_link_libraries(audio_core PUBLIC common core Opus::opus)
if (YUZU_USE_EXTERNAL_FFMPEG)
add_dependencies(audio_core ffmpeg-build)
endif()
target_include_directories(audio_core PUBLIC ${FFmpeg_INCLUDE_DIR})
target_link_libraries(audio_core PRIVATE ${FFmpeg_LIBRARIES})
target_link_options(audio_core PRIVATE ${FFmpeg_LDFLAGS})
target_link_libraries(audio_core PUBLIC common core)
if (ENABLE_CUBEB)
target_sources(audio_core PRIVATE
@@ -1,107 +1,71 @@
// 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
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavcodec/codec.h>
#include <libavcodec/packet.h>
#include <libavutil/channel_layout.h>
#include <libavutil/frame.h>
}
#include "audio_core/adsp/apps/opus/opus_decode_object.h"
#include "audio_core/adsp/apps/opus/opus_types.h"
#include "common/assert.h"
#include "core/hle/service/audio/errors.h"
namespace AudioCore::ADSP::OpusDecoder {
namespace {
bool IsValidChannelCount(u32 channel_count) {
return channel_count == 1 || channel_count == 2;
}
} // namespace
u32 OpusDecodeObject::GetWorkBufferSize(u32 channel_count) {
if (!IsValidChannelCount(channel_count)) {
return 0;
}
return static_cast<u32>(sizeof(OpusDecodeObject)) + opus_decoder_get_size(channel_count);
if (channel_count == 1 || channel_count == 2)
return u32(sizeof(OpusDecodeObject)) + 16 * channel_count;
return 0;
}
OpusDecodeObject& OpusDecodeObject::Initialize(u64 buffer, u64 buffer2) {
auto* new_decoder = reinterpret_cast<OpusDecodeObject*>(buffer);
auto* comparison = reinterpret_cast<OpusDecodeObject*>(buffer2);
if (new_decoder->magic == DecodeObjectMagic) {
if (!new_decoder->initialized ||
(new_decoder->initialized && new_decoder->self == comparison)) {
new_decoder->state_valid = true;
}
} else {
new_decoder->initialized = false;
new_decoder->state_valid = true;
}
return *new_decoder;
Result OpusDecodeObject::InitializeDecoder(u32 sample_rate, u32 channel_count) {
if ((codec = avcodec_find_decoder(AV_CODEC_ID_OPUS)) == nullptr)
return Service::Audio::ResultLibOpusInvalidState;
if ((avctx = avcodec_alloc_context3(codec)) == nullptr)
return Service::Audio::ResultLibOpusInvalidState;
avctx->sample_rate = sample_rate;
av_channel_layout_default(&avctx->ch_layout, channel_count);
return ResultSuccess;
}
s32 OpusDecodeObject::InitializeDecoder(u32 sample_rate, u32 channel_count) {
if (!state_valid) {
return OPUS_INVALID_STATE;
}
if (initialized) {
return OPUS_OK;
}
// Unfortunately libopus does not expose the OpusDecoder struct publicly, so we can't include
// it in this class. Nintendo does not allocate memory, which is why we have a workbuffer
// provided.
// We could use _create and have libopus allocate it for us, but then we have to separately
// track which decoder is being used between this and multistream in order to call the correct
// destroy from the host side.
// This is a bit cringe, but is safe as these objects are only ever initialized inside the given
// workbuffer, and GetWorkBufferSize will guarantee there's enough space to follow.
decoder = (LibOpusDecoder*)(this + 1);
s32 ret = opus_decoder_init(decoder, sample_rate, channel_count);
if (ret == OPUS_OK) {
magic = DecodeObjectMagic;
initialized = true;
state_valid = true;
self = this;
final_range = 0;
}
return ret;
Result OpusDecodeObject::Shutdown() {
if (avctx)
avcodec_free_context(&avctx);
return ResultSuccess;
}
s32 OpusDecodeObject::Shutdown() {
if (!state_valid) {
return OPUS_INVALID_STATE;
Result OpusDecodeObject::ResetDecoder() {
if (avctx) {
avcodec_flush_buffers(avctx);
return ResultSuccess;
}
if (initialized) {
magic = 0x0;
initialized = false;
state_valid = false;
self = nullptr;
final_range = 0;
decoder = nullptr;
}
return OPUS_OK;
return Service::Audio::ResultLibOpusInvalidState;
}
s32 OpusDecodeObject::ResetDecoder() {
return opus_decoder_ctl(decoder, OPUS_RESET_STATE);
}
s32 OpusDecodeObject::Decode(u32& out_sample_count, u64 output_data, u64 output_data_size,
u64 input_data, u64 input_data_size) {
ASSERT(initialized);
Result OpusDecodeObject::Decode(u32& out_sample_count, u64 output_data, u64 output_data_size, u64 input_data, u64 input_data_size) {
out_sample_count = 0;
if (avctx) {
AVPacket* avpkt = av_packet_alloc();
av_new_packet(avpkt, int(input_data_size));
std::memcpy(avpkt->data, reinterpret_cast<const u8*>(input_data), input_data_size);
avcodec_send_packet(avctx, avpkt);
if (!state_valid) {
return OPUS_INVALID_STATE;
AVFrame* frame = av_frame_alloc();
avcodec_receive_frame(avctx, frame);
std::memcpy(reinterpret_cast<u16*>(output_data), frame->data, output_data_size);
av_frame_free(&frame);
av_packet_free(&avpkt);
out_sample_count = frame->nb_samples;
return ResultSuccess;
}
auto ret_code_or_samples = opus_decode(
decoder, reinterpret_cast<const u8*>(input_data), static_cast<opus_int32>(input_data_size),
reinterpret_cast<opus_int16*>(output_data), static_cast<opus_int32>(output_data_size), 0);
if (ret_code_or_samples < OPUS_OK) {
return ret_code_or_samples;
}
out_sample_count = ret_code_or_samples;
return opus_decoder_ctl(decoder, OPUS_GET_FINAL_RANGE_REQUEST, &final_range);
return Service::Audio::ResultLibOpusInvalidState;
}
} // namespace AudioCore::ADSP::OpusDecoder
@@ -1,38 +1,31 @@
// 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 <opus.h>
#include "common/common_types.h"
#include "audio_core/adsp/apps/opus/opus_types.h"
#include "core/hle/result.h"
struct AVCodec;
struct AVCodecContext;
namespace AudioCore::ADSP::OpusDecoder {
using LibOpusDecoder = ::OpusDecoder;
static constexpr u32 DecodeObjectMagic = 0xDEADBEEF;
class OpusDecodeObject {
public:
static u32 GetWorkBufferSize(u32 channel_count);
static OpusDecodeObject& Initialize(u64 buffer, u64 buffer2);
s32 InitializeDecoder(u32 sample_rate, u32 channel_count);
s32 Shutdown();
s32 ResetDecoder();
s32 Decode(u32& out_sample_count, u64 output_data, u64 output_data_size, u64 input_data,
u64 input_data_size);
u32 GetFinalRange() const noexcept {
return final_range;
}
Result InitializeDecoder(u32 sample_rate, u32 channel_count);
Result Shutdown();
Result ResetDecoder();
Result Decode(u32& out_sample_count, u64 output_data, u64 output_data_size, u64 input_data, u64 input_data_size);
private:
u32 magic;
bool initialized;
bool state_valid;
OpusDecodeObject* self;
u32 final_range;
LibOpusDecoder* decoder;
AVCodec const* codec = nullptr;
AVCodecContext* avctx = nullptr;
};
static_assert(std::is_trivially_constructible_v<OpusDecodeObject>);
} // namespace AudioCore::ADSP::OpusDecoder
+193 -194
View File
@@ -16,6 +16,7 @@
#include "common/thread.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/service/audio/errors.h"
namespace AudioCore::ADSP::OpusDecoder {
@@ -49,11 +50,16 @@ OpusDecoder::~OpusDecoder() {
// Shutdown the thread
Send(Direction::DSP, Message::Shutdown);
auto msg = Receive(Direction::Host);
ASSERT_MSG(msg == Message::ShutdownOK, "Expected Opus shutdown code {}, got {}",
Message::ShutdownOK, msg);
ASSERT_MSG(msg == Message::ShutdownOK, "Expected Opus shutdown code {}, got {}", Message::ShutdownOK, msg);
main_thread.request_stop();
main_thread.join();
running = false;
// Must shutdown as there are AV allocations which are manual
for (auto& e : decode_objects)
e.second.Shutdown();
for (auto& e : ms_decode_objects)
e.second.Shutdown();
}
void OpusDecoder::Send(Direction dir, u32 message) {
@@ -68,202 +74,195 @@ void OpusDecoder::Init(std::stop_token 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.");
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 = s32(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 = s32(shared_memory->host_send_data[2]);
auto channel_count = s32(shared_memory->host_send_data[3]);
ASSERT(sample_rate >= 0);
ASSERT(IsValidChannelCount(channel_count));
ASSERT(buffer_size >= OpusDecodeObject::GetWorkBufferSize(channel_count));
if (auto const it = decode_objects.find(buffer); it != decode_objects.end()) {
it->second.Shutdown();
shared_memory->dsp_return_data[0] = it->second.InitializeDecoder(sample_rate, channel_count).raw;
} else {
OpusDecodeObject obj{};
shared_memory->dsp_return_data[0] = obj.InitializeDecoder(sample_rate, channel_count).raw;
decode_objects.insert_or_assign(buffer, obj);
}
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];
if (auto const it = decode_objects.find(buffer); it != decode_objects.end()) {
shared_memory->dsp_return_data[0] = it->second.Shutdown().raw;
} else {
LOG_ERROR(Audio_DSP, "operating unregistered buffer {}", buffer);
shared_memory->dsp_return_data[0] = Service::Audio::ResultLibOpusInvalidState.raw;
}
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<u32>(shared_memory->host_send_data[5]);
auto reset_requested = shared_memory->host_send_data[6];
u32 decoded_samples{0};
if (auto const it = decode_objects.find(buffer); it != decode_objects.end()) {
auto res = ResultSuccess;
if (reset_requested)
res = it->second.ResetDecoder();
if (res == ResultSuccess)
res = it->second.Decode(decoded_samples, output_data, output_data_size, input_data, input_data_size);
auto end_time = system.CoreTiming().GetGlobalTimeUs();
shared_memory->dsp_return_data[0] = res.raw;
shared_memory->dsp_return_data[1] = decoded_samples;
shared_memory->dsp_return_data[2] = (end_time - start_time).count();
} else {
LOG_ERROR(Audio_DSP, "operating unregistered buffer {}", buffer);
shared_memory->dsp_return_data[0] = Service::Audio::ResultLibOpusInvalidState.raw;
}
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 = s32(shared_memory->host_send_data[0]);
auto stereo_stream_count = s32(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 = s32(shared_memory->host_send_data[2]);
auto channel_count = s32(shared_memory->host_send_data[3]);
auto total_stream_count = s32(shared_memory->host_send_data[4]);
auto stereo_stream_count = s32(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));
if (auto const it = ms_decode_objects.find(buffer); it != ms_decode_objects.end()) {
it->second.Shutdown();
shared_memory->dsp_return_data[0] = it->second.InitializeDecoder(sample_rate, total_stream_count, channel_count, stereo_stream_count, mappings).raw;
} else {
OpusMultiStreamDecodeObject obj{};
shared_memory->dsp_return_data[0] = obj.InitializeDecoder(sample_rate, total_stream_count, channel_count, stereo_stream_count, mappings).raw;
ms_decode_objects.insert_or_assign(buffer, obj);
}
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];
if (auto const it = ms_decode_objects.find(buffer); it != ms_decode_objects.end()) {
shared_memory->dsp_return_data[0] = it->second.Shutdown().raw;
} else {
LOG_ERROR(Audio_DSP, "operating unregistered buffer {}", buffer);
shared_memory->dsp_return_data[0] = Service::Audio::ResultLibOpusInvalidState.raw;
}
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<u32>(shared_memory->host_send_data[5]);
auto reset_requested = shared_memory->host_send_data[6];
u32 decoded_samples{0};
if (auto const it = ms_decode_objects.find(buffer); it != ms_decode_objects.end()) {
auto res = ResultSuccess;
if (reset_requested)
res = it->second.ResetDecoder();
if (res == ResultSuccess)
res = it->second.Decode(decoded_samples, output_data, output_data_size, input_data, input_data_size);
auto end_time = system.CoreTiming().GetGlobalTimeUs();
shared_memory->dsp_return_data[0] = res.raw;
shared_memory->dsp_return_data[1] = decoded_samples;
shared_memory->dsp_return_data[2] = (end_time - start_time).count();
} else {
LOG_ERROR(Audio_DSP, "operating unregistered buffer {}", buffer);
shared_memory->dsp_return_data[0] = Service::Audio::ResultLibOpusInvalidState.raw;
}
Send(Direction::Host, Message::DecodeInterleavedForMultiStreamOK);
break;
}
default:
LOG_ERROR(Audio_DSP, "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<s32>(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<s32>(shared_memory->host_send_data[2]);
auto channel_count = static_cast<s32>(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<u32>(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<s32>(shared_memory->host_send_data[0]);
auto stereo_stream_count = static_cast<s32>(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<s32>(shared_memory->host_send_data[2]);
auto channel_count = static_cast<s32>(shared_memory->host_send_data[3]);
auto total_stream_count = static_cast<s32>(shared_memory->host_send_data[4]);
auto stereo_stream_count = static_cast<s32>(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<u32>(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
+7 -3
View File
@@ -9,6 +9,9 @@
#include <memory>
#include <thread>
#include "ankerl/unordered_dense.h"
#include "audio_core/adsp/apps/opus/opus_decode_object.h"
#include "audio_core/adsp/apps/opus/opus_multistream_decode_object.h"
#include "audio_core/adsp/apps/opus/shared_memory.h"
#include "audio_core/adsp/mailbox.h"
#include "common/common_types.h"
@@ -68,9 +71,7 @@ public:
}
private:
/**
* Initializing thread, launched at audio_core boot to avoid blocking the main emu boot thread.
*/
/// @brief 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.
@@ -90,6 +91,9 @@ private:
/// Structure shared with the host, input data set by the host before sending a mailbox message,
/// and the responses are written back by the OpusDecoder.
SharedMemory* shared_memory{};
ankerl::unordered_dense::map<u64, OpusDecodeObject> decode_objects;
ankerl::unordered_dense::map<u64, OpusMultiStreamDecodeObject> ms_decode_objects;
};
} // namespace AudioCore::ADSP::OpusDecoder
@@ -1,8 +1,20 @@
// 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
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavcodec/codec.h>
#include <libavcodec/packet.h>
#include <libavutil/channel_layout.h>
#include <libavutil/frame.h>
}
#include "audio_core/adsp/apps/opus/opus_multistream_decode_object.h"
#include "common/assert.h"
#include "core/hle/result.h"
#include "core/hle/service/audio/errors.h"
namespace AudioCore::ADSP::OpusDecoder {
@@ -12,100 +24,59 @@ bool IsValidChannelCount(u32 channel_count) {
}
bool IsValidStreamCounts(u32 total_stream_count, u32 stereo_stream_count) {
return total_stream_count > 0 && static_cast<s32>(stereo_stream_count) >= 0 &&
stereo_stream_count <= total_stream_count && IsValidChannelCount(total_stream_count);
return total_stream_count > 0 && s32(stereo_stream_count) >= 0
&& stereo_stream_count <= total_stream_count && IsValidChannelCount(total_stream_count);
}
} // namespace
u32 OpusMultiStreamDecodeObject::GetWorkBufferSize(u32 total_stream_count,
u32 stereo_stream_count) {
if (IsValidStreamCounts(total_stream_count, stereo_stream_count)) {
return static_cast<u32>(sizeof(OpusMultiStreamDecodeObject)) +
opus_multistream_decoder_get_size(total_stream_count, stereo_stream_count);
}
u32 OpusMultiStreamDecodeObject::GetWorkBufferSize(u32 total_stream_count, u32 stereo_stream_count) {
if (IsValidStreamCounts(total_stream_count, stereo_stream_count))
return u32(sizeof(OpusMultiStreamDecodeObject)) + 2556 * (total_stream_count * stereo_stream_count);
return 0;
}
OpusMultiStreamDecodeObject& OpusMultiStreamDecodeObject::Initialize(u64 buffer, u64 buffer2) {
auto* new_decoder = reinterpret_cast<OpusMultiStreamDecodeObject*>(buffer);
auto* comparison = reinterpret_cast<OpusMultiStreamDecodeObject*>(buffer2);
if (new_decoder->magic == DecodeMultiStreamObjectMagic) {
if (!new_decoder->initialized ||
(new_decoder->initialized && new_decoder->self == comparison)) {
new_decoder->state_valid = true;
}
} else {
new_decoder->initialized = false;
new_decoder->state_valid = true;
}
return *new_decoder;
Result OpusMultiStreamDecodeObject::InitializeDecoder(u32 sample_rate, u32 total_stream_count, u32 channel_count, u32 stereo_stream_count, u8* mappings) {
if ((codec = avcodec_find_decoder(AV_CODEC_ID_OPUS)) == nullptr)
return Service::Audio::ResultLibOpusInvalidState;
if ((avctx = avcodec_alloc_context3(codec)))
return Service::Audio::ResultLibOpusInvalidState;
avctx->sample_rate = sample_rate;
av_channel_layout_default(&avctx->ch_layout, channel_count);
return ResultSuccess;
}
s32 OpusMultiStreamDecodeObject::InitializeDecoder(u32 sample_rate, u32 total_stream_count,
u32 channel_count, u32 stereo_stream_count,
u8* mappings) {
if (!state_valid) {
return OPUS_INVALID_STATE;
}
if (initialized) {
return OPUS_OK;
}
// See OpusDecodeObject::InitializeDecoder for an explanation of this
decoder = (LibOpusMSDecoder*)(this + 1);
s32 ret = opus_multistream_decoder_init(decoder, sample_rate, channel_count, total_stream_count,
stereo_stream_count, mappings);
if (ret == OPUS_OK) {
magic = DecodeMultiStreamObjectMagic;
initialized = true;
state_valid = true;
self = this;
final_range = 0;
}
return ret;
Result OpusMultiStreamDecodeObject::Shutdown() {
if (avctx)
avcodec_free_context(&avctx);
return ResultSuccess;
}
s32 OpusMultiStreamDecodeObject::Shutdown() {
if (!state_valid) {
return OPUS_INVALID_STATE;
Result OpusMultiStreamDecodeObject::ResetDecoder() {
if (avctx) {
avcodec_flush_buffers(avctx);
return ResultSuccess;
}
if (initialized) {
magic = 0x0;
initialized = false;
state_valid = false;
self = nullptr;
final_range = 0;
decoder = nullptr;
}
return OPUS_OK;
return Service::Audio::ResultLibOpusInvalidState;
}
s32 OpusMultiStreamDecodeObject::ResetDecoder() {
return opus_multistream_decoder_ctl(decoder, OPUS_RESET_STATE);
}
s32 OpusMultiStreamDecodeObject::Decode(u32& out_sample_count, u64 output_data,
u64 output_data_size, u64 input_data, u64 input_data_size) {
ASSERT(initialized);
Result OpusMultiStreamDecodeObject::Decode(u32& out_sample_count, u64 output_data, u64 output_data_size, u64 input_data, u64 input_data_size) {
out_sample_count = 0;
if (avctx) {
AVPacket* avpkt = av_packet_alloc();
av_new_packet(avpkt, int(input_data_size));
std::memcpy(avpkt->data, reinterpret_cast<const u8*>(input_data), input_data_size);
avcodec_send_packet(avctx, avpkt);
if (!state_valid) {
return OPUS_INVALID_STATE;
AVFrame* frame = av_frame_alloc();
avcodec_receive_frame(avctx, frame);
std::memcpy(reinterpret_cast<u16*>(output_data), frame->data, output_data_size);
av_frame_free(&frame);
av_packet_free(&avpkt);
out_sample_count = frame->nb_samples;
return ResultSuccess;
}
auto ret_code_or_samples = opus_multistream_decode(
decoder, reinterpret_cast<const u8*>(input_data), static_cast<opus_int32>(input_data_size),
reinterpret_cast<opus_int16*>(output_data), static_cast<opus_int32>(output_data_size), 0);
if (ret_code_or_samples < OPUS_OK) {
return ret_code_or_samples;
}
out_sample_count = ret_code_or_samples;
return opus_multistream_decoder_ctl(decoder, OPUS_GET_FINAL_RANGE_REQUEST, &final_range);
return Service::Audio::ResultLibOpusInvalidState;
}
} // namespace AudioCore::ADSP::OpusDecoder
@@ -1,39 +1,31 @@
// 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 <opus_multistream.h>
#include "audio_core/adsp/apps/opus/opus_types.h"
#include "common/common_types.h"
#include "core/hle/result.h"
struct AVCodec;
struct AVCodecContext;
namespace AudioCore::ADSP::OpusDecoder {
using LibOpusMSDecoder = ::OpusMSDecoder;
static constexpr u32 DecodeMultiStreamObjectMagic = 0xDEADBEEF;
class OpusMultiStreamDecodeObject {
public:
static u32 GetWorkBufferSize(u32 total_stream_count, u32 stereo_stream_count);
static OpusMultiStreamDecodeObject& Initialize(u64 buffer, u64 buffer2);
s32 InitializeDecoder(u32 sample_rate, u32 total_stream_count, u32 channel_count,
u32 stereo_stream_count, u8* mappings);
s32 Shutdown();
s32 ResetDecoder();
s32 Decode(u32& out_sample_count, u64 output_data, u64 output_data_size, u64 input_data,
u64 input_data_size);
u32 GetFinalRange() const noexcept {
return final_range;
}
Result InitializeDecoder(u32 sample_rate, u32 total_stream_count, u32 channel_count, u32 stereo_stream_count, u8* mappings);
Result Shutdown();
Result ResetDecoder();
Result Decode(u32& out_sample_count, u64 output_data, u64 output_data_size, u64 input_data, u64 input_data_size);
private:
u32 magic;
bool initialized;
bool state_valid;
OpusMultiStreamDecodeObject* self;
u32 final_range;
LibOpusMSDecoder* decoder;
AVCodec const* codec = nullptr;
AVCodecContext* avctx = nullptr;
};
static_assert(std::is_trivially_constructible_v<OpusMultiStreamDecodeObject>);
} // namespace AudioCore::ADSP::OpusDecoder
@@ -0,0 +1,32 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "common/common_types.h"
namespace AudioCore::ADSP {
static constexpr u32 DECODE_OBJECT_MAGIC = 0xDEADBEEF;
struct LibOpusDecoder {
u32 magic;
bool initialized;
bool state_valid;
LibOpusDecoder* self;
u32 final_range;
void* decoder;
};
static_assert(sizeof(LibOpusDecoder) == 32);
static constexpr u32 DECODE_MULTISTREAM_OBJECT_MAGIC = 0xDEADBEEF;
struct LibOpusMultistreamDecoder {
u32 magic;
bool initialized;
bool state_valid;
LibOpusMultistreamDecoder* self;
u32 final_range;
void* decoder;
};
static_assert(sizeof(LibOpusMultistreamDecoder) == 32);
}
+26 -48
View File
@@ -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,39 +9,18 @@
#include "audio_core/audio_core.h"
#include "audio_core/opus/hardware_opus.h"
#include "core/core.h"
#include "core/hle/result.h"
namespace AudioCore::OpusDecoder {
namespace {
using namespace Service::Audio;
static constexpr Result ResultCodeFromLibOpusErrorCode(u64 error_code) {
s32 error{static_cast<s32>(error_code)};
ASSERT(error <= OPUS_OK);
switch (error) {
case OPUS_ALLOC_FAIL:
R_THROW(ResultLibOpusAllocFail);
case OPUS_INVALID_STATE:
R_THROW(ResultLibOpusInvalidState);
case OPUS_UNIMPLEMENTED:
R_THROW(ResultLibOpusUnimplemented);
case OPUS_INVALID_PACKET:
R_THROW(ResultLibOpusInvalidPacket);
case OPUS_INTERNAL_ERROR:
R_THROW(ResultLibOpusInternalError);
case OPUS_BUFFER_TOO_SMALL:
R_THROW(ResultBufferTooSmall);
case OPUS_BAD_ARG:
R_THROW(ResultLibOpusBadArg);
case OPUS_OK:
R_RETURN(ResultSuccess);
}
UNREACHABLE();
}
} // namespace
HardwareOpus::HardwareOpus(Core::System& system_)
: system{system_}, opus_decoder{system.AudioCore().ADSP().OpusDecoder()} {
: system{system_}
, opus_decoder{system.AudioCore().ADSP().OpusDecoder()}
{
opus_decoder.SetSharedMemory(shared_memory);
}
@@ -89,7 +71,7 @@ Result HardwareOpus::InitializeDecodeObject(u32 sample_rate, u32 channel_count,
R_THROW(ResultInvalidOpusDSPReturnCode);
}
R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0]));
R_RETURN(Result(u32(shared_memory.dsp_return_data[0])));
}
Result HardwareOpus::InitializeMultiStreamDecodeObject(u32 sample_rate, u32 channel_count,
@@ -117,7 +99,7 @@ Result HardwareOpus::InitializeMultiStreamDecodeObject(u32 sample_rate, u32 chan
R_THROW(ResultInvalidOpusDSPReturnCode);
}
R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0]));
R_RETURN(Result(u32(shared_memory.dsp_return_data[0])));
}
Result HardwareOpus::ShutdownDecodeObject(void* buffer, u64 buffer_size) {
@@ -131,7 +113,7 @@ Result HardwareOpus::ShutdownDecodeObject(void* buffer, u64 buffer_size) {
"Expected Opus shutdown code {}, got {}",
ADSP::OpusDecoder::Message::ShutdownDecodeObjectOK, msg);
R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0]));
R_RETURN(Result(u32(shared_memory.dsp_return_data[0])));
}
Result HardwareOpus::ShutdownMultiStreamDecodeObject(void* buffer, u64 buffer_size) {
@@ -146,7 +128,7 @@ Result HardwareOpus::ShutdownMultiStreamDecodeObject(void* buffer, u64 buffer_si
"Expected Opus shutdown code {}, got {}",
ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObjectOK, msg);
R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0]));
R_RETURN(Result(u32(shared_memory.dsp_return_data[0])));
}
Result HardwareOpus::DecodeInterleaved(u32& out_sample_count, void* output_data,
@@ -170,12 +152,12 @@ Result HardwareOpus::DecodeInterleaved(u32& out_sample_count, void* output_data,
R_THROW(ResultInvalidOpusDSPReturnCode);
}
auto error_code{static_cast<s32>(shared_memory.dsp_return_data[0])};
if (error_code == OPUS_OK) {
out_sample_count = static_cast<u32>(shared_memory.dsp_return_data[1]);
auto error_code = s32(shared_memory.dsp_return_data[0]);
if (error_code == ResultSuccess.raw) {
out_sample_count = u32(shared_memory.dsp_return_data[1]);
out_time_taken = 1000 * shared_memory.dsp_return_data[2];
}
R_RETURN(ResultCodeFromLibOpusErrorCode(error_code));
R_RETURN(Result(u32(error_code)));
}
Result HardwareOpus::DecodeInterleavedForMultiStream(u32& out_sample_count, void* output_data,
@@ -184,29 +166,27 @@ Result HardwareOpus::DecodeInterleavedForMultiStream(u32& out_sample_count, void
void* buffer, u64& out_time_taken,
bool reset) {
std::scoped_lock l{mutex};
shared_memory.host_send_data[0] = (u64)buffer;
shared_memory.host_send_data[1] = (u64)input_data;
shared_memory.host_send_data[0] = u64(buffer);
shared_memory.host_send_data[1] = u64(input_data);
shared_memory.host_send_data[2] = input_data_size;
shared_memory.host_send_data[3] = (u64)output_data;
shared_memory.host_send_data[3] = u64(output_data);
shared_memory.host_send_data[4] = output_data_size;
shared_memory.host_send_data[5] = 0;
shared_memory.host_send_data[6] = reset;
opus_decoder.Send(ADSP::Direction::DSP,
ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStream);
opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStream);
auto msg = opus_decoder.Receive(ADSP::Direction::Host);
if (msg != ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStreamOK) {
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStreamOK, msg);
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStreamOK, msg);
R_THROW(ResultInvalidOpusDSPReturnCode);
}
auto error_code{static_cast<s32>(shared_memory.dsp_return_data[0])};
if (error_code == OPUS_OK) {
auto const error_code = shared_memory.dsp_return_data[0];
if (error_code == ResultSuccess.raw) {
out_sample_count = static_cast<u32>(shared_memory.dsp_return_data[1]);
out_time_taken = 1000 * shared_memory.dsp_return_data[2];
}
R_RETURN(ResultCodeFromLibOpusErrorCode(error_code));
R_RETURN(Result(u32(shared_memory.dsp_return_data[0])));
}
Result HardwareOpus::MapMemory(void* buffer, u64 buffer_size) {
@@ -217,8 +197,7 @@ Result HardwareOpus::MapMemory(void* buffer, u64 buffer_size) {
opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::MapMemory);
auto msg = opus_decoder.Receive(ADSP::Direction::Host);
if (msg != ADSP::OpusDecoder::Message::MapMemoryOK) {
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
ADSP::OpusDecoder::Message::MapMemoryOK, msg);
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", ADSP::OpusDecoder::Message::MapMemoryOK, msg);
R_THROW(ResultInvalidOpusDSPReturnCode);
}
R_SUCCEED();
@@ -232,8 +211,7 @@ Result HardwareOpus::UnmapMemory(void* buffer, u64 buffer_size) {
opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::UnmapMemory);
auto msg = opus_decoder.Receive(ADSP::Direction::Host);
if (msg != ADSP::OpusDecoder::Message::UnmapMemoryOK) {
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
ADSP::OpusDecoder::Message::UnmapMemoryOK, msg);
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", ADSP::OpusDecoder::Message::UnmapMemoryOK, msg);
R_THROW(ResultInvalidOpusDSPReturnCode);
}
R_SUCCEED();
+3 -2
View File
@@ -1,11 +1,12 @@
// 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 <mutex>
#include <opus.h>
#include "audio_core/adsp/apps/opus/opus_decoder.h"
#include "audio_core/adsp/apps/opus/shared_memory.h"
#include "audio_core/adsp/mailbox.h"
-23
View File
@@ -476,29 +476,6 @@ std::string SanitizePath(std::string_view path_, DirectorySeparator directory_se
path.erase(std::unique(start, path.end(),
[type2](char c1, char c2) { return c1 == type2 && c2 == type2; }),
path.end());
const bool absolute = !path.empty() && path[0] == type2;
std::vector<std::string_view> parts;
for (const auto part : SplitPathComponents(path))
{
if (part.empty() || part == ".")
continue;
if (part == ".." && !parts.empty() && parts.back() != "..")
parts.pop_back();
else if (part != "..") parts.push_back(part);
}
std::string resolved = absolute ? std::string(1, type2) : std::string{};
for (std::size_t i = 0; i < parts.size(); ++i)
{
if (i != 0)
resolved += type2;
resolved.append(parts[i].data(), parts[i].size());
}
path = std::move(resolved);
return std::string(RemoveTrailingSlash(path));
}
+8 -16
View File
@@ -156,6 +156,14 @@ void UpdateGPUAccuracy() {
values.current_gpu_accuracy = values.gpu_accuracy.GetValue();
}
bool IsGPULevelLow() {
return values.current_gpu_accuracy == GpuAccuracy::Low;
}
bool IsGPULevelMedium() {
return values.current_gpu_accuracy == GpuAccuracy::Medium;
}
bool IsGPULevelHigh() {
return values.current_gpu_accuracy == GpuAccuracy::High;
}
@@ -168,22 +176,6 @@ bool IsDMALevelSafe() {
return values.dma_accuracy.GetValue() == DmaAccuracy::Safe;
}
bool IsGPUFenceBehaviorDefault() {
return values.gpu_fence_behavior.GetValue() == GpuFenceBehavior::Default;
}
bool IsGPUFenceBehaviorBalanced() {
return values.gpu_fence_behavior.GetValue() == GpuFenceBehavior::Balanced;
}
bool IsGPUFenceBehaviorAccurate() {
return values.gpu_fence_behavior.GetValue() == GpuFenceBehavior::Accurate;
}
bool IsGPUFenceBehaviorStrict() {
return values.gpu_fence_behavior.GetValue() == GpuFenceBehavior::Strict;
}
bool IsFastmemEnabled() {
if (values.cpu_accuracy.GetValue() == Settings::CpuAccuracy::Debugging)
return bool(values.cpuopt_fastmem);
+11 -17
View File
@@ -420,7 +420,7 @@ struct Values {
#ifdef __ANDROID__
GpuAccuracy::Low,
#else
GpuAccuracy::High,
GpuAccuracy::Medium,
#endif
"gpu_accuracy",
Category::RendererAdvanced,
@@ -428,7 +428,7 @@ struct Values {
true,
true};
GpuAccuracy current_gpu_accuracy{GpuAccuracy::High};
GpuAccuracy current_gpu_accuracy{GpuAccuracy::Medium};
SwitchableSetting<DmaAccuracy, true> dma_accuracy{linkage,
DmaAccuracy::Default,
@@ -438,16 +438,6 @@ struct Values {
true,
true};
SwitchableSetting<GpuFenceBehavior, true> gpu_fence_behavior{linkage,
GpuFenceBehavior::Default,
GpuFenceBehavior::Default,
GpuFenceBehavior::Strict,
"gpu_fence_behavior",
Category::RendererAdvanced,
Specialization::Default,
true,
true};
SwitchableSetting<VramUsageMode, true> vram_usage_mode{linkage,
VramUsageMode::Conservative,
"vram_usage_mode",
@@ -555,6 +545,13 @@ struct Values {
Specialization::Default,
true,
true};
SwitchableSetting<bool> antiflicker{linkage,
false,
"antiflicker",
Category::RendererHacks,
Specialization::Default,
true,
true};
SwitchableSetting<bool> async_presentation{linkage,
#ifdef __ANDROID__
false,
@@ -874,16 +871,13 @@ extern Values values;
bool getDebugKnobAt(u8 i);
void UpdateGPUAccuracy();
bool IsGPULevelLow();
bool IsGPULevelMedium();
bool IsGPULevelHigh();
bool IsDMALevelDefault();
bool IsDMALevelSafe();
bool IsGPUFenceBehaviorDefault();
bool IsGPUFenceBehaviorBalanced();
bool IsGPUFenceBehaviorAccurate();
bool IsGPUFenceBehaviorStrict();
bool IsFastmemEnabled();
void SetNceEnabled(bool is_64bit);
bool IsNceEnabled();
+1 -2
View File
@@ -135,9 +135,8 @@ ENUM(FramePacingMode, Target_Auto, Target_30, Target_60, Target_90, Target_120);
ENUM(VSyncMode, Immediate, Mailbox, Fifo, FifoRelaxed);
ENUM(VramUsageMode, Conservative, Aggressive);
ENUM(RendererBackend, OpenGL_GLSL, Vulkan, Null, OpenGL_GLASM, OpenGL_SPIRV);
ENUM(GpuAccuracy, Low, High);
ENUM(GpuAccuracy, Low, Medium, High);
ENUM(DmaAccuracy, Default, Unsafe, Safe);
ENUM(GpuFenceBehavior, Default, Immediate, Balanced, Accurate, Strict);
ENUM(CpuBackend, Dynarmic, Nce);
ENUM(CpuAccuracy, Auto, Accurate, Unsafe, Paranoid, Debugging);
ENUM(CpuClock, Off, Boost, Fast)
-1
View File
@@ -1209,7 +1209,6 @@ else()
endif()
endif()
target_include_directories(core PRIVATE ${OPUS_INCLUDE_DIRS})
target_link_libraries(core PUBLIC common PRIVATE audio_core hid_core network video_core nx_tzdb tz)
if (BOOST_NO_HEADERS)
+1 -18
View File
@@ -145,14 +145,7 @@ bool Patcher::PatchText(std::span<const u8> program_image, const Kernel::CodeSet
// MRS Xn, CNTFRQ_EL0
if (auto mrs = MRS{inst}; mrs.Verify() && mrs.GetSystemReg() == CntfrqEl0) {
bool pre_buffer = false;
auto ret = AddRelocations(pre_buffer);
if (pre_buffer) {
WriteCntfrqHandler(ret, oaknut::XReg{static_cast<int>(mrs.GetRt())}, c_pre);
} else {
WriteCntfrqHandler(ret, oaknut::XReg{static_cast<int>(mrs.GetRt())}, c);
}
continue;
UNREACHABLE();
}
// MSR TPIDR_EL0, Xn
@@ -584,16 +577,6 @@ void Patcher::WriteMsrHandler(ModuleDestLabel module_dest, oaknut::XReg src_reg,
this->BranchToModule(module_dest);
}
void Patcher::WriteCntfrqHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg, oaknut::VectorCodeGenerator& cg) {
cg.MOV(dest_reg, Common::WallClock::CNTFRQ);
// Jump back to the instruction after the emulated MRS.
if (&cg == &c_pre)
this->BranchToModulePre(module_dest);
else
this->BranchToModule(module_dest);
}
void Patcher::WriteCntpctHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg, oaknut::VectorCodeGenerator& cg) {
#if defined(HAS_NCE)
static Common::WallClock clock(false, 1);
-2
View File
@@ -80,7 +80,6 @@ private:
void WriteSvcTrampoline(ModuleDestLabel module_dest, u32 svc_id, oaknut::VectorCodeGenerator& code, oaknut::Label& save_ctx, oaknut::Label& load_ctx);
void WriteMrsHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg, oaknut::SystemReg src_reg, oaknut::VectorCodeGenerator& code);
void WriteMsrHandler(ModuleDestLabel module_dest, oaknut::XReg src_reg, oaknut::VectorCodeGenerator& code);
void WriteCntfrqHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg, oaknut::VectorCodeGenerator& code);
void WriteCntpctHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg, oaknut::VectorCodeGenerator& code);
// Convenience wrappers using default code generator
@@ -91,7 +90,6 @@ private:
void WriteSvcTrampoline(ModuleDestLabel module_dest, u32 svc_id) { WriteSvcTrampoline(module_dest, svc_id, c, m_save_context, m_load_context); }
void WriteMrsHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg, oaknut::SystemReg src_reg) { WriteMrsHandler(module_dest, dest_reg, src_reg, c); }
void WriteMsrHandler(ModuleDestLabel module_dest, oaknut::XReg src_reg) { WriteMsrHandler(module_dest, src_reg, c); }
void WriteCntfrqHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg) { WriteCntfrqHandler(module_dest, dest_reg, c); }
void WriteCntpctHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg) { WriteCntpctHandler(module_dest, dest_reg, c); }
private:
+1 -1
View File
@@ -140,7 +140,7 @@ void ProgramMetadata::LoadManual(bool is_64_bit, ProgramAddressSpaceType address
}
bool ProgramMetadata::Is64BitProgram() const {
return bool(npdm_header.has_64_bit_instructions);
return npdm_header.has_64_bit_instructions;
}
ProgramAddressSpaceType ProgramMetadata::GetAddressSpaceType() const {
-16
View File
@@ -984,22 +984,6 @@ bool RegisteredCache::RemoveExistingEntry(u64 title_id) const {
return removed_data;
}
bool RegisteredCache::Delete(const NcaID& id) const {
const auto path = GetRelativePathFromNcaID(id, false, true, false);
const bool is_file = dir->GetFileRelative(path) != nullptr;
const bool is_dir = dir->GetDirectoryRelative(path) != nullptr;
if (is_file) {
return dir->DeleteFile(path);
}
if (is_dir) {
return dir->DeleteSubdirectoryRecursive(path);
}
return true;
}
InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFunction& copy,
bool overwrite_if_exists,
std::optional<NcaID> override_id) {
-1
View File
@@ -188,7 +188,6 @@ public:
// Removes an existing entry based on title id
bool RemoveExistingEntry(u64 title_id) const;
bool Delete(const NcaID& id) const;
private:
template <typename T>
+5 -17
View File
@@ -35,16 +35,6 @@ namespace {
constexpr size_t MaxOpenFiles = 8192;
bool IsWithinRoot(std::string_view root, std::string_view full_path) {
if (root.empty())
return true;
if (full_path.size() < root.size() || full_path.substr(0, root.size()) != root)
return false;
return full_path.size() == root.size() || full_path[root.size()] == '/' || full_path[root.size()] == '\\';
}
constexpr FS::FileAccessMode ModeFlagsToFileAccessMode(OpenMode mode) {
switch (mode) {
case OpenMode::Read:
@@ -413,8 +403,7 @@ RealVfsDirectory::~RealVfsDirectory() = default;
VirtualFile RealVfsDirectory::GetFileRelative(std::string_view relative_path) const {
const auto full_path = FS::SanitizePath(path + '/' + std::string(relative_path));
if (!FS::Exists(full_path) || FS::IsDir(full_path)
|| !IsWithinRoot(FS::SanitizePath(path), full_path)) {
if (!FS::Exists(full_path) || FS::IsDir(full_path)) {
return nullptr;
}
return base.OpenFile(full_path, perms);
@@ -422,8 +411,7 @@ VirtualFile RealVfsDirectory::GetFileRelative(std::string_view relative_path) co
VirtualDir RealVfsDirectory::GetDirectoryRelative(std::string_view relative_path) const {
const auto full_path = FS::SanitizePath(path + '/' + std::string(relative_path));
if (!FS::Exists(full_path) || !FS::IsDir(full_path)
|| !IsWithinRoot(FS::SanitizePath(path), full_path)) {
if (!FS::Exists(full_path) || !FS::IsDir(full_path)) {
return nullptr;
}
return base.OpenDirectory(full_path, perms);
@@ -439,7 +427,7 @@ VirtualDir RealVfsDirectory::GetSubdirectory(std::string_view name) const {
VirtualFile RealVfsDirectory::CreateFileRelative(std::string_view relative_path) {
const auto full_path = FS::SanitizePath(path + '/' + std::string(relative_path));
if (!FS::CreateParentDirs(full_path) || !IsWithinRoot(FS::SanitizePath(path), full_path)) {
if (!FS::CreateParentDirs(full_path)) {
return nullptr;
}
return base.CreateFile(full_path, perms);
@@ -452,7 +440,7 @@ VirtualDir RealVfsDirectory::CreateDirectoryRelative(std::string_view relative_p
bool RealVfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) {
const auto full_path = FS::SanitizePath(this->path + '/' + std::string(name));
return FS::RemoveDirRecursively(full_path);
return base.DeleteDirectory(full_path);
}
std::vector<VirtualFile> RealVfsDirectory::GetFiles() const {
@@ -518,7 +506,7 @@ VirtualFile RealVfsDirectory::CreateFile(std::string_view name) {
bool RealVfsDirectory::DeleteSubdirectory(std::string_view name) {
const std::string subdir_path = (path + '/').append(name);
return FS::RemoveDir(subdir_path);
return base.DeleteDirectory(subdir_path);
}
bool RealVfsDirectory::DeleteFile(std::string_view name) {
@@ -57,7 +57,7 @@ ISelfController::ISelfController(Core::System& system_, std::shared_ptr<Applet>
{64, nullptr, "SetInputDetectionSourceSet"},
{65, D<&ISelfController::ReportUserIsActive>, "ReportUserIsActive"},
{66, nullptr, "GetCurrentIlluminance"},
{67, D<&ISelfController::IsIlluminanceAvailable>, "IsIlluminanceAvailable"},
{67, nullptr, "IsIlluminanceAvailable"},
{68, D<&ISelfController::SetAutoSleepDisabled>, "SetAutoSleepDisabled"},
{69, D<&ISelfController::IsAutoSleepDisabled>, "IsAutoSleepDisabled"},
{70, nullptr, "ReportMultimediaError"},
@@ -347,12 +347,6 @@ Result ISelfController::IsAutoSleepDisabled(Out<bool> out_is_auto_sleep_disabled
R_SUCCEED();
}
Result ISelfController::IsIlluminanceAvailable(Out<bool> out_is_illuminance_available) {
LOG_WARNING(Service_AM, "(stubbed)");
*out_is_illuminance_available = false;
R_SUCCEED();
}
Result ISelfController::SetInputDetectionPolicy(InputDetectionPolicy input_detection_policy) {
LOG_WARNING(Service_AM, "(STUBBED) called");
R_SUCCEED();
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
@@ -60,7 +60,6 @@ private:
Result ReportUserIsActive();
Result SetAutoSleepDisabled(bool is_auto_sleep_disabled);
Result IsAutoSleepDisabled(Out<bool> out_is_auto_sleep_disabled);
Result IsIlluminanceAvailable(Out<bool> out_is_illuminance_available);
Result SetInputDetectionPolicy(InputDetectionPolicy input_detection_policy);
Result GetAccumulatedSuspendedTickValue(Out<u64> out_accumulated_suspended_tick_value);
Result GetAccumulatedSuspendedTickChangedEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
+10 -42
View File
@@ -43,16 +43,6 @@ static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base,
return base->GetDirectoryRelative(dir_name);
}
static std::string_view GetGuestParentPath(std::string_view path) {
const auto name_index = path.find_last_of("\\/");
return name_index == std::string_view::npos ? std::string_view{} : path.substr(0, name_index);
}
static std::string_view GetGuestFilename(std::string_view path) {
const auto name_index = path.find_last_of("\\/");
return name_index == std::string_view::npos ? path : path.substr(name_index + 1);
}
VfsDirectoryServiceWrapper::VfsDirectoryServiceWrapper(FileSys::VirtualDir backing_)
: backing(std::move(backing_)) {}
@@ -93,12 +83,11 @@ Result VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const {
return ResultSuccess;
}
const auto filename = GetGuestFilename(path);
auto dir = GetDirectoryRelativeWrapped(backing, GetGuestParentPath(path));
if (filename.empty() || dir == nullptr || dir->GetFile(filename) == nullptr) {
auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
if (dir == nullptr || dir->GetFile(Common::FS::GetFilename(path)) == nullptr) {
return FileSys::ResultPathNotFound;
}
if (!dir->DeleteFile(filename)) {
if (!dir->DeleteFile(Common::FS::GetFilename(path))) {
// TODO(DarkLordZach): Find a better error code for this
return ResultUnknown;
}
@@ -108,9 +97,7 @@ Result VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const {
Result VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) const {
std::string path(Common::FS::SanitizePath(path_));
if (GetDirectoryRelativeWrapped(backing, path) != nullptr) {
return FileSys::ResultPathAlreadyExists;
}
// NOTE: This is inaccurate behavior. CreateDirectory is not recursive.
// CreateDirectory should return PathNotFound if the parent directory does not exist.
// This is here temporarily in order to have UMM "work" in the meantime.
@@ -130,19 +117,8 @@ Result VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) con
Result VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_) const {
std::string path(Common::FS::SanitizePath(path_));
const auto dirname = GetGuestFilename(path);
auto dir = GetDirectoryRelativeWrapped(backing, GetGuestParentPath(path));
FileSys::VirtualDir target{};
if (!dirname.empty() && dir != nullptr) {
target = dir->GetSubdirectory(dirname);
}
if (target == nullptr) {
return FileSys::ResultPathNotFound;
}
if (!target->GetFiles().empty() || !target->GetSubdirectories().empty()) {
return ResultUnknown;
}
if (!dir->DeleteSubdirectory(dirname)) {
auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
if (!dir->DeleteSubdirectory(Common::FS::GetFilename(path))) {
// TODO(DarkLordZach): Find a better error code for this
return ResultUnknown;
}
@@ -151,12 +127,8 @@ Result VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_) con
Result VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string& path_) const {
std::string path(Common::FS::SanitizePath(path_));
const auto dirname = GetGuestFilename(path);
auto dir = GetDirectoryRelativeWrapped(backing, GetGuestParentPath(path));
if (dirname.empty() || dir == nullptr || dir->GetSubdirectory(dirname) == nullptr) {
return FileSys::ResultPathNotFound;
}
if (!dir->DeleteSubdirectoryRecursive(dirname)) {
auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
if (!dir->DeleteSubdirectoryRecursive(Common::FS::GetFilename(path))) {
// TODO(DarkLordZach): Find a better error code for this
return ResultUnknown;
}
@@ -165,13 +137,9 @@ Result VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string&
Result VfsDirectoryServiceWrapper::CleanDirectoryRecursively(const std::string& path) const {
const std::string sanitized_path(Common::FS::SanitizePath(path));
const auto dirname = GetGuestFilename(sanitized_path);
auto dir = GetDirectoryRelativeWrapped(backing, GetGuestParentPath(sanitized_path));
auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(sanitized_path));
if (dirname.empty() || dir == nullptr || dir->GetSubdirectory(dirname) == nullptr) {
return FileSys::ResultPathNotFound;
}
if (!dir->CleanSubdirectoryRecursive(dirname)) {
if (!dir->CleanSubdirectoryRecursive(Common::FS::GetFilename(sanitized_path))) {
// TODO(DarkLordZach): Find a better error code for this
return ResultUnknown;
}
+1 -7
View File
@@ -237,7 +237,7 @@ IHidServer::IHidServer(Core::System& system_, std::shared_ptr<ResourceManager> r
{3013, nullptr, "SetDebugPadGenericPadMap"}, //21.0.0+
{3014, nullptr, "GetDebugPadKeyboardMap"}, //21.0.0+
{3015, nullptr, "SetDebugPadKeyboardMap"}, //21.0.0+
{3150, C<&IHidServer::SetMouseLibraryVersion>, "SetMouseLibraryVersion"}, //21.0.0+
{3150, nullptr, "SetMouseLibraryVersion"}, //21.0.0+
// What? -- {12010, nullptr, "SetButtonConfigLeft"},
};
// clang-format on
@@ -1471,12 +1471,6 @@ Result IHidServer::SetTouchScreenResolution(u32 width, u32 height,
R_SUCCEED();
}
Result IHidServer::SetMouseLibraryVersion(ClientAppletResourceUserId aruid) {
LOG_INFO(Service_HID, "(STUBBED) called, applet_resource_user_id={}", aruid.pid);
R_SUCCEED();
}
std::shared_ptr<ResourceManager> IHidServer::GetResourceManager() {
resource_manager->Initialize();
return resource_manager;
+1 -2
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@@ -263,7 +263,6 @@ private:
Result IsFirmwareUpdateNeededForNotification(Out<bool> out_is_firmware_update_needed,
s32 unknown, ClientAppletResourceUserId aruid);
Result SetTouchScreenResolution(u32 width, u32 height, ClientAppletResourceUserId aruid);
Result SetMouseLibraryVersion(ClientAppletResourceUserId aruid);
std::shared_ptr<ResourceManager> resource_manager;
std::shared_ptr<HidFirmwareSettings> firmware_settings;
+2 -297
View File
@@ -1,19 +1,9 @@
// 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 <algorithm>
#include <array>
#include <memory>
#include <vector>
#include "core/core.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/romfs_factory.h"
#include "core/hle/api_version.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/ncm/ncm.h"
#include "core/hle/service/server_manager.h"
@@ -98,268 +88,6 @@ public:
}
};
class IContentStorage final : public ServiceFramework<IContentStorage> {
public:
explicit IContentStorage(Core::System& system_, FileSys::StorageId id)
: ServiceFramework{system_, "IContentStorage"}, storage{id} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IContentStorage::GeneratePlaceHolderId, "GeneratePlaceHolderId"},
{1, &IContentStorage::CreatePlaceHolder, "CreatePlaceHolder"},
{2, &IContentStorage::DeletePlaceHolder, "DeletePlaceHolder"},
{4, &IContentStorage::WritePlaceHolder, "WritePlaceHolder"},
{5, &IContentStorage::Register, "Register"},
{6, &IContentStorage::Delete, "Delete"},
};
// clang-format on
RegisterHandlers(functions);
}
private:
void GeneratePlaceHolderId(HLERequestContext& ctx) {
LOG_DEBUG(Service_NCM, "called");
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
rb.PushRaw(FileSys::PlaceholderCache::Generate());
}
void CreatePlaceHolder(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
[[maybe_unused]] FileSys::NcaID content_id{};
FileSys::NcaID placeholder_id{};
if constexpr (HLE::ApiVersion::HOS_VERSION_MAJOR >= 16) {
placeholder_id = rp.PopRaw<FileSys::NcaID>();
content_id = rp.PopRaw<FileSys::NcaID>();
} else {
content_id = rp.PopRaw<FileSys::NcaID>();
placeholder_id = rp.PopRaw<FileSys::NcaID>();
}
const auto size = rp.Pop<s64>();
auto* const placeholder_cache =
system.GetFileSystemController().GetPlaceholderCacheForStorage(storage);
const bool succeeded =
placeholder_cache != nullptr && size >= 0 &&
(placeholder_cache->Exists(placeholder_id) ||
placeholder_cache->Create(placeholder_id, static_cast<u64>(size)));
if (succeeded) {
LOG_DEBUG(Service_NCM, "called, storage_id={}, size={}", static_cast<u32>(storage),
size);
} else {
LOG_WARNING(Service_NCM, "failed, storage_id={}, size={}", static_cast<u32>(storage),
size);
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(succeeded ? ResultSuccess : ResultUnknown);
}
void DeletePlaceHolder(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto placeholder_id = rp.PopRaw<FileSys::NcaID>();
auto* const placeholder_cache =
system.GetFileSystemController().GetPlaceholderCacheForStorage(storage);
const bool succeeded =
placeholder_cache != nullptr &&
(!placeholder_cache->Exists(placeholder_id) || placeholder_cache->Delete(placeholder_id));
if (succeeded) {
LOG_DEBUG(Service_NCM, "called, storage_id={}", static_cast<u32>(storage));
} else {
LOG_WARNING(Service_NCM, "failed, storage_id={}", static_cast<u32>(storage));
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(succeeded ? ResultSuccess : ResultUnknown);
}
void WritePlaceHolder(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto placeholder_id = rp.PopRaw<FileSys::NcaID>();
const auto offset = rp.Pop<u64>();
const auto data = ctx.ReadBuffer();
auto* const placeholder_cache =
system.GetFileSystemController().GetPlaceholderCacheForStorage(storage);
const std::vector<u8> write_data{data.begin(), data.end()};
const bool succeeded =
placeholder_cache != nullptr &&
placeholder_cache->Write(placeholder_id, offset, write_data);
if (succeeded) {
LOG_DEBUG(Service_NCM, "called, storage_id={}, offset={}, size={}",
static_cast<u32>(storage), offset, data.size());
} else {
LOG_WARNING(Service_NCM, "failed, storage_id={}, offset={}, size={}",
static_cast<u32>(storage), offset, data.size());
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(succeeded ? ResultSuccess : ResultUnknown);
}
void Register(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
FileSys::NcaID content_id{};
FileSys::NcaID placeholder_id{};
if constexpr (HLE::ApiVersion::HOS_VERSION_MAJOR >= 16) {
placeholder_id = rp.PopRaw<FileSys::NcaID>();
content_id = rp.PopRaw<FileSys::NcaID>();
} else {
content_id = rp.PopRaw<FileSys::NcaID>();
placeholder_id = rp.PopRaw<FileSys::NcaID>();
}
auto& fsc = system.GetFileSystemController();
auto* const placeholder_cache = fsc.GetPlaceholderCacheForStorage(storage);
auto* const registered_cache = fsc.GetRegisteredCacheForStorage(storage);
const bool succeeded =
placeholder_cache != nullptr && registered_cache != nullptr &&
placeholder_cache->Register(registered_cache, placeholder_id, content_id);
if (succeeded) {
LOG_DEBUG(Service_NCM, "called, storage_id={}", static_cast<u32>(storage));
} else {
LOG_WARNING(Service_NCM, "failed, storage_id={}", static_cast<u32>(storage));
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(succeeded ? ResultSuccess : ResultUnknown);
}
void Delete(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto content_id = rp.PopRaw<FileSys::NcaID>();
auto* const registered_cache =
system.GetFileSystemController().GetRegisteredCacheForStorage(storage);
const bool succeeded = registered_cache != nullptr && registered_cache->Delete(content_id);
if (succeeded) {
registered_cache->Refresh();
}
if (succeeded) {
LOG_DEBUG(Service_NCM, "called, storage_id={}", static_cast<u32>(storage));
} else {
LOG_WARNING(Service_NCM, "failed, storage_id={}", static_cast<u32>(storage));
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(succeeded ? ResultSuccess : ResultUnknown);
}
FileSys::StorageId storage;
};
class IContentMetaDatabase final : public ServiceFramework<IContentMetaDatabase> {
public:
explicit IContentMetaDatabase(Core::System& system_, FileSys::StorageId id)
: ServiceFramework{system_, "IContentMetaDatabase"}, storage{id} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IContentMetaDatabase::Set, "Set"},
{2, &IContentMetaDatabase::Remove, "Remove"},
{8, &IContentMetaDatabase::Has, "Has"},
{15, &IContentMetaDatabase::Commit, "Commit"},
};
// clang-format on
RegisterHandlers(functions);
}
private:
struct ContentMetaKey {
u64 id;
u32 version;
FileSys::TitleType type;
u8 install_type;
std::array<u8, 2> padding;
};
static_assert(sizeof(ContentMetaKey) == 0x10);
void Set(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto key = rp.PopRaw<ContentMetaKey>();
const auto entry_matches = [&key](const ContentMetaKey& entry) {
return entry.id == key.id && entry.version == key.version && entry.type == key.type &&
entry.install_type == key.install_type;
};
if (std::find_if(entries.begin(), entries.end(), entry_matches) == entries.end()) {
entries.push_back(key);
}
LOG_DEBUG(Service_NCM,
"called, storage_id={}, title_id={:016X}, version={}, type={}, size={}",
static_cast<u32>(storage), key.id, key.version, static_cast<u8>(key.type),
ctx.GetReadBufferSize());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void Remove(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto key = rp.PopRaw<ContentMetaKey>();
std::erase_if(entries, [&key](const ContentMetaKey& entry) {
return entry.id == key.id && entry.version == key.version && entry.type == key.type &&
entry.install_type == key.install_type;
});
LOG_DEBUG(Service_NCM, "called, storage_id={}, title_id={:016X}, version={}, type={}",
static_cast<u32>(storage), key.id, key.version, static_cast<u8>(key.type));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void Has(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto key = rp.PopRaw<ContentMetaKey>();
const bool has_pending =
std::find_if(entries.begin(), entries.end(), [&key](const ContentMetaKey& entry) {
return entry.id == key.id && entry.version == key.version &&
entry.type == key.type && entry.install_type == key.install_type;
}) != entries.end();
auto* const registered_cache =
system.GetFileSystemController().GetRegisteredCacheForStorage(storage);
const bool has_registered =
registered_cache != nullptr &&
registered_cache->HasEntry(key.id, FileSys::ContentRecordType::Meta);
LOG_DEBUG(Service_NCM, "called, storage_id={}, title_id={:016X}, version={}, type={}, has={}",
static_cast<u32>(storage), key.id, key.version, static_cast<u8>(key.type),
has_pending || has_registered);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(has_pending || has_registered);
}
void Commit(HLERequestContext& ctx) {
auto* const registered_cache =
system.GetFileSystemController().GetRegisteredCacheForStorage(storage);
if (registered_cache != nullptr) {
registered_cache->Refresh();
}
LOG_DEBUG(Service_NCM, "called, storage_id={}", static_cast<u32>(storage));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
FileSys::StorageId storage;
std::vector<ContentMetaKey> entries;
};
class LR final : public ServiceFramework<LR> {
public:
explicit LR(Core::System& system_) : ServiceFramework{system_, "lr"} {
@@ -385,8 +113,8 @@ public:
{1, nullptr, "CreateContentMetaDatabase"},
{2, nullptr, "VerifyContentStorage"},
{3, nullptr, "VerifyContentMetaDatabase"},
{4, &NCM::OpenContentStorage, "OpenContentStorage"},
{5, &NCM::OpenContentMetaDatabase, "OpenContentMetaDatabase"},
{4, nullptr, "OpenContentStorage"},
{5, nullptr, "OpenContentMetaDatabase"},
{6, nullptr, "CloseContentStorageForcibly"},
{7, nullptr, "CloseContentMetaDatabaseForcibly"},
{8, nullptr, "CleanupContentMetaDatabase"},
@@ -402,29 +130,6 @@ public:
RegisterHandlers(functions);
}
private:
void OpenContentStorage(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto storage_id = rp.PopEnum<FileSys::StorageId>();
LOG_DEBUG(Service_NCM, "called, storage_id={}", static_cast<u32>(storage_id));
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IContentStorage>(ctx, system, storage_id);
}
void OpenContentMetaDatabase(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto storage_id = rp.PopEnum<FileSys::StorageId>();
LOG_DEBUG(Service_NCM, "called, storage_id={}", static_cast<u32>(storage_id));
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IContentMetaDatabase>(ctx, system, storage_id);
}
};
void LoopProcess(Core::System& system) {
@@ -9,7 +9,6 @@
#include "core/file_sys/registered_cache.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/ns/application_manager_interface.h"
#include "core/file_sys/content_archive.h"
@@ -20,7 +19,6 @@
#include "core/launch_timestamp_cache.h"
#include <algorithm>
#include <cstring>
#include <vector>
namespace Service::NS {
@@ -38,14 +36,14 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
{1, nullptr, "GenerateApplicationRecordCount"},
{2, D<&IApplicationManagerInterface::GetApplicationRecordUpdateSystemEvent>, "GetApplicationRecordUpdateSystemEvent"},
{3, nullptr, "GetApplicationViewDeprecated"},
{4, D<&IApplicationManagerInterface::DeleteApplicationEntity>, "DeleteApplicationEntity"},
{5, D<&IApplicationManagerInterface::DeleteApplicationCompletely>, "DeleteApplicationCompletely"},
{4, nullptr, "DeleteApplicationEntity"},
{5, nullptr, "DeleteApplicationCompletely"},
{6, nullptr, "IsAnyApplicationEntityRedundant"},
{7, nullptr, "DeleteRedundantApplicationEntity"},
{8, nullptr, "IsApplicationEntityMovable"},
{9, nullptr, "MoveApplicationEntity"},
{11, nullptr, "CalculateApplicationOccupiedSize"},
{16, &IApplicationManagerInterface::PushApplicationRecord, "PushApplicationRecord"},
{16, nullptr, "PushApplicationRecord"},
{17, nullptr, "ListApplicationRecordContentMeta"},
{19, nullptr, "LaunchApplicationOld"},
{21, nullptr, "GetApplicationContentPath"},
@@ -645,27 +643,6 @@ Result IApplicationManagerInterface::IsAnyApplicationEntityInstalled(
R_SUCCEED();
}
Result IApplicationManagerInterface::DeleteApplicationEntity(u64 application_id) {
LOG_DEBUG(Service_NS, "called, application_id={:016X}", application_id);
auto& fsc = system.GetFileSystemController();
if (auto* const user_cache = fsc.GetUserNANDContents(); user_cache != nullptr) {
user_cache->RemoveExistingEntry(application_id);
user_cache->Refresh();
}
if (auto* const sdmc_cache = fsc.GetSDMCContents(); sdmc_cache != nullptr) {
sdmc_cache->RemoveExistingEntry(application_id);
sdmc_cache->Refresh();
}
record_update_system_event.Signal(system.Kernel());
R_SUCCEED();
}
Result IApplicationManagerInterface::DeleteApplicationCompletely(u64 application_id) {
R_RETURN(DeleteApplicationEntity(application_id));
}
Result IApplicationManagerInterface::GetApplicationViewDeprecated(
OutArray<ApplicationViewV19, BufferAttr_HipcMapAlias> out_application_views,
InArray<u64, BufferAttr_HipcMapAlias> application_ids) {
@@ -866,29 +843,6 @@ Result IApplicationManagerInterface::Unknown4053() {
R_SUCCEED();
}
void IApplicationManagerInterface::PushApplicationRecord(HLERequestContext& ctx) {
const auto record = ctx.ReadBuffer();
u64 application_id{};
if (record.size() >= sizeof(application_id)) {
std::memcpy(&application_id, record.data(), sizeof(application_id));
}
LOG_DEBUG(Service_NS, "called, application_id={:016X}, size={}", application_id, record.size());
auto& fsc = system.GetFileSystemController();
if (auto* const user_cache = fsc.GetUserNANDContents(); user_cache != nullptr) {
user_cache->Refresh();
}
if (auto* const sdmc_cache = fsc.GetSDMCContents(); sdmc_cache != nullptr) {
sdmc_cache->Refresh();
}
record_update_system_event.Signal(system.Kernel());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IApplicationManagerInterface::ListApplicationTitle(HLERequestContext& ctx) {
LOG_DEBUG(Service_NS, "called");
IReadOnlyApplicationControlDataInterface(system).ListApplicationTitle(ctx);
@@ -56,8 +56,6 @@ public:
Result ResumeAll();
Result IsQualificationTransitionSupportedByProcessId(Out<bool> out_is_supported,
u64 process_id);
Result DeleteApplicationEntity(u64 application_id);
Result DeleteApplicationCompletely(u64 application_id);
Result GetStorageSize(Out<s64> out_total_space_size, Out<s64> out_free_space_size,
FileSys::StorageId storage_id);
Result TouchApplication(u64 application_id);
@@ -76,7 +74,6 @@ public:
Result RequestDownloadApplicationControlDataInBackground(u64 control_source,
u64 application_id);
void PushApplicationRecord(HLERequestContext& ctx);
void ListApplicationTitle(HLERequestContext& ctx);
private:
+2 -5
View File
@@ -1,6 +1,3 @@
// 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
@@ -31,9 +28,9 @@ SPL_MIG::SPL_MIG(Core::System& system_, std::shared_ptr<Module> module_)
static const FunctionInfo functions[] = {
{0, &SPL::GetConfig, "GetConfig"},
{1, &SPL::ModularExponentiate, "ModularExponentiate"},
{2, &SPL::GenerateAesKek, "GenerateAesKek"},
{2, nullptr, "GenerateAesKek"},
{3, nullptr, "LoadAesKey"},
{4, &SPL::GenerateAesKey, "GenerateAesKey"},
{4, nullptr, "GenerateAesKey"},
{5, &SPL::SetConfig, "SetConfig"},
{7, &SPL::GenerateRandomBytes, "GenerateRandomBytes"},
{11, &SPL::IsDevelopment, "IsDevelopment"},
-30
View File
@@ -59,36 +59,6 @@ void Module::Interface::ModularExponentiate(HLERequestContext& ctx) {
rb.Push(ResultSecureMonitorNotImplemented);
}
void Module::Interface::GenerateAesKek(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
[[maybe_unused]] const auto key_source = rp.PopRaw<KeySource>();
const auto generation = rp.Pop<u32>();
const auto option = rp.Pop<u32>();
LOG_WARNING(Service_SPL, "(STUBBED) called, generation={:#x}, option={:#x}", generation,
option);
AccessKey access_key{};
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
rb.PushRaw(access_key);
}
void Module::Interface::GenerateAesKey(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
[[maybe_unused]] const auto access_key = rp.PopRaw<AccessKey>();
[[maybe_unused]] const auto key_source = rp.PopRaw<KeySource>();
LOG_WARNING(Service_SPL, "(STUBBED) called");
AesKey aes_key{};
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
rb.PushRaw(aes_key);
}
void Module::Interface::SetConfig(HLERequestContext& ctx) {
UNIMPLEMENTED_MSG("SetConfig is not implemented!");
-5
View File
@@ -1,6 +1,3 @@
// 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
@@ -28,8 +25,6 @@ public:
// General
void GetConfig(HLERequestContext& ctx);
void ModularExponentiate(HLERequestContext& ctx);
void GenerateAesKek(HLERequestContext& ctx);
void GenerateAesKey(HLERequestContext& ctx);
void SetConfig(HLERequestContext& ctx);
void GenerateRandomBytes(HLERequestContext& ctx);
void IsDevelopment(HLERequestContext& ctx);
+11 -11
View File
@@ -1662,17 +1662,17 @@ bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const {
bool EmulatedController::IsControllerSupported(bool use_temporary_value) const {
const auto type = is_configuring.load() && use_temporary_value ? tmp_npad_type.load() : npad_type.load();
switch (type) {
case NpadStyleIndex::Fullkey: return bool(supported_style_tag.fullkey);
case NpadStyleIndex::Handheld: return bool(supported_style_tag.handheld);
case NpadStyleIndex::JoyconDual: return bool(supported_style_tag.joycon_dual);
case NpadStyleIndex::JoyconLeft: return bool(supported_style_tag.joycon_left);
case NpadStyleIndex::JoyconRight: return bool(supported_style_tag.joycon_right);
case NpadStyleIndex::GameCube: return bool(supported_style_tag.gamecube);
case NpadStyleIndex::Pokeball: return bool(supported_style_tag.palma);
case NpadStyleIndex::NES: return bool(supported_style_tag.lark);
case NpadStyleIndex::SNES: return bool(supported_style_tag.lucia);
case NpadStyleIndex::N64: return bool(supported_style_tag.lagoon);
case NpadStyleIndex::SegaGenesis: return bool(supported_style_tag.lager);
case NpadStyleIndex::Fullkey: return supported_style_tag.fullkey;
case NpadStyleIndex::Handheld: return supported_style_tag.handheld;
case NpadStyleIndex::JoyconDual: return supported_style_tag.joycon_dual;
case NpadStyleIndex::JoyconLeft: return supported_style_tag.joycon_left;
case NpadStyleIndex::JoyconRight: return supported_style_tag.joycon_right;
case NpadStyleIndex::GameCube: return supported_style_tag.gamecube;
case NpadStyleIndex::Pokeball: return supported_style_tag.palma;
case NpadStyleIndex::NES: return supported_style_tag.lark;
case NpadStyleIndex::SNES: return supported_style_tag.lucia;
case NpadStyleIndex::N64: return supported_style_tag.lagoon;
case NpadStyleIndex::SegaGenesis: return supported_style_tag.lager;
default: return false;
}
}
@@ -66,7 +66,7 @@ Result NpadAbstractSixAxisHandler::UpdateSixAxisState() {
continue;
}
auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)];
UpdateSixaxisInternalState(npad_entry, data->aruid, bool(data->flag.enable_six_axis_sensor));
UpdateSixaxisInternalState(npad_entry, data->aruid, data->flag.enable_six_axis_sensor);
}
return ResultSuccess;
}
@@ -78,7 +78,7 @@ Result NpadAbstractSixAxisHandler::UpdateSixAxisState(u64 aruid) {
return ResultSuccess;
}
auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)];
UpdateSixaxisInternalState(npad_entry, data->aruid, bool(data->flag.enable_six_axis_sensor));
UpdateSixaxisInternalState(npad_entry, data->aruid, data->flag.enable_six_axis_sensor);
return ResultSuccess;
}
@@ -89,7 +89,7 @@ Result NpadAbstractSixAxisHandler::UpdateSixAxisState2(u64 aruid) {
return ResultSuccess;
}
auto& npad_internal_state = aruid_data->shared_memory_format->npad.npad_entry[npad_index];
UpdateSixaxisInternalState(npad_internal_state, aruid, bool(aruid_data->flag.enable_six_axis_sensor));
UpdateSixaxisInternalState(npad_internal_state, aruid, aruid_data->flag.enable_six_axis_sensor);
return ResultSuccess;
}
+21 -17
View File
@@ -24,7 +24,7 @@ void NPadData::SetNpadAnalogStickUseCenterClamp(bool is_enabled) {
}
bool NPadData::GetNpadAnalogStickUseCenterClamp() const {
return bool(status.use_center_clamp);
return status.use_center_clamp;
}
void NPadData::SetNpadSystemExtStateEnabled(bool is_enabled) {
@@ -32,7 +32,7 @@ void NPadData::SetNpadSystemExtStateEnabled(bool is_enabled) {
}
bool NPadData::GetNpadSystemExtState() const {
return bool(status.system_ext_state);
return status.system_ext_state;
}
Result NPadData::SetSupportedNpadIdType(std::span<const Core::HID::NpadIdType> list) {
@@ -42,14 +42,18 @@ Result NPadData::SetSupportedNpadIdType(std::span<const Core::HID::NpadIdType> l
}
supported_npad_id_types_count = list.size();
std::memcpy(supported_npad_id_types.data(), list.data(), list.size() * sizeof(Core::HID::NpadIdType));
memcpy(supported_npad_id_types.data(), list.data(),
list.size() * sizeof(Core::HID::NpadIdType));
return ResultSuccess;
}
std::size_t NPadData::GetSupportedNpadIdType(std::span<Core::HID::NpadIdType> out_list) const {
std::size_t out_size = (std::min)(supported_npad_id_types_count, out_list.size());
std::memcpy(out_list.data(), supported_npad_id_types.data(), out_size * sizeof(Core::HID::NpadIdType));
memcpy(out_list.data(), supported_npad_id_types.data(),
out_size * sizeof(Core::HID::NpadIdType));
return out_size;
}
@@ -150,27 +154,27 @@ bool NPadData::IsNpadStyleIndexSupported(Core::HID::NpadStyleIndex style_index)
Core::HID::NpadStyleTag style = {supported_npad_style_set};
switch (style_index) {
case Core::HID::NpadStyleIndex::Fullkey:
return bool(style.fullkey);
return style.fullkey;
case Core::HID::NpadStyleIndex::Handheld:
return bool(style.handheld);
return style.handheld;
case Core::HID::NpadStyleIndex::JoyconDual:
return bool(style.joycon_dual);
return style.joycon_dual;
case Core::HID::NpadStyleIndex::JoyconLeft:
return bool(style.joycon_left);
return style.joycon_left;
case Core::HID::NpadStyleIndex::JoyconRight:
return bool(style.joycon_right);
return style.joycon_right;
case Core::HID::NpadStyleIndex::GameCube:
return bool(style.gamecube);
return style.gamecube;
case Core::HID::NpadStyleIndex::Pokeball:
return bool(style.palma);
return style.palma;
case Core::HID::NpadStyleIndex::NES:
return bool(style.lark);
return style.lark;
case Core::HID::NpadStyleIndex::SNES:
return bool(style.lucia);
return style.lucia;
case Core::HID::NpadStyleIndex::N64:
return bool(style.lagoon);
return style.lagoon;
case Core::HID::NpadStyleIndex::SegaGenesis:
return bool(style.lager);
return style.lager;
default:
return false;
}
@@ -181,7 +185,7 @@ void NPadData::SetLrAssignmentMode(bool is_enabled) {
}
bool NPadData::GetLrAssignmentMode() const {
return bool(status.lr_assignment_mode);
return status.lr_assignment_mode;
}
void NPadData::SetAssigningSingleOnSlSrPress(bool is_enabled) {
@@ -189,7 +193,7 @@ void NPadData::SetAssigningSingleOnSlSrPress(bool is_enabled) {
}
bool NPadData::GetAssigningSingleOnSlSrPress() const {
return bool(status.assigning_single_on_sl_sr_press);
return status.assigning_single_on_sl_sr_press;
}
void NPadData::SetHomeProtectionEnabled(bool is_enabled, Core::HID::NpadIdType npad_id) {
@@ -548,7 +548,7 @@ void TouchResource::OnTouchUpdate(s64 timestamp) {
}
auto& touch_shared = applet_data->shared_memory_format->touch_screen;
StorePreviousTouchState(previous_touch_state, data.finger_map, current_touch_state, bool(applet_data->flag.enable_touchscreen));
StorePreviousTouchState(previous_touch_state, data.finger_map, current_touch_state, applet_data->flag.enable_touchscreen);
touch_shared.touch_screen_lifo.WriteNextEntry(current_touch_state);
}
}
+9 -13
View File
@@ -193,6 +193,9 @@ std::unique_ptr<TranslationMap> 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"
@@ -220,14 +223,14 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent) {
tr("Controls the quality of texture rendering at oblique angles.\nSafe to set at 16x on "
"most GPUs."));
INSERT(Settings, gpu_accuracy, tr("GPU Mode:"),
tr("Controls the GPU emulation mode.\nMost games render fine with Fast, but Accurate is still "
tr("Controls the GPU emulation mode.\nMost games render fine with Fast or Balanced "
"modes, but Accurate is still "
"required for some.\nParticles tend to only render correctly with Accurate mode."));
INSERT(Settings, dma_accuracy, tr("DMA Accuracy:"),
tr("Controls the DMA read mode.\nUnsafe is faster, while Safe is more stable and can fix issues in some games.\nDefault follows the GPU Accuracy setting."));
INSERT(Settings, gpu_fence_behavior, tr("GPU Fence Behavior:"),
tr("Controls the GPU fence synchronization behavior.\nImmediate is the fastest option, but can introduce some issues.\nBalanced offers better compatibility and may fix issues in some games.\nAccurate further improves compatibility at the cost of some performance.\nStrict is the slowest option, but can fix issues that require stricter synchronization.\nDefault follows the GPU Accuracy setting."));
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 data by reading it back before uploading.\nSome games require this to render certain effects properly."));
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"),
@@ -427,6 +430,7 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QObject* parent) {
translations->insert({Settings::EnumMetadata<Settings::GpuAccuracy>::Index(),
{
PAIR(GpuAccuracy, Low, tr("Fast")),
PAIR(GpuAccuracy, Medium, tr("Balanced")),
PAIR(GpuAccuracy, High, tr("Accurate")),
}});
translations->insert({Settings::EnumMetadata<Settings::DmaAccuracy>::Index(),
@@ -435,14 +439,6 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QObject* parent) {
PAIR(DmaAccuracy, Unsafe, tr("Unsafe (fast)")),
PAIR(DmaAccuracy, Safe, tr("Safe (stable)")),
}});
translations->insert({Settings::EnumMetadata<Settings::GpuFenceBehavior>::Index(),
{
PAIR(GpuFenceBehavior, Default, tr("Default")),
PAIR(GpuFenceBehavior, Immediate, tr("Immediate")),
PAIR(GpuFenceBehavior, Balanced, tr("Balanced")),
PAIR(GpuFenceBehavior, Accurate, tr("Accurate")),
PAIR(GpuFenceBehavior, Strict, tr("Strict")),
}});
translations->insert(
{Settings::EnumMetadata<Settings::CpuAccuracy>::Index(),
{
@@ -64,6 +64,7 @@ static const std::map<Settings::ConsoleMode, QString> use_docked_mode_texts_map
static const std::map<Settings::GpuAccuracy, QString> gpu_accuracy_texts_map = {
{Settings::GpuAccuracy::Low, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Fast"))},
{Settings::GpuAccuracy::Medium, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Balanced"))},
{Settings::GpuAccuracy::High, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Accurate"))},
};

Some files were not shown because too many files have changed in this diff Show More