diff options
353 files changed, 11010 insertions, 3522 deletions
diff --git a/.codespellrc b/.codespellrc index 29bcf35026..01ddd23624 100644 --- a/.codespellrc +++ b/.codespellrc @@ -2,5 +2,5 @@ ; SPDX-License-Identifier: GPL-2.0-or-later [codespell] -skip = ./.git,./build,./dist,./Doxyfile,./externals,./LICENSES +skip = ./.git,./build,./dist,./Doxyfile,./externals,./LICENSES,./src/android/app/src/main/res ignore-words-list = aci,allright,ba,deques,froms,hda,inout,lod,masia,nam,nax,nd,optin,pullrequests,pullrequest,te,transfered,unstall,uscaled,zink diff --git a/.gitignore b/.gitignore index a5f7248c70..fbadb208be 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,8 @@ CMakeSettings.json # OSX global filetypes # Created by Finder or Spotlight in directories for various OS functionality (indexing, etc) .DS_Store +.DS_Store? +._* .AppleDouble .LSOverride .Spotlight-V100 diff --git a/.gitmodules b/.gitmodules index 95eae81097..89f2ad924f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -52,3 +52,6 @@ [submodule "libadrenotools"] path = externals/libadrenotools url = https://github.com/bylaws/libadrenotools +[submodule "tzdb_to_nx"] + path = externals/nx_tzdb/tzdb_to_nx + url = https://github.com/lat9nq/tzdb_to_nx.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 3d03bbf94e..f5ef0ef500 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -59,6 +59,8 @@ option(YUZU_CHECK_SUBMODULES "Check if submodules are present" ON) option(YUZU_ENABLE_LTO "Enable link-time optimization" OFF) +option(YUZU_DOWNLOAD_TIME_ZONE_DATA "Always download time zone binaries" OFF) + CMAKE_DEPENDENT_OPTION(YUZU_USE_FASTER_LD "Check if a faster linker is available" ON "NOT WIN32" OFF) # On Android, fetch and compile libcxx before doing anything else @@ -487,7 +489,7 @@ if (ENABLE_SDL2) if (YUZU_USE_BUNDLED_SDL2) # Detect toolchain and platform if ((MSVC_VERSION GREATER_EQUAL 1920 AND MSVC_VERSION LESS 1940) AND ARCHITECTURE_x86_64) - set(SDL2_VER "SDL2-2.0.18") + set(SDL2_VER "SDL2-2.28.0") else() message(FATAL_ERROR "No bundled SDL2 binaries for your toolchain. Disable YUZU_USE_BUNDLED_SDL2 and provide your own.") endif() @@ -507,7 +509,7 @@ if (ENABLE_SDL2) elseif (YUZU_USE_EXTERNAL_SDL2) message(STATUS "Using SDL2 from externals.") else() - find_package(SDL2 2.0.18 REQUIRED) + find_package(SDL2 2.26.4 REQUIRED) endif() endif() diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 500eb21e39..7cce27d51f 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -63,8 +63,9 @@ if (YUZU_USE_EXTERNAL_SDL2) # Yuzu itself needs: Atomic Audio Events Joystick Haptic Sensor Threads Timers # Since 2.0.18 Atomic+Threads required for HIDAPI/libusb (see https://github.com/libsdl-org/SDL/issues/5095) # Yuzu-cmd also needs: Video (depends on Loadso/Dlopen) + # CPUinfo also required for SDL Audio, at least until 2.28.0 (see https://github.com/libsdl-org/SDL/issues/7809) set(SDL_UNUSED_SUBSYSTEMS - CPUinfo File Filesystem + File Filesystem Locale Power Render) foreach(_SUB ${SDL_UNUSED_SUBSYSTEMS}) string(TOUPPER ${_SUB} _OPT) @@ -139,6 +140,9 @@ if (YUZU_USE_EXTERNAL_VULKAN_HEADERS) add_subdirectory(Vulkan-Headers) endif() +# TZDB (Time Zone Database) +add_subdirectory(nx_tzdb) + if (NOT TARGET LLVM::Demangle) add_library(demangle demangle/ItaniumDemangle.cpp) target_include_directories(demangle PUBLIC ./demangle) diff --git a/externals/SDL b/externals/SDL -Subproject f17058b562c8a1090c0c996b42982721ace9090 +Subproject 491fba1d06a4810645092b2559b9cc94abeb23b diff --git a/externals/nx_tzdb/CMakeLists.txt b/externals/nx_tzdb/CMakeLists.txt new file mode 100644 index 0000000000..5937862507 --- /dev/null +++ b/externals/nx_tzdb/CMakeLists.txt @@ -0,0 +1,101 @@ +# SPDX-FileCopyrightText: 2023 yuzu Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later + +set(NX_TZDB_INCLUDE_DIR "${CMAKE_CURRENT_BINARY_DIR}/include") + +add_library(nx_tzdb INTERFACE) + +find_program(GIT git) +find_program(GNU_MAKE make) +find_program(DATE_PROG date) + +set(CAN_BUILD_NX_TZDB true) + +if (NOT GIT) + set(CAN_BUILD_NX_TZDB false) +endif() +if (NOT GNU_MAKE) + set(CAN_BUILD_NX_TZDB false) +endif() +if (NOT DATE_PROG) + set(CAN_BUILD_NX_TZDB false) +endif() +if (CMAKE_SYSTEM_NAME STREQUAL "Windows" OR ANDROID) + # tzdb_to_nx currently requires a posix-compliant host + # MinGW and Android are handled here due to the executable format being different from the host system + # TODO (lat9nq): cross-compiling support + set(CAN_BUILD_NX_TZDB false) +endif() + +set(NX_TZDB_VERSION "220816") +set(NX_TZDB_ARCHIVE "${CMAKE_CURRENT_BINARY_DIR}/${NX_TZDB_VERSION}.zip") + +set(NX_TZDB_ROMFS_DIR "${CMAKE_CURRENT_BINARY_DIR}/nx_tzdb") + +if ((NOT CAN_BUILD_NX_TZDB OR YUZU_DOWNLOAD_TIME_ZONE_DATA) AND NOT EXISTS ${NX_TZDB_ARCHIVE}) + set(NX_TZDB_DOWNLOAD_URL "https://github.com/lat9nq/tzdb_to_nx/releases/download/${NX_TZDB_VERSION}/${NX_TZDB_VERSION}.zip") + + message(STATUS "Downloading time zone data from ${NX_TZDB_DOWNLOAD_URL}...") + file(DOWNLOAD ${NX_TZDB_DOWNLOAD_URL} ${NX_TZDB_ARCHIVE} + STATUS NX_TZDB_DOWNLOAD_STATUS) + list(GET NX_TZDB_DOWNLOAD_STATUS 0 NX_TZDB_DOWNLOAD_STATUS_CODE) + if (NOT NX_TZDB_DOWNLOAD_STATUS_CODE EQUAL 0) + message(FATAL_ERROR "Time zone data download failed (status code ${NX_TZDB_DOWNLOAD_STATUS_CODE})") + endif() + + file(ARCHIVE_EXTRACT + INPUT + ${NX_TZDB_ARCHIVE} + DESTINATION + ${NX_TZDB_ROMFS_DIR}) +elseif (CAN_BUILD_NX_TZDB AND NOT YUZU_DOWNLOAD_TIME_ZONE_DATA) + add_subdirectory(tzdb_to_nx) + add_dependencies(nx_tzdb x80e) + + set(NX_TZDB_ROMFS_DIR "${NX_TZDB_DIR}") +endif() + +target_include_directories(nx_tzdb + INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include + INTERFACE ${NX_TZDB_INCLUDE_DIR}) + +function(CreateHeader ZONE_PATH HEADER_NAME) + set(HEADER_PATH "${NX_TZDB_INCLUDE_DIR}/nx_tzdb/${HEADER_NAME}.h") + add_custom_command( + OUTPUT + ${NX_TZDB_INCLUDE_DIR}/nx_tzdb/${HEADER_NAME}.h + COMMAND + ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_SOURCE_DIR}/NxTzdbCreateHeader.cmake + ${ZONE_PATH} + ${HEADER_NAME} + ${NX_TZDB_INCLUDE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR} + DEPENDS + tzdb_template.h.in + NxTzdbCreateHeader.cmake) + + target_sources(nx_tzdb PRIVATE ${HEADER_PATH}) +endfunction() + +CreateHeader(${NX_TZDB_ROMFS_DIR} base) +CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo zoneinfo) +CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Africa africa) +CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/America america) +CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/America/Argentina america_argentina) +CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/America/Indiana america_indiana) +CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/America/Kentucky america_kentucky) +CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/America/North_Dakota america_north_dakota) +CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Antarctica antarctica) +CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Arctic arctic) +CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Asia asia) +CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Atlantic atlantic) +CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Australia australia) +CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Brazil brazil) +CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Canada canada) +CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Chile chile) +CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Etc etc) +CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Europe europe) +CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Indian indian) +CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Mexico mexico) +CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Pacific pacific) +CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/US us) diff --git a/externals/nx_tzdb/ListFilesInDirectory.cmake b/externals/nx_tzdb/ListFilesInDirectory.cmake new file mode 100644 index 0000000000..35a9e726ab --- /dev/null +++ b/externals/nx_tzdb/ListFilesInDirectory.cmake @@ -0,0 +1,8 @@ +# SPDX-FileCopyrightText: 2023 yuzu Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later + +# CMake does not have a way to list the files in a specific directory, +# so we need this script to do that for us in a platform-agnostic fashion + +file(GLOB FILE_LIST LIST_DIRECTORIES false RELATIVE ${CMAKE_SOURCE_DIR} "*") +execute_process(COMMAND ${CMAKE_COMMAND} -E echo "${FILE_LIST};") diff --git a/externals/nx_tzdb/NxTzdbCreateHeader.cmake b/externals/nx_tzdb/NxTzdbCreateHeader.cmake new file mode 100644 index 0000000000..8c29e1167b --- /dev/null +++ b/externals/nx_tzdb/NxTzdbCreateHeader.cmake @@ -0,0 +1,46 @@ +# SPDX-FileCopyrightText: 2023 yuzu Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later + +set(ZONE_PATH ${CMAKE_ARGV3}) +set(HEADER_NAME ${CMAKE_ARGV4}) +set(NX_TZDB_INCLUDE_DIR ${CMAKE_ARGV5}) +set(NX_TZDB_SOURCE_DIR ${CMAKE_ARGV6}) + +execute_process( + COMMAND ${CMAKE_COMMAND} -P ${NX_TZDB_SOURCE_DIR}/ListFilesInDirectory.cmake + WORKING_DIRECTORY ${ZONE_PATH} + OUTPUT_VARIABLE FILE_LIST) + +set(DIRECTORY_NAME ${HEADER_NAME}) + +set(FILE_DATA "") +foreach(ZONE_FILE ${FILE_LIST}) + if (ZONE_FILE STREQUAL "\n") + continue() + endif() + + string(APPEND FILE_DATA "{\"${ZONE_FILE}\",\n{") + + file(READ ${ZONE_PATH}/${ZONE_FILE} ZONE_DATA HEX) + string(LENGTH "${ZONE_DATA}" ZONE_DATA_LEN) + foreach(I RANGE 0 ${ZONE_DATA_LEN} 2) + math(EXPR BREAK_LINE "(${I} + 2) % 38") + + string(SUBSTRING "${ZONE_DATA}" "${I}" 2 HEX_DATA) + if (NOT HEX_DATA) + break() + endif() + + string(APPEND FILE_DATA "0x${HEX_DATA},") + if (BREAK_LINE EQUAL 0) + string(APPEND FILE_DATA "\n") + else() + string(APPEND FILE_DATA " ") + endif() + endforeach() + + string(APPEND FILE_DATA "}},\n") +endforeach() + +file(READ ${NX_TZDB_SOURCE_DIR}/tzdb_template.h.in NX_TZDB_TEMPLATE_H_IN) +file(CONFIGURE OUTPUT ${NX_TZDB_INCLUDE_DIR}/nx_tzdb/${HEADER_NAME}.h CONTENT "${NX_TZDB_TEMPLATE_H_IN}") diff --git a/externals/nx_tzdb/include/nx_tzdb.h b/externals/nx_tzdb/include/nx_tzdb.h new file mode 100644 index 0000000000..1f7c6069ae --- /dev/null +++ b/externals/nx_tzdb/include/nx_tzdb.h @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "nx_tzdb/africa.h" +#include "nx_tzdb/america.h" +#include "nx_tzdb/america_argentina.h" +#include "nx_tzdb/america_indiana.h" +#include "nx_tzdb/america_kentucky.h" +#include "nx_tzdb/america_north_dakota.h" +#include "nx_tzdb/antarctica.h" +#include "nx_tzdb/arctic.h" +#include "nx_tzdb/asia.h" +#include "nx_tzdb/atlantic.h" +#include "nx_tzdb/australia.h" +#include "nx_tzdb/base.h" +#include "nx_tzdb/brazil.h" +#include "nx_tzdb/canada.h" +#include "nx_tzdb/chile.h" +#include "nx_tzdb/etc.h" +#include "nx_tzdb/europe.h" +#include "nx_tzdb/indian.h" +#include "nx_tzdb/mexico.h" +#include "nx_tzdb/pacific.h" +#include "nx_tzdb/us.h" +#include "nx_tzdb/zoneinfo.h" diff --git a/externals/nx_tzdb/tzdb_template.h.in b/externals/nx_tzdb/tzdb_template.h.in new file mode 100644 index 0000000000..289d002ea8 --- /dev/null +++ b/externals/nx_tzdb/tzdb_template.h.in @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <cstdint> +#include <map> +#include <vector> + +namespace NxTzdb { + +// clang-format off +const static std::map<const char*, const std::vector<uint8_t>> @DIRECTORY_NAME@ = +{ +@FILE_DATA@}; +// clang-format on + +} // namespace NxTzdb diff --git a/externals/nx_tzdb/tzdb_to_nx b/externals/nx_tzdb/tzdb_to_nx new file mode 160000 +Subproject 212afa2394a74226dcf1b7996a570aae17debb6 diff --git a/externals/vcpkg b/externals/vcpkg -Subproject 656fcc6ab2b05c6d999b7eaca717027ac3738f7 +Subproject cbf56573a987527b39272e88cbdd11389b78c6e diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts index d4698ae1c5..bab4f4d0fb 100644 --- a/src/android/app/build.gradle.kts +++ b/src/android/app/build.gradle.kts @@ -2,12 +2,17 @@ // SPDX-License-Identifier: GPL-3.0-or-later import android.annotation.SuppressLint +import kotlin.collections.setOf +import org.jetbrains.kotlin.konan.properties.Properties +import org.jlleitschuh.gradle.ktlint.reporter.ReporterType plugins { id("com.android.application") id("org.jetbrains.kotlin.android") id("kotlin-parcelize") kotlin("plugin.serialization") version "1.8.21" + id("androidx.navigation.safeargs.kotlin") + id("org.jlleitschuh.gradle.ktlint") version "11.4.0" } /** @@ -42,24 +47,27 @@ android { jniLibs.useLegacyPackaging = true } - lint { - // This is important as it will run lint but not abort on error - // Lint has some overly obnoxious "errors" that should really be warnings - abortOnError = false - - //Uncomment disable lines for test builds... - //disable 'MissingTranslation'bin - //disable 'ExtraTranslation' - } - defaultConfig { // TODO If this is ever modified, change application_id in strings.xml applicationId = "org.yuzu.yuzu_emu" minSdk = 30 targetSdk = 33 - versionCode = 1 versionName = getGitVersion() + // If you want to use autoVersion for the versionCode, create a property in local.properties + // named "autoVersioned" and set it to "true" + val properties = Properties() + val versionProperty = try { + properties.load(project.rootProject.file("local.properties").inputStream()) + properties.getProperty("autoVersioned") ?: "" + } catch (e: Exception) { "" } + + versionCode = if (versionProperty == "true") { + autoVersion + } else { + 1 + } + ndk { @SuppressLint("ChromeOsAbiSupport") abiFilters += listOf("arm64-v8a") @@ -152,6 +160,24 @@ android { } } +tasks.getByPath("preBuild").dependsOn("ktlintCheck") + +ktlint { + version.set("0.47.1") + android.set(true) + ignoreFailures.set(false) + disabledRules.set( + setOf( + "no-wildcard-imports", + "package-name", + "import-ordering" + ) + ) + reporters { + reporter(ReporterType.CHECKSTYLE) + } +} + dependencies { implementation("androidx.core:core-ktx:1.10.1") implementation("androidx.appcompat:appcompat:1.6.1") diff --git a/src/android/app/src/main/AndroidManifest.xml b/src/android/app/src/main/AndroidManifest.xml index 1e92098ec1..e31ad69e20 100644 --- a/src/android/app/src/main/AndroidManifest.xml +++ b/src/android/app/src/main/AndroidManifest.xml @@ -24,6 +24,7 @@ SPDX-License-Identifier: GPL-3.0-or-later android:hasFragileUserData="true" android:supportsRtl="true" android:isGame="true" + android:localeConfig="@xml/locales_config" android:banner="@drawable/tv_banner" android:extractNativeLibs="true" android:fullBackupContent="@xml/data_extraction_rules" @@ -52,8 +53,9 @@ SPDX-License-Identifier: GPL-3.0-or-later <activity android:name="org.yuzu.yuzu_emu.activities.EmulationActivity" android:theme="@style/Theme.Yuzu.Main" - android:launchMode="singleTop" android:screenOrientation="userLandscape" + android:supportsPictureInPicture="true" + android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|uiMode" android:exported="true"> <intent-filter> diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt index 22af9e435a..9c32e044c0 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt @@ -14,16 +14,18 @@ import android.widget.TextView import androidx.annotation.Keep import androidx.fragment.app.DialogFragment import com.google.android.material.dialog.MaterialAlertDialogBuilder +import java.lang.ref.WeakReference import org.yuzu.yuzu_emu.YuzuApplication.Companion.appContext import org.yuzu.yuzu_emu.activities.EmulationActivity import org.yuzu.yuzu_emu.utils.DocumentsTree.Companion.isNativePath +import org.yuzu.yuzu_emu.utils.FileUtil.exists import org.yuzu.yuzu_emu.utils.FileUtil.getFileSize +import org.yuzu.yuzu_emu.utils.FileUtil.isDirectory import org.yuzu.yuzu_emu.utils.FileUtil.openContentUri import org.yuzu.yuzu_emu.utils.Log.error import org.yuzu.yuzu_emu.utils.Log.verbose import org.yuzu.yuzu_emu.utils.Log.warning import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable -import java.lang.ref.WeakReference /** * Class which contains methods that interact @@ -74,7 +76,9 @@ object NativeLibrary { fun openContentUri(path: String?, openmode: String?): Int { return if (isNativePath(path!!)) { YuzuApplication.documentsTree!!.openContentUri(path, openmode) - } else openContentUri(appContext, path, openmode) + } else { + openContentUri(appContext, path, openmode) + } } @Keep @@ -82,7 +86,29 @@ object NativeLibrary { fun getSize(path: String?): Long { return if (isNativePath(path!!)) { YuzuApplication.documentsTree!!.getFileSize(path) - } else getFileSize(appContext, path) + } else { + getFileSize(appContext, path) + } + } + + @Keep + @JvmStatic + fun exists(path: String?): Boolean { + return if (isNativePath(path!!)) { + YuzuApplication.documentsTree!!.exists(path) + } else { + exists(appContext, path) + } + } + + @Keep + @JvmStatic + fun isDirectory(path: String?): Boolean { + return if (isNativePath(path!!)) { + YuzuApplication.documentsTree!!.isDirectory(path) + } else { + isDirectory(appContext, path) + } } /** @@ -227,6 +253,8 @@ object NativeLibrary { external fun setAppDirectory(directory: String) + external fun installFileToNand(filename: String): Int + external fun initializeGpuDriver( hookLibDir: String?, customDriverDir: String?, @@ -258,7 +286,7 @@ object NativeLibrary { /** * Unpauses emulation from a paused state. */ - external fun unPauseEmulation() + external fun unpauseEmulation() /** * Pauses emulation. @@ -281,6 +309,26 @@ object NativeLibrary { external fun isRunning(): Boolean /** + * Returns true if emulation is paused. + */ + external fun isPaused(): Boolean + + /** + * Mutes emulation sound + */ + external fun muteAudio(): Boolean + + /** + * Unmutes emulation sound + */ + external fun unmuteAudio(): Boolean + + /** + * Returns true if emulation audio is muted. + */ + external fun isMuted(): Boolean + + /** * Returns the performance stats for the current game */ external fun getPerfStats(): DoubleArray @@ -429,7 +477,9 @@ object NativeLibrary { Html.FROM_HTML_MODE_LEGACY ) ) - .setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int -> emulationActivity.finish() } + .setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int -> + emulationActivity.finish() + } .setOnDismissListener { emulationActivity.finish() } emulationActivity.runOnUiThread { val alert = builder.create() @@ -507,4 +557,15 @@ object NativeLibrary { const val RELEASED = 0 const val PRESSED = 1 } + + /** + * Result from installFileToNand + */ + object InstallFileToNandResult { + const val Success = 0 + const val SuccessFileOverwritten = 1 + const val Error = 2 + const val ErrorBaseGame = 3 + const val ErrorFilenameExtension = 4 + } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt index 4c947b7869..04ab6a220a 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt @@ -7,12 +7,12 @@ import android.app.Application import android.app.NotificationChannel import android.app.NotificationManager import android.content.Context +import java.io.File import org.yuzu.yuzu_emu.utils.DirectoryInitialization import org.yuzu.yuzu_emu.utils.DocumentsTree import org.yuzu.yuzu_emu.utils.GpuDriverHelper -import java.io.File -fun Context.getPublicFilesDir() : File = getExternalFilesDir(null) ?: filesDir +fun Context.getPublicFilesDir(): File = getExternalFilesDir(null) ?: filesDir class YuzuApplication : Application() { private fun createNotificationChannels() { @@ -21,7 +21,9 @@ class YuzuApplication : Application() { getString(R.string.emulation_notification_channel_name), NotificationManager.IMPORTANCE_LOW ) - emulationChannel.description = getString(R.string.emulation_notification_channel_description) + emulationChannel.description = getString( + R.string.emulation_notification_channel_description + ) emulationChannel.setSound(null, null) emulationChannel.vibrationPattern = null @@ -48,7 +50,7 @@ class YuzuApplication : Application() { GpuDriverHelper.initializeDriverParameters(applicationContext) NativeLibrary.logDeviceInfo() - createNotificationChannels(); + createNotificationChannels() } companion object { diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt index 20a0394f54..ae665ed2ea 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt @@ -4,49 +4,57 @@ package org.yuzu.yuzu_emu.activities import android.app.Activity +import android.app.PendingIntent +import android.app.PictureInPictureParams +import android.app.RemoteAction +import android.content.BroadcastReceiver import android.content.Context import android.content.Intent +import android.content.IntentFilter +import android.content.res.Configuration import android.graphics.Rect +import android.graphics.drawable.Icon import android.hardware.Sensor import android.hardware.SensorEvent import android.hardware.SensorEventListener import android.hardware.SensorManager +import android.os.Build import android.os.Bundle +import android.util.Rational import android.view.InputDevice import android.view.KeyEvent import android.view.MotionEvent import android.view.Surface import android.view.View import android.view.inputmethod.InputMethodManager +import android.widget.Toast import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsControllerCompat -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle -import androidx.window.layout.WindowInfoTracker -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch +import androidx.navigation.fragment.NavHostFragment import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.R +import org.yuzu.yuzu_emu.databinding.ActivityEmulationBinding +import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting +import org.yuzu.yuzu_emu.features.settings.model.IntSetting import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel -import org.yuzu.yuzu_emu.fragments.EmulationFragment import org.yuzu.yuzu_emu.model.Game import org.yuzu.yuzu_emu.utils.ControllerMappingHelper import org.yuzu.yuzu_emu.utils.ForegroundService import org.yuzu.yuzu_emu.utils.InputHandler +import org.yuzu.yuzu_emu.utils.MemoryUtil import org.yuzu.yuzu_emu.utils.NfcReader -import org.yuzu.yuzu_emu.utils.SerializableHelper.parcelable import org.yuzu.yuzu_emu.utils.ThemeHelper import kotlin.math.roundToInt class EmulationActivity : AppCompatActivity(), SensorEventListener { + private lateinit var binding: ActivityEmulationBinding + private var controllerMappingHelper: ControllerMappingHelper? = null var isActivityRecreated = false - private var emulationFragment: EmulationFragment? = null private lateinit var nfcReader: NfcReader private lateinit var inputHandler: InputHandler @@ -55,7 +63,10 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { private var motionTimestamp: Long = 0 private var flipMotionOrientation: Boolean = false - private lateinit var game: Game + private val actionPause = "ACTION_EMULATOR_PAUSE" + private val actionPlay = "ACTION_EMULATOR_PLAY" + private val actionMute = "ACTION_EMULATOR_MUTE" + private val actionUnmute = "ACTION_EMULATOR_UNMUTE" private val settingsViewModel: SettingsViewModel by viewModels() @@ -70,45 +81,42 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { settingsViewModel.settings.loadSettings() super.onCreate(savedInstanceState) - if (savedInstanceState == null) { - // Get params we were passed - game = intent.parcelable(EXTRA_SELECTED_GAME)!! - isActivityRecreated = false - } else { - isActivityRecreated = true - restoreState(savedInstanceState) - } + + binding = ActivityEmulationBinding.inflate(layoutInflater) + setContentView(binding.root) + + val navHostFragment = + supportFragmentManager.findFragmentById(R.id.fragment_container) as NavHostFragment + val navController = navHostFragment.navController + navController + .setGraph(R.navigation.emulation_navigation, intent.extras) + + isActivityRecreated = savedInstanceState != null + controllerMappingHelper = ControllerMappingHelper() // Set these options now so that the SurfaceView the game renders into is the right size. enableFullscreenImmersive() - setContentView(R.layout.activity_emulation) window.decorView.setBackgroundColor(getColor(android.R.color.black)) - // Find or create the EmulationFragment - emulationFragment = - supportFragmentManager.findFragmentById(R.id.frame_emulation_fragment) as EmulationFragment? - if (emulationFragment == null) { - emulationFragment = EmulationFragment.newInstance(game) - supportFragmentManager.beginTransaction() - .add(R.id.frame_emulation_fragment, emulationFragment!!) - .commit() - } - title = game.title - nfcReader = NfcReader(this) nfcReader.initialize() inputHandler = InputHandler() inputHandler.initialize() - lifecycleScope.launch(Dispatchers.Main) { - lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { - WindowInfoTracker.getOrCreate(this@EmulationActivity) - .windowLayoutInfo(this@EmulationActivity) - .collect { emulationFragment?.updateCurrentLayout(this@EmulationActivity, it) } - } + val memoryUtil = MemoryUtil(this) + if (memoryUtil.isLessThan(8, MemoryUtil.Gb)) { + Toast.makeText( + this, + getString( + R.string.device_memory_inadequate, + memoryUtil.getDeviceRAM(), + "8 ${getString(R.string.memory_gigabyte)}" + ), + Toast.LENGTH_LONG + ).show() } // Start a foreground service to prevent the app from getting killed in the background @@ -143,6 +151,8 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { super.onResume() nfcReader.startScanning() startMotionSensorListener() + + buildPictureInPictureParams() } override fun onPause() { @@ -151,17 +161,22 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { stopMotionSensorListener() } + override fun onUserLeaveHint() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) { + if (BooleanSetting.PICTURE_IN_PICTURE.boolean && !isInPictureInPictureMode) { + val pictureInPictureParamsBuilder = PictureInPictureParams.Builder() + .getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder() + enterPictureInPictureMode(pictureInPictureParamsBuilder.build()) + } + } + } + override fun onNewIntent(intent: Intent) { super.onNewIntent(intent) setIntent(intent) nfcReader.onNewIntent(intent) } - override fun onSaveInstanceState(outState: Bundle) { - outState.putParcelable(EXTRA_SELECTED_GAME, game) - super.onSaveInstanceState(outState) - } - override fun dispatchKeyEvent(event: KeyEvent): Boolean { if (event.source and InputDevice.SOURCE_JOYSTICK != InputDevice.SOURCE_JOYSTICK && event.source and InputDevice.SOURCE_GAMEPAD != InputDevice.SOURCE_GAMEPAD @@ -248,10 +263,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { override fun onAccuracyChanged(sensor: Sensor, i: Int) {} - private fun restoreState(savedInstanceState: Bundle) { - game = savedInstanceState.parcelable(EXTRA_SELECTED_GAME)!! - } - private fun enableFullscreenImmersive() { WindowCompat.setDecorFitsSystemWindows(window, false) @@ -262,6 +273,144 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { } } + private fun PictureInPictureParams.Builder.getPictureInPictureAspectBuilder(): + PictureInPictureParams.Builder { + val aspectRatio = when (IntSetting.RENDERER_ASPECT_RATIO.int) { + 0 -> Rational(16, 9) + 1 -> Rational(4, 3) + 2 -> Rational(21, 9) + 3 -> Rational(16, 10) + else -> null // Best fit + } + return this.apply { aspectRatio?.let { setAspectRatio(it) } } + } + + private fun PictureInPictureParams.Builder.getPictureInPictureActionsBuilder(): + PictureInPictureParams.Builder { + val pictureInPictureActions: MutableList<RemoteAction> = mutableListOf() + val pendingFlags = PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE + + if (NativeLibrary.isPaused()) { + val playIcon = Icon.createWithResource(this@EmulationActivity, R.drawable.ic_pip_play) + val playPendingIntent = PendingIntent.getBroadcast( + this@EmulationActivity, + R.drawable.ic_pip_play, + Intent(actionPlay), + pendingFlags + ) + val playRemoteAction = RemoteAction( + playIcon, + getString(R.string.play), + getString(R.string.play), + playPendingIntent + ) + pictureInPictureActions.add(playRemoteAction) + } else { + val pauseIcon = Icon.createWithResource(this@EmulationActivity, R.drawable.ic_pip_pause) + val pausePendingIntent = PendingIntent.getBroadcast( + this@EmulationActivity, + R.drawable.ic_pip_pause, + Intent(actionPause), + pendingFlags + ) + val pauseRemoteAction = RemoteAction( + pauseIcon, + getString(R.string.pause), + getString(R.string.pause), + pausePendingIntent + ) + pictureInPictureActions.add(pauseRemoteAction) + } + + if (NativeLibrary.isMuted()) { + val unmuteIcon = Icon.createWithResource( + this@EmulationActivity, + R.drawable.ic_pip_unmute + ) + val unmutePendingIntent = PendingIntent.getBroadcast( + this@EmulationActivity, + R.drawable.ic_pip_unmute, + Intent(actionUnmute), + pendingFlags + ) + val unmuteRemoteAction = RemoteAction( + unmuteIcon, + getString(R.string.unmute), + getString(R.string.unmute), + unmutePendingIntent + ) + pictureInPictureActions.add(unmuteRemoteAction) + } else { + val muteIcon = Icon.createWithResource(this@EmulationActivity, R.drawable.ic_pip_mute) + val mutePendingIntent = PendingIntent.getBroadcast( + this@EmulationActivity, + R.drawable.ic_pip_mute, + Intent(actionMute), + pendingFlags + ) + val muteRemoteAction = RemoteAction( + muteIcon, + getString(R.string.mute), + getString(R.string.mute), + mutePendingIntent + ) + pictureInPictureActions.add(muteRemoteAction) + } + + return this.apply { setActions(pictureInPictureActions) } + } + + fun buildPictureInPictureParams() { + val pictureInPictureParamsBuilder = PictureInPictureParams.Builder() + .getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder() + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + pictureInPictureParamsBuilder.setAutoEnterEnabled( + BooleanSetting.PICTURE_IN_PICTURE.boolean + ) + } + setPictureInPictureParams(pictureInPictureParamsBuilder.build()) + } + + private var pictureInPictureReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent) { + if (intent.action == actionPlay) { + if (NativeLibrary.isPaused()) NativeLibrary.unpauseEmulation() + } else if (intent.action == actionPause) { + if (!NativeLibrary.isPaused()) NativeLibrary.pauseEmulation() + } + if (intent.action == actionUnmute) { + if (NativeLibrary.isMuted()) NativeLibrary.unmuteAudio() + } else if (intent.action == actionMute) { + if (!NativeLibrary.isMuted()) NativeLibrary.muteAudio() + } + buildPictureInPictureParams() + } + } + + override fun onPictureInPictureModeChanged( + isInPictureInPictureMode: Boolean, + newConfig: Configuration + ) { + super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig) + if (isInPictureInPictureMode) { + IntentFilter().apply { + addAction(actionPause) + addAction(actionPlay) + addAction(actionMute) + addAction(actionUnmute) + }.also { + registerReceiver(pictureInPictureReceiver, it) + } + } else { + try { + unregisterReceiver(pictureInPictureReceiver) + } catch (ignored: Exception) { + } + // Always resume audio, since there is no UI button + if (NativeLibrary.isMuted()) NativeLibrary.unmuteAudio() + } + } + private fun startMotionSensorListener() { val sensorManager = this.getSystemService(Context.SENSOR_SERVICE) as SensorManager val gyroSensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt index 7f9e2e2d47..e91277d359 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt @@ -16,6 +16,7 @@ import androidx.appcompat.app.AppCompatActivity import androidx.documentfile.provider.DocumentFile import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope +import androidx.navigation.findNavController import androidx.preference.PreferenceManager import androidx.recyclerview.widget.AsyncDifferConfig import androidx.recyclerview.widget.DiffUtil @@ -23,13 +24,13 @@ import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView import coil.load import kotlinx.coroutines.launch +import org.yuzu.yuzu_emu.HomeNavigationDirections import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.YuzuApplication +import org.yuzu.yuzu_emu.adapters.GameAdapter.GameViewHolder import org.yuzu.yuzu_emu.databinding.CardGameBinding -import org.yuzu.yuzu_emu.activities.EmulationActivity import org.yuzu.yuzu_emu.model.Game -import org.yuzu.yuzu_emu.adapters.GameAdapter.GameViewHolder import org.yuzu.yuzu_emu.model.GamesViewModel class GameAdapter(private val activity: AppCompatActivity) : @@ -58,7 +59,10 @@ class GameAdapter(private val activity: AppCompatActivity) : override fun onClick(view: View) { val holder = view.tag as GameViewHolder - val gameExists = DocumentFile.fromSingleUri(YuzuApplication.appContext, Uri.parse(holder.game.path))?.exists() == true + val gameExists = DocumentFile.fromSingleUri( + YuzuApplication.appContext, + Uri.parse(holder.game.path) + )?.exists() == true if (!gameExists) { Toast.makeText( YuzuApplication.appContext, @@ -78,7 +82,8 @@ class GameAdapter(private val activity: AppCompatActivity) : ) .apply() - EmulationActivity.launch(activity, holder.game) + val action = HomeNavigationDirections.actionGlobalEmulationActivity(holder.game) + view.findNavController().navigate(action) } inner class GameViewHolder(val binding: CardGameBinding) : diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt index b719dd5394..d3df3bc81d 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt @@ -58,11 +58,12 @@ class HomeSettingAdapter(private val activity: AppCompatActivity, var options: L ) when (option.titleId) { - R.string.get_early_access -> binding.optionLayout.background = - ContextCompat.getDrawable( - binding.optionCard.context, - R.drawable.premium_background - ) + R.string.get_early_access -> + binding.optionLayout.background = + ContextCompat.getDrawable( + binding.optionCard.context, + R.drawable.premium_background + ) } } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/applets/keyboard/SoftwareKeyboard.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/applets/keyboard/SoftwareKeyboard.kt index 82a6712b68..e058067c97 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/applets/keyboard/SoftwareKeyboard.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/applets/keyboard/SoftwareKeyboard.kt @@ -12,10 +12,10 @@ import android.view.WindowInsets import android.view.inputmethod.InputMethodManager import androidx.annotation.Keep import androidx.core.view.ViewCompat +import java.io.Serializable import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.applets.keyboard.ui.KeyboardDialogFragment -import java.io.Serializable @Keep object SoftwareKeyboard { @@ -40,19 +40,22 @@ object SoftwareKeyboard { // There isn't a good way to know that the IMM is dismissed, so poll every 500ms to submit inline keyboard result. val handler = Handler(Looper.myLooper()!!) val delayMs = 500 - handler.postDelayed(object : Runnable { - override fun run() { - val insets = ViewCompat.getRootWindowInsets(overlayView) - val isKeyboardVisible = insets!!.isVisible(WindowInsets.Type.ime()) - if (isKeyboardVisible) { - handler.postDelayed(this, delayMs.toLong()) - return - } + handler.postDelayed( + object : Runnable { + override fun run() { + val insets = ViewCompat.getRootWindowInsets(overlayView) + val isKeyboardVisible = insets!!.isVisible(WindowInsets.Type.ime()) + if (isKeyboardVisible) { + handler.postDelayed(this, delayMs.toLong()) + return + } - // No longer visible, submit the result. - NativeLibrary.submitInlineKeyboardInput(KeyEvent.KEYCODE_ENTER) - } - }, delayMs.toLong()) + // No longer visible, submit the result. + NativeLibrary.submitInlineKeyboardInput(KeyEvent.KEYCODE_ENTER) + } + }, + delayMs.toLong() + ) } @JvmStatic diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress.kt index 3b1559c805..a18efef198 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress.kt @@ -20,7 +20,10 @@ object DiskShaderCacheProgress { emulationActivity.getString(R.string.loading), emulationActivity.getString(R.string.preparing_shaders) ) - fragment.show(emulationActivity.supportFragmentManager, ShaderProgressDialogFragment.TAG) + fragment.show( + emulationActivity.supportFragmentManager, + ShaderProgressDialogFragment.TAG + ) } synchronized(finishLock) { finishLock.wait() } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ui/ShaderProgressDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ui/ShaderProgressDialogFragment.kt index 2c68c9ac3a..8a8e0a6e89 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ui/ShaderProgressDialogFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ui/ShaderProgressDialogFragment.kt @@ -62,7 +62,9 @@ class ShaderProgressDialogFragment : DialogFragment() { shaderProgressViewModel.message.observe(viewLifecycleOwner) { msg -> alertDialog.setMessage(msg) } - synchronized(DiskShaderCacheProgress.finishLock) { DiskShaderCacheProgress.finishLock.notifyAll() } + synchronized(DiskShaderCacheProgress.finishLock) { + DiskShaderCacheProgress.finishLock.notifyAll() + } } override fun onDestroyView() { diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/DocumentProvider.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/DocumentProvider.kt index 4c3a9ca806..f3be156b5e 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/DocumentProvider.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/DocumentProvider.kt @@ -13,11 +13,11 @@ import android.os.ParcelFileDescriptor import android.provider.DocumentsContract import android.provider.DocumentsProvider import android.webkit.MimeTypeMap +import java.io.* import org.yuzu.yuzu_emu.BuildConfig import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.YuzuApplication import org.yuzu.yuzu_emu.getPublicFilesDir -import java.io.* class DocumentProvider : DocumentsProvider() { private val baseDirectory: File @@ -44,7 +44,7 @@ class DocumentProvider : DocumentsProvider() { DocumentsContract.Document.COLUMN_SIZE ) - const val AUTHORITY : String = BuildConfig.APPLICATION_ID + ".user" + const val AUTHORITY: String = BuildConfig.APPLICATION_ID + ".user" const val ROOT_ID: String = "root" } @@ -58,7 +58,11 @@ class DocumentProvider : DocumentsProvider() { private fun getFile(documentId: String): File { if (documentId.startsWith(ROOT_ID)) { val file = baseDirectory.resolve(documentId.drop(ROOT_ID.length + 1)) - if (!file.exists()) throw FileNotFoundException("${file.absolutePath} ($documentId) not found") + if (!file.exists()) { + throw FileNotFoundException( + "${file.absolutePath} ($documentId) not found" + ) + } return file } else { throw FileNotFoundException("'$documentId' is not in any known root") @@ -80,7 +84,8 @@ class DocumentProvider : DocumentsProvider() { add(DocumentsContract.Root.COLUMN_SUMMARY, null) add( DocumentsContract.Root.COLUMN_FLAGS, - DocumentsContract.Root.FLAG_SUPPORTS_CREATE or DocumentsContract.Root.FLAG_SUPPORTS_IS_CHILD + DocumentsContract.Root.FLAG_SUPPORTS_CREATE or + DocumentsContract.Root.FLAG_SUPPORTS_IS_CHILD ) add(DocumentsContract.Root.COLUMN_TITLE, context!!.getString(R.string.app_name)) add(DocumentsContract.Root.COLUMN_DOCUMENT_ID, getDocumentId(baseDirectory)) @@ -127,11 +132,13 @@ class DocumentProvider : DocumentsProvider() { try { if (DocumentsContract.Document.MIME_TYPE_DIR == mimeType) { - if (!newFile.mkdir()) + if (!newFile.mkdir()) { throw IOException("Failed to create directory") + } } else { - if (!newFile.createNewFile()) + if (!newFile.createNewFile()) { throw IOException("Failed to create file") + } } } catch (e: IOException) { throw FileNotFoundException("Couldn't create document '${newFile.path}': ${e.message}") @@ -142,8 +149,9 @@ class DocumentProvider : DocumentsProvider() { override fun deleteDocument(documentId: String?) { val file = getFile(documentId!!) - if (!file.delete()) + if (!file.delete()) { throw FileNotFoundException("Couldn't delete document with ID '$documentId'") + } } override fun removeDocument(documentId: String, parentDocumentId: String?) { @@ -151,38 +159,55 @@ class DocumentProvider : DocumentsProvider() { val file = getFile(documentId) if (parent == file || file.parentFile == null || file.parentFile!! == parent) { - if (!file.delete()) + if (!file.delete()) { throw FileNotFoundException("Couldn't delete document with ID '$documentId'") + } } else { throw FileNotFoundException("Couldn't delete document with ID '$documentId'") } } override fun renameDocument(documentId: String?, displayName: String?): String { - if (displayName == null) - throw FileNotFoundException("Couldn't rename document '$documentId' as the new name is null") + if (displayName == null) { + throw FileNotFoundException( + "Couldn't rename document '$documentId' as the new name is null" + ) + } val sourceFile = getFile(documentId!!) val sourceParentFile = sourceFile.parentFile - ?: throw FileNotFoundException("Couldn't rename document '$documentId' as it has no parent") + ?: throw FileNotFoundException( + "Couldn't rename document '$documentId' as it has no parent" + ) val destFile = sourceParentFile.resolve(displayName) try { - if (!sourceFile.renameTo(destFile)) - throw FileNotFoundException("Couldn't rename document from '${sourceFile.name}' to '${destFile.name}'") + if (!sourceFile.renameTo(destFile)) { + throw FileNotFoundException( + "Couldn't rename document from '${sourceFile.name}' to '${destFile.name}'" + ) + } } catch (e: Exception) { - throw FileNotFoundException("Couldn't rename document from '${sourceFile.name}' to '${destFile.name}': ${e.message}") + throw FileNotFoundException( + "Couldn't rename document from '${sourceFile.name}' to '${destFile.name}': " + + "${e.message}" + ) } return getDocumentId(destFile) } private fun copyDocument( - sourceDocumentId: String, sourceParentDocumentId: String, + sourceDocumentId: String, + sourceParentDocumentId: String, targetParentDocumentId: String? ): String { - if (!isChildDocument(sourceParentDocumentId, sourceDocumentId)) - throw FileNotFoundException("Couldn't copy document '$sourceDocumentId' as its parent is not '$sourceParentDocumentId'") + if (!isChildDocument(sourceParentDocumentId, sourceDocumentId)) { + throw FileNotFoundException( + "Couldn't copy document '$sourceDocumentId' as its parent is not " + + "'$sourceParentDocumentId'" + ) + } return copyDocument(sourceDocumentId, targetParentDocumentId) } @@ -193,8 +218,13 @@ class DocumentProvider : DocumentsProvider() { val newFile = parent.resolveWithoutConflict(oldFile.name) try { - if (!(newFile.createNewFile() && newFile.setWritable(true) && newFile.setReadable(true))) + if (!( + newFile.createNewFile() && newFile.setWritable(true) && + newFile.setReadable(true) + ) + ) { throw IOException("Couldn't create new file") + } FileInputStream(oldFile).use { inStream -> FileOutputStream(newFile).use { outStream -> @@ -209,12 +239,14 @@ class DocumentProvider : DocumentsProvider() { } override fun moveDocument( - sourceDocumentId: String, sourceParentDocumentId: String?, + sourceDocumentId: String, + sourceParentDocumentId: String?, targetParentDocumentId: String? ): String { try { val newDocumentId = copyDocument( - sourceDocumentId, sourceParentDocumentId!!, + sourceDocumentId, + sourceParentDocumentId!!, targetParentDocumentId ) removeDocument(sourceDocumentId, sourceParentDocumentId) @@ -245,24 +277,30 @@ class DocumentProvider : DocumentsProvider() { add(DocumentsContract.Document.COLUMN_DOCUMENT_ID, localDocumentId) add( DocumentsContract.Document.COLUMN_DISPLAY_NAME, - if (localFile == baseDirectory) context!!.getString(R.string.app_name) else localFile.name + if (localFile == baseDirectory) { + context!!.getString(R.string.app_name) + } else { + localFile.name + } ) add(DocumentsContract.Document.COLUMN_SIZE, localFile.length()) add(DocumentsContract.Document.COLUMN_MIME_TYPE, getTypeForFile(localFile)) add(DocumentsContract.Document.COLUMN_LAST_MODIFIED, localFile.lastModified()) add(DocumentsContract.Document.COLUMN_FLAGS, flags) - if (localFile == baseDirectory) + if (localFile == baseDirectory) { add(DocumentsContract.Root.COLUMN_ICON, R.drawable.ic_yuzu) + } } return cursor } private fun getTypeForFile(file: File): Any { - return if (file.isDirectory) + return if (file.isDirectory) { DocumentsContract.Document.MIME_TYPE_DIR - else + } else { getTypeForName(file.name) + } } private fun getTypeForName(name: String): Any { @@ -270,8 +308,9 @@ class DocumentProvider : DocumentsProvider() { if (lastDot >= 0) { val extension = name.substring(lastDot + 1) val mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension) - if (mime != null) + if (mime != null) { return mime + } } return "application/octect-stream" } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt index 3dfd66779d..d41933766d 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt @@ -8,6 +8,10 @@ enum class BooleanSetting( override val section: String, override val defaultValue: Boolean ) : AbstractBooleanSetting { + CPU_DEBUG_MODE("cpu_debug_mode", Settings.SECTION_CPU, false), + FASTMEM("cpuopt_fastmem", Settings.SECTION_CPU, true), + FASTMEM_EXCLUSIVES("cpuopt_fastmem_exclusives", Settings.SECTION_CPU, true), + PICTURE_IN_PICTURE("picture_in_picture", Settings.SECTION_GENERAL, true), USE_CUSTOM_RTC("custom_rtc_enabled", Settings.SECTION_SYSTEM, false); override var boolean: Boolean = defaultValue @@ -27,6 +31,7 @@ enum class BooleanSetting( companion object { private val NOT_RUNTIME_EDITABLE = listOf( + PICTURE_IN_PICTURE, USE_CUSTOM_RTC ) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt index c5722a5a17..4427a7d9dc 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt @@ -26,13 +26,18 @@ enum class IntSetting( RENDERER_FORCE_MAX_CLOCK( "force_max_clock", Settings.SECTION_RENDERER, - 1 + 0 ), RENDERER_ASYNCHRONOUS_SHADERS( "use_asynchronous_shaders", Settings.SECTION_RENDERER, 0 ), + RENDERER_REACTIVE_FLUSHING( + "use_reactive_flushing", + Settings.SECTION_RENDERER, + 0 + ), RENDERER_DEBUG( "debug", Settings.SECTION_RENDERER, @@ -88,6 +93,11 @@ enum class IntSetting( Settings.SECTION_RENDERER, 0 ), + RENDERER_SCREEN_LAYOUT( + "screen_layout", + Settings.SECTION_RENDERER, + Settings.LayoutOption_MobileLandscape + ), RENDERER_ASPECT_RATIO( "aspect_ratio", Settings.SECTION_RENDERER, diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt index 8df20b928f..88afb2223c 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt @@ -4,11 +4,11 @@ package org.yuzu.yuzu_emu.features.settings.model import android.text.TextUtils +import java.util.* import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.YuzuApplication import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivityView import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile -import java.util.* class Settings { private var gameId: String? = null @@ -133,7 +133,6 @@ class Settings { const val PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER = "EmulationMenuSettings_JoystickRelCenter" const val PREF_MENU_SETTINGS_DPAD_SLIDE = "EmulationMenuSettings_DpadSlideEnable" const val PREF_MENU_SETTINGS_HAPTICS = "EmulationMenuSettings_Haptics" - const val PREF_MENU_SETTINGS_LANDSCAPE = "EmulationMenuSettings_LandscapeScreenLayout" const val PREF_MENU_SETTINGS_SHOW_FPS = "EmulationMenuSettings_ShowFps" const val PREF_MENU_SETTINGS_SHOW_OVERLAY = "EmulationMenuSettings_ShowOverlay" @@ -144,6 +143,10 @@ class Settings { private val configFileSectionsMap: MutableMap<String, List<String>> = HashMap() + const val LayoutOption_Unspecified = 0 + const val LayoutOption_MobilePortrait = 4 + const val LayoutOption_MobileLandscape = 5 + init { configFileSectionsMap[SettingsFile.FILE_NAME_CONFIG] = listOf( diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt index 63f95690c7..6621289fd5 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt @@ -8,6 +8,7 @@ enum class StringSetting( override val section: String, override val defaultValue: String ) : AbstractStringSetting { + AUDIO_OUTPUT_ENGINE("output_engine", Settings.SECTION_AUDIO, "auto"), CUSTOM_RTC("custom_rtc", Settings.SECTION_SYSTEM, "0"); override var string: String = defaultValue diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/HeaderSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/HeaderSetting.kt index 0f8edbfb01..a670013111 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/HeaderSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/HeaderSetting.kt @@ -3,12 +3,8 @@ package org.yuzu.yuzu_emu.features.settings.model.view -import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting - class HeaderSetting( - setting: AbstractSetting?, - titleId: Int, - descriptionId: Int -) : SettingsItem(setting, titleId, descriptionId) { + titleId: Int +) : SettingsItem(null, titleId, 0) { override val type = TYPE_HEADER } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SingleChoiceSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SingleChoiceSetting.kt index 9eac9904e8..7306ec458e 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SingleChoiceSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SingleChoiceSetting.kt @@ -4,7 +4,6 @@ package org.yuzu.yuzu_emu.features.settings.model.view import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting -import org.yuzu.yuzu_emu.features.settings.model.IntSetting class SingleChoiceSetting( setting: AbstractIntSetting?, diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt index 842648ce45..92d0167ae0 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt @@ -3,13 +3,11 @@ package org.yuzu.yuzu_emu.features.settings.model.view +import kotlin.math.roundToInt import org.yuzu.yuzu_emu.features.settings.model.AbstractFloatSetting import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting -import org.yuzu.yuzu_emu.features.settings.model.FloatSetting -import org.yuzu.yuzu_emu.features.settings.model.IntSetting import org.yuzu.yuzu_emu.utils.Log -import kotlin.math.roundToInt class SliderSetting( setting: AbstractSetting?, @@ -19,7 +17,7 @@ class SliderSetting( val max: Int, val units: String, val key: String? = null, - val defaultValue: Int? = null, + val defaultValue: Int? = null ) : SettingsItem(setting, titleId, descriptionId) { override val type = TYPE_SLIDER diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt index 9e9b00d10d..3b6731dcd5 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt @@ -5,24 +5,25 @@ package org.yuzu.yuzu_emu.features.settings.model.view import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting import org.yuzu.yuzu_emu.features.settings.model.AbstractStringSetting -import org.yuzu.yuzu_emu.features.settings.model.StringSetting class StringSingleChoiceSetting( - val key: String? = null, setting: AbstractSetting?, titleId: Int, descriptionId: Int, - val choicesId: Array<String>, - private val valuesId: Array<String>?, + val choices: Array<String>, + val values: Array<String>?, + val key: String? = null, private val defaultValue: String? = null ) : SettingsItem(setting, titleId, descriptionId) { override val type = TYPE_STRING_SINGLE_CHOICE fun getValueAt(index: Int): String? { - if (valuesId == null) return null - return if (index >= 0 && index < valuesId.size) { - valuesId[index] - } else "" + if (values == null) return null + return if (index >= 0 && index < values.size) { + values[index] + } else { + "" + } } val selectedValue: String @@ -35,8 +36,8 @@ class StringSingleChoiceSetting( val selectValueIndex: Int get() { val selectedValue = selectedValue - for (i in valuesId!!.indices) { - if (valuesId[i] == selectedValue) { + for (i in values!!.indices) { + if (values[i] == selectedValue) { return i } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt index a3ef59c2f4..8a9d13a92e 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt @@ -3,8 +3,6 @@ package org.yuzu.yuzu_emu.features.settings.model.view -import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting - class SubmenuSetting( titleId: Int, descriptionId: Int, diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt index 72e2cce2ac..a5af5a7aec 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt @@ -8,17 +8,18 @@ import android.content.Intent import android.os.Bundle import android.view.Menu import android.view.View +import android.view.ViewGroup.MarginLayoutParams import android.widget.Toast +import androidx.activity.OnBackPressedCallback +import androidx.activity.result.ActivityResultLauncher import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity import androidx.core.view.ViewCompat import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsCompat -import android.view.ViewGroup.MarginLayoutParams -import androidx.activity.OnBackPressedCallback import androidx.core.view.updatePadding import com.google.android.material.color.MaterialColors -import org.yuzu.yuzu_emu.NativeLibrary +import java.io.IOException import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting @@ -29,7 +30,6 @@ import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel import org.yuzu.yuzu_emu.features.settings.model.StringSetting import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile import org.yuzu.yuzu_emu.utils.* -import java.io.IOException class SettingsActivity : AppCompatActivity(), SettingsActivityView { private val presenter = SettingsActivityPresenter(this) @@ -59,7 +59,9 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView { setSupportActionBar(binding.toolbarSettings) supportActionBar!!.setDisplayHomeAsUpEnabled(true) - if (InsetsHelper.getSystemGestureType(applicationContext) != InsetsHelper.GESTURE_NAVIGATION) { + if (InsetsHelper.getSystemGestureType(applicationContext) != + InsetsHelper.GESTURE_NAVIGATION + ) { binding.navigationBarShade.setBackgroundColor( ThemeHelper.getColorWithOpacity( MaterialColors.getColor( @@ -75,7 +77,8 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView { this, object : OnBackPressedCallback(true) { override fun handleOnBackPressed() = navigateBack() - }) + } + ) setInsets() } @@ -148,11 +151,13 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView { private fun areSystemAnimationsEnabled(): Boolean { val duration = android.provider.Settings.Global.getFloat( contentResolver, - android.provider.Settings.Global.ANIMATOR_DURATION_SCALE, 1f + android.provider.Settings.Global.ANIMATOR_DURATION_SCALE, + 1f ) val transition = android.provider.Settings.Global.getFloat( contentResolver, - android.provider.Settings.Global.TRANSITION_ANIMATION_SCALE, 1f + android.provider.Settings.Global.TRANSITION_ANIMATION_SCALE, + 1f ) return duration != 0f && transition != 0f } @@ -207,7 +212,9 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView { get() = supportFragmentManager.findFragmentByTag(FRAGMENT_TAG) as SettingsFragment? private fun setInsets() { - ViewCompat.setOnApplyWindowInsetsListener(binding.frameContent) { view: View, windowInsets: WindowInsetsCompat -> + ViewCompat.setOnApplyWindowInsetsListener( + binding.frameContent + ) { view: View, windowInsets: WindowInsetsCompat -> val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout()) view.updatePadding( @@ -239,5 +246,17 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView { settings.putExtra(ARG_GAME_ID, gameId) context.startActivity(settings) } + + fun launch( + context: Context, + launcher: ActivityResultLauncher<Intent>, + menuTag: String?, + gameId: String? + ) { + val settings = Intent(context, SettingsActivity::class.java) + settings.putExtra(ARG_MENU_TAG, menuTag) + settings.putExtra(ARG_GAME_ID, gameId) + launcher.launch(settings) + } } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityPresenter.kt index 4361d95fbb..93e677b21b 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityPresenter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityPresenter.kt @@ -6,12 +6,12 @@ package org.yuzu.yuzu_emu.features.settings.ui import android.content.Context import android.os.Bundle import android.text.TextUtils +import java.io.File import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.features.settings.model.Settings import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile import org.yuzu.yuzu_emu.utils.DirectoryInitialization import org.yuzu.yuzu_emu.utils.Log -import java.io.File class SettingsActivityPresenter(private val activityView: SettingsActivityView) { val settings: Settings get() = activityView.settings @@ -46,9 +46,15 @@ class SettingsActivityPresenter(private val activityView: SettingsActivityView) private fun prepareDirectoriesIfNeeded() { val configFile = - File(DirectoryInitialization.userDirectory + "/config/" + SettingsFile.FILE_NAME_CONFIG + ".ini") + File( + "${DirectoryInitialization.userDirectory}/config/" + + "${SettingsFile.FILE_NAME_CONFIG}.ini" + ) if (!configFile.exists()) { - Log.error(DirectoryInitialization.userDirectory + "/config/" + SettingsFile.FILE_NAME_CONFIG + ".ini") + Log.error( + "${DirectoryInitialization.userDirectory}/config/" + + "${SettingsFile.FILE_NAME_CONFIG}.ini" + ) Log.error("yuzu config file could not be found!") } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt index 1eb4899fcb..ce0b92c90e 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt @@ -13,7 +13,6 @@ import android.view.ViewGroup import android.widget.TextView import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity -import androidx.fragment.app.setFragmentResultListener import androidx.recyclerview.widget.RecyclerView import com.google.android.material.datepicker.MaterialDatePicker import com.google.android.material.dialog.MaterialAlertDialogBuilder @@ -139,7 +138,7 @@ class SettingsAdapter( clickedItem = item dialog = MaterialAlertDialogBuilder(context) .setTitle(item.nameId) - .setSingleChoiceItems(item.choicesId, item.selectValueIndex, this) + .setSingleChoiceItems(item.choices, item.selectValueIndex, this) .show() } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt index 8671479502..70a74c4dda 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt @@ -50,7 +50,10 @@ class SettingsFragment : Fragment(), SettingsFragmentView { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { settingsAdapter = SettingsAdapter(this, requireActivity()) - val dividerDecoration = MaterialDividerItemDecoration(requireContext(), LinearLayoutManager.VERTICAL) + val dividerDecoration = MaterialDividerItemDecoration( + requireContext(), + LinearLayoutManager.VERTICAL + ) dividerDecoration.isLastItemDecorated = false binding.listSettings.apply { adapter = settingsAdapter @@ -99,7 +102,9 @@ class SettingsFragment : Fragment(), SettingsFragmentView { } private fun setInsets() { - ViewCompat.setOnApplyWindowInsetsListener(binding.listSettings) { view: View, windowInsets: WindowInsetsCompat -> + ViewCompat.setOnApplyWindowInsetsListener( + binding.listSettings + ) { view: View, windowInsets: WindowInsetsCompat -> val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) view.updatePadding(bottom = insets.bottom) windowInsets diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt index 061046b2e7..59c1d9d545 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt @@ -7,7 +7,6 @@ import android.content.SharedPreferences import android.os.Build import android.text.TextUtils import androidx.preference.PreferenceManager -import com.google.android.material.dialog.MaterialAlertDialogBuilder import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.YuzuApplication import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting @@ -43,7 +42,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) } fun putSetting(setting: AbstractSetting) { - if (setting.section == null) { + if (setting.section == null || setting.key == null) { return } @@ -166,6 +165,15 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) IntSetting.CPU_ACCURACY.defaultValue ) ) + add( + SwitchSetting( + BooleanSetting.PICTURE_IN_PICTURE, + R.string.picture_in_picture, + R.string.picture_in_picture_description, + BooleanSetting.PICTURE_IN_PICTURE.key, + BooleanSetting.PICTURE_IN_PICTURE.defaultValue + ) + ) } } @@ -227,7 +235,6 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) private fun addGraphicsSettings(sl: ArrayList<SettingsItem>) { settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_graphics)) sl.apply { - add( SingleChoiceSetting( IntSetting.RENDERER_ACCURACY, @@ -285,6 +292,17 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) ) add( SingleChoiceSetting( + IntSetting.RENDERER_SCREEN_LAYOUT, + R.string.renderer_screen_layout, + 0, + R.array.rendererScreenLayoutNames, + R.array.rendererScreenLayoutValues, + IntSetting.RENDERER_SCREEN_LAYOUT.key, + IntSetting.RENDERER_SCREEN_LAYOUT.defaultValue + ) + ) + add( + SingleChoiceSetting( IntSetting.RENDERER_ASPECT_RATIO, R.string.renderer_aspect_ratio, 0, @@ -321,23 +339,45 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) IntSetting.RENDERER_ASYNCHRONOUS_SHADERS.defaultValue ) ) + add( + SwitchSetting( + IntSetting.RENDERER_REACTIVE_FLUSHING, + R.string.renderer_reactive_flushing, + R.string.renderer_reactive_flushing_description, + IntSetting.RENDERER_REACTIVE_FLUSHING.key, + IntSetting.RENDERER_REACTIVE_FLUSHING.defaultValue + ) + ) } } private fun addAudioSettings(sl: ArrayList<SettingsItem>) { settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_audio)) - sl.add( - SliderSetting( - IntSetting.AUDIO_VOLUME, - R.string.audio_volume, - R.string.audio_volume_description, - 0, - 100, - "%", - IntSetting.AUDIO_VOLUME.key, - IntSetting.AUDIO_VOLUME.defaultValue - ) - ) + sl.apply { + add( + StringSingleChoiceSetting( + StringSetting.AUDIO_OUTPUT_ENGINE, + R.string.audio_output_engine, + 0, + settingsActivity.resources.getStringArray(R.array.outputEngineEntries), + settingsActivity.resources.getStringArray(R.array.outputEngineValues), + StringSetting.AUDIO_OUTPUT_ENGINE.key, + StringSetting.AUDIO_OUTPUT_ENGINE.defaultValue + ) + ) + add( + SliderSetting( + IntSetting.AUDIO_VOLUME, + R.string.audio_volume, + R.string.audio_volume_description, + 0, + 100, + "%", + IntSetting.AUDIO_VOLUME.key, + IntSetting.AUDIO_VOLUME.defaultValue + ) + ) + } } private fun addThemeSettings(sl: ArrayList<SettingsItem>) { @@ -440,6 +480,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) private fun addDebugSettings(sl: ArrayList<SettingsItem>) { settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_debug)) sl.apply { + add(HeaderSetting(R.string.gpu)) add( SingleChoiceSetting( IntSetting.RENDERER_BACKEND, @@ -460,6 +501,39 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) IntSetting.RENDERER_DEBUG.defaultValue ) ) + + add(HeaderSetting(R.string.cpu)) + add( + SwitchSetting( + BooleanSetting.CPU_DEBUG_MODE, + R.string.cpu_debug_mode, + R.string.cpu_debug_mode_description, + BooleanSetting.CPU_DEBUG_MODE.key, + BooleanSetting.CPU_DEBUG_MODE.defaultValue + ) + ) + + val fastmem = object : AbstractBooleanSetting { + override var boolean: Boolean + get() = + BooleanSetting.FASTMEM.boolean && BooleanSetting.FASTMEM_EXCLUSIVES.boolean + set(value) { + BooleanSetting.FASTMEM.boolean = value + BooleanSetting.FASTMEM_EXCLUSIVES.boolean = value + } + override val key: String? = null + override val section: String = Settings.SECTION_CPU + override val isRuntimeEditable: Boolean = false + override val valueAsString: String = "" + override val defaultValue: Any = true + } + add( + SwitchSetting( + fastmem, + R.string.fastmem, + 0 + ) + ) } } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt index 04c045e774..7955532eee 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt @@ -4,15 +4,15 @@ package org.yuzu.yuzu_emu.features.settings.ui.viewholder import android.view.View -import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding -import org.yuzu.yuzu_emu.features.settings.model.view.DateTimeSetting -import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem -import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter import java.time.Instant import java.time.ZoneId import java.time.ZonedDateTime import java.time.format.DateTimeFormatter import java.time.format.FormatStyle +import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding +import org.yuzu.yuzu_emu.features.settings.model.view.DateTimeSetting +import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem +import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) : SettingViewHolder(binding.root, adapter) { diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt index de764a27f4..e4e321bd36 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt @@ -26,6 +26,14 @@ class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: Setti for (i in values.indices) { if (values[i] == item.selectedValue) { binding.textSettingDescription.text = resMgr.getStringArray(item.choicesId)[i] + return + } + } + } else if (item is StringSingleChoiceSetting) { + for (i in item.values!!.indices) { + if (item.values[i] == item.selectedValue) { + binding.textSettingDescription.text = item.choices[i] + return } } } else { diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt index b163bd6ca3..54f531795c 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt @@ -6,8 +6,8 @@ package org.yuzu.yuzu_emu.features.settings.ui.viewholder import android.view.View import android.widget.CompoundButton import org.yuzu.yuzu_emu.databinding.ListItemSettingSwitchBinding -import org.yuzu.yuzu_emu.features.settings.model.view.SwitchSetting import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem +import org.yuzu.yuzu_emu.features.settings.model.view.SwitchSetting import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter class SwitchSettingViewHolder(val binding: ListItemSettingSwitchBinding, adapter: SettingsAdapter) : diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt index e29bca11df..70a52df5d4 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt @@ -3,6 +3,8 @@ package org.yuzu.yuzu_emu.features.settings.utils +import java.io.* +import java.util.* import org.ini4j.Wini import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.R @@ -13,8 +15,6 @@ import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivityView import org.yuzu.yuzu_emu.utils.BiMap import org.yuzu.yuzu_emu.utils.DirectoryInitialization import org.yuzu.yuzu_emu.utils.Log -import java.io.* -import java.util.* /** * Contains static methods for interacting with .ini files in which settings are stored. @@ -137,9 +137,12 @@ object SettingsFile { for (settingKey in sortedKeySet) { val setting = settings[settingKey] NativeLibrary.setUserSetting( - gameId, mapSectionNameFromIni( + gameId, + mapSectionNameFromIni( section.name - ), setting!!.key, setting.valueAsString + ), + setting!!.key, + setting.valueAsString ) } } @@ -148,13 +151,17 @@ object SettingsFile { private fun mapSectionNameFromIni(generalSectionName: String): String? { return if (sectionsMap.getForward(generalSectionName) != null) { sectionsMap.getForward(generalSectionName) - } else generalSectionName + } else { + generalSectionName + } } private fun mapSectionNameToIni(generalSectionName: String): String { return if (sectionsMap.getBackward(generalSectionName) != null) { sectionsMap.getBackward(generalSectionName).toString() - } else generalSectionName + } else { + generalSectionName + } } fun getSettingsFile(fileName: String): File { @@ -237,5 +244,21 @@ object SettingsFile { val setting = settings[key] parser.put(header, setting!!.key, setting.valueAsString) } + + BooleanSetting.values().forEach { + if (!keySet.contains(it.key)) { + parser.put(header, it.key, it.valueAsString) + } + } + IntSetting.values().forEach { + if (!keySet.contains(it.key)) { + parser.put(header, it.key, it.valueAsString) + } + } + StringSetting.values().forEach { + if (!keySet.contains(it.key)) { + parser.put(header, it.key, it.valueAsString) + } + } } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt index c92e2755c2..2ff827c6b0 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt @@ -66,7 +66,11 @@ class AboutFragment : Fragment() { true } - binding.buttonContributors.setOnClickListener { openLink(getString(R.string.contributors_link)) } + binding.buttonContributors.setOnClickListener { + openLink( + getString(R.string.contributors_link) + ) + } binding.buttonLicenses.setOnClickListener { exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true) binding.root.findNavController().navigate(R.id.action_aboutFragment_to_licensesFragment) @@ -101,7 +105,9 @@ class AboutFragment : Fragment() { } private fun setInsets() = - ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _: View, windowInsets: WindowInsetsCompat -> + ViewCompat.setOnApplyWindowInsetsListener( + binding.root + ) { _: View, windowInsets: WindowInsetsCompat -> val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout()) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EarlyAccessFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EarlyAccessFragment.kt index d8bbc1ce40..dbc16da4af 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EarlyAccessFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EarlyAccessFragment.kt @@ -49,7 +49,11 @@ class EarlyAccessFragment : Fragment() { parentFragmentManager.primaryNavigationFragment?.findNavController()?.popBackStack() } - binding.getEarlyAccessButton.setOnClickListener { openLink(getString(R.string.play_store_link)) } + binding.getEarlyAccessButton.setOnClickListener { + openLink( + getString(R.string.play_store_link) + ) + } setInsets() } @@ -60,7 +64,9 @@ class EarlyAccessFragment : Fragment() { } private fun setInsets() = - ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _: View, windowInsets: WindowInsetsCompat -> + ViewCompat.setOnApplyWindowInsetsListener( + binding.root + ) { _: View, windowInsets: WindowInsetsCompat -> val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout()) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt index 9523381cd3..09976db62f 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt @@ -7,30 +7,39 @@ import android.annotation.SuppressLint import android.app.AlertDialog import android.content.Context import android.content.DialogInterface +import android.content.Intent import android.content.SharedPreferences import android.content.pm.ActivityInfo -import android.content.res.Resources +import android.content.res.Configuration import android.graphics.Color import android.os.Bundle import android.os.Handler import android.os.Looper import android.util.Rational -import android.util.TypedValue import android.view.* import android.widget.TextView import androidx.activity.OnBackPressedCallback +import androidx.activity.result.ActivityResultLauncher +import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.widget.PopupMenu import androidx.core.content.res.ResourcesCompat import androidx.core.graphics.Insets import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat -import androidx.core.view.updatePadding +import androidx.core.view.isVisible import androidx.fragment.app.Fragment +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle +import androidx.navigation.fragment.navArgs import androidx.preference.PreferenceManager import androidx.window.layout.FoldingFeature +import androidx.window.layout.WindowInfoTracker import androidx.window.layout.WindowLayoutInfo import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.slider.Slider +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.YuzuApplication @@ -41,9 +50,8 @@ import org.yuzu.yuzu_emu.features.settings.model.IntSetting import org.yuzu.yuzu_emu.features.settings.model.Settings import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile -import org.yuzu.yuzu_emu.model.Game +import org.yuzu.yuzu_emu.overlay.InputOverlay import org.yuzu.yuzu_emu.utils.* -import org.yuzu.yuzu_emu.utils.SerializableHelper.parcelable class EmulationFragment : Fragment(), SurfaceHolder.Callback { private lateinit var preferences: SharedPreferences @@ -54,13 +62,30 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { private var _binding: FragmentEmulationBinding? = null private val binding get() = _binding!! - private lateinit var game: Game + val args by navArgs<EmulationFragmentArgs>() + + private var isInFoldableLayout = false + + private lateinit var onReturnFromSettings: ActivityResultLauncher<Intent> override fun onAttach(context: Context) { super.onAttach(context) if (context is EmulationActivity) { emulationActivity = context NativeLibrary.setEmulationActivity(context) + + lifecycleScope.launch(Dispatchers.Main) { + lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { + WindowInfoTracker.getOrCreate(context) + .windowLayoutInfo(context) + .collect { updateFoldableLayout(context, it) } + } + } + + onReturnFromSettings = context.activityResultRegistry.register( + "SettingsResult", + ActivityResultContracts.StartActivityForResult() + ) { updateScreenLayout() } } else { throw IllegalStateException("EmulationFragment must have EmulationActivity parent") } @@ -75,8 +100,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { // So this fragment doesn't restart on configuration changes; i.e. rotation. retainInstance = true preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) - game = requireArguments().parcelable(EmulationActivity.EXTRA_SELECTED_GAME)!! - emulationState = EmulationState(game.path) + emulationState = EmulationState(args.game.path) } /** @@ -100,7 +124,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { updateShowFpsOverlay() binding.inGameMenu.getHeaderView(0).findViewById<TextView>(R.id.text_game_title).text = - game.title + args.game.title binding.inGameMenu.setNavigationItemSelectedListener { when (it.itemId) { R.id.menu_pause_emulation -> { @@ -125,7 +149,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { } R.id.menu_settings -> { - SettingsActivity.launch(requireContext(), SettingsFile.FILE_NAME_CONFIG, "") + SettingsActivity.launch( + requireContext(), + onReturnFromSettings, + SettingsFile.FILE_NAME_CONFIG, + "" + ) true } @@ -150,9 +179,48 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { requireActivity(), object : OnBackPressedCallback(true) { override fun handleOnBackPressed() { - if (binding.drawerLayout.isOpen) binding.drawerLayout.close() else binding.drawerLayout.open() + if (binding.drawerLayout.isOpen) { + binding.drawerLayout.close() + } else { + binding.drawerLayout.open() + } + } + } + ) + + viewLifecycleOwner.lifecycleScope.launch(Dispatchers.Main) { + lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { + WindowInfoTracker.getOrCreate(requireContext()) + .windowLayoutInfo(requireActivity()) + .collect { updateFoldableLayout(requireActivity() as EmulationActivity, it) } + } + } + } + + override fun onConfigurationChanged(newConfig: Configuration) { + super.onConfigurationChanged(newConfig) + if (emulationActivity?.isInPictureInPictureMode == true) { + if (binding.drawerLayout.isOpen) { + binding.drawerLayout.close() + } + if (EmulationMenuSettings.showOverlay) { + binding.surfaceInputOverlay.post { binding.surfaceInputOverlay.isVisible = false } + } + } else { + if (EmulationMenuSettings.showOverlay) { + binding.surfaceInputOverlay.post { binding.surfaceInputOverlay.isVisible = true } + } + if (!isInFoldableLayout) { + if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) { + binding.surfaceInputOverlay.orientation = InputOverlay.PORTRAIT + } else { + binding.surfaceInputOverlay.orientation = InputOverlay.LANDSCAPE } - }) + } + if (!binding.surfaceInputOverlay.isInEditMode) { + refreshInputOverlay() + } + } } override fun onResume() { @@ -161,16 +229,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { DirectoryInitialization.start(requireContext()) } - binding.surfaceEmulation.setAspectRatio( - when (IntSetting.RENDERER_ASPECT_RATIO.int) { - 0 -> Rational(16, 9) - 1 -> Rational(4, 3) - 2 -> Rational(21, 9) - 3 -> Rational(16, 10) - 4 -> null // Stretch - else -> Rational(16, 9) - } - ) + updateScreenLayout() emulationState.run(emulationActivity!!.isActivityRecreated) } @@ -231,31 +290,72 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { } } - private val Number.toPx get() = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, this.toFloat(), Resources.getSystem().displayMetrics).toInt() - - fun updateCurrentLayout(emulationActivity: EmulationActivity, newLayoutInfo: WindowLayoutInfo) { - val isFolding = (newLayoutInfo.displayFeatures.find { it is FoldingFeature } as? FoldingFeature)?.let { - if (it.isSeparating) { - emulationActivity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED - if (it.orientation == FoldingFeature.Orientation.HORIZONTAL) { - binding.surfaceEmulation.layoutParams.height = it.bounds.top - binding.inGameMenu.layoutParams.height = it.bounds.bottom - binding.overlayContainer.layoutParams.height = it.bounds.bottom - 48.toPx - binding.overlayContainer.updatePadding(0, 0, 0, 24.toPx) - } + @SuppressLint("SourceLockedOrientationActivity") + private fun updateOrientation() { + emulationActivity?.let { + it.requestedOrientation = when (IntSetting.RENDERER_SCREEN_LAYOUT.int) { + Settings.LayoutOption_MobileLandscape -> + ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE + Settings.LayoutOption_MobilePortrait -> + ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT + Settings.LayoutOption_Unspecified -> ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED + else -> ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE } - it.isSeparating - } ?: false + } + } + + private fun updateScreenLayout() { + binding.surfaceEmulation.setAspectRatio( + when (IntSetting.RENDERER_ASPECT_RATIO.int) { + 0 -> Rational(16, 9) + 1 -> Rational(4, 3) + 2 -> Rational(21, 9) + 3 -> Rational(16, 10) + 4 -> null // Stretch + else -> Rational(16, 9) + } + ) + emulationActivity?.buildPictureInPictureParams() + updateOrientation() + } + + private fun updateFoldableLayout( + emulationActivity: EmulationActivity, + newLayoutInfo: WindowLayoutInfo + ) { + val isFolding = + (newLayoutInfo.displayFeatures.find { it is FoldingFeature } as? FoldingFeature)?.let { + if (it.isSeparating) { + emulationActivity.requestedOrientation = + ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED + if (it.orientation == FoldingFeature.Orientation.HORIZONTAL) { + // Restrict emulation and overlays to the top of the screen + binding.emulationContainer.layoutParams.height = it.bounds.top + binding.overlayContainer.layoutParams.height = it.bounds.top + // Restrict input and menu drawer to the bottom of the screen + binding.inputContainer.layoutParams.height = it.bounds.bottom + binding.inGameMenu.layoutParams.height = it.bounds.bottom + + isInFoldableLayout = true + binding.surfaceInputOverlay.orientation = InputOverlay.FOLDABLE + refreshInputOverlay() + } + } + it.isSeparating + } ?: false if (!isFolding) { - binding.surfaceEmulation.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT - binding.inGameMenu.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT + binding.emulationContainer.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT + binding.inputContainer.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT binding.overlayContainer.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT - binding.overlayContainer.updatePadding(0, 0, 0, 0) - emulationActivity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE + binding.inGameMenu.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT + isInFoldableLayout = false + updateOrientation() + onConfigurationChanged(resources.configuration) } - binding.surfaceInputOverlay.requestLayout() - binding.inGameMenu.requestLayout() + binding.emulationContainer.requestLayout() + binding.inputContainer.requestLayout() binding.overlayContainer.requestLayout() + binding.inGameMenu.requestLayout() } override fun surfaceCreated(holder: SurfaceHolder) { @@ -385,7 +485,19 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { popup.show() } + @SuppressLint("SourceLockedOrientationActivity") private fun startConfiguringControls() { + // Lock the current orientation to prevent editing inconsistencies + if (IntSetting.RENDERER_SCREEN_LAYOUT.int == Settings.LayoutOption_Unspecified) { + emulationActivity?.let { + it.requestedOrientation = + if (resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) { + ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT + } else { + ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE + } + } + } binding.doneControlConfig.visibility = View.VISIBLE binding.surfaceInputOverlay.setIsInEditMode(true) } @@ -393,6 +505,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { private fun stopConfiguringControls() { binding.doneControlConfig.visibility = View.GONE binding.surfaceInputOverlay.setIsInEditMode(false) + // Unlock the orientation if it was locked for editing + if (IntSetting.RENDERER_SCREEN_LAYOUT.int == Settings.LayoutOption_Unspecified) { + emulationActivity?.let { + it.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED + } + } } @SuppressLint("SetTextI18n") @@ -402,18 +520,22 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { inputScaleSlider.apply { valueTo = 150F value = preferences.getInt(Settings.PREF_CONTROL_SCALE, 50).toFloat() - addOnChangeListener(Slider.OnChangeListener { _, value, _ -> - inputScaleValue.text = "${value.toInt()}%" - setControlScale(value.toInt()) - }) + addOnChangeListener( + Slider.OnChangeListener { _, value, _ -> + inputScaleValue.text = "${value.toInt()}%" + setControlScale(value.toInt()) + } + ) } inputOpacitySlider.apply { valueTo = 100F value = preferences.getInt(Settings.PREF_CONTROL_OPACITY, 100).toFloat() - addOnChangeListener(Slider.OnChangeListener { _, value, _ -> - inputOpacityValue.text = "${value.toInt()}%" - setControlOpacity(value.toInt()) - }) + addOnChangeListener( + Slider.OnChangeListener { _, value, _ -> + inputOpacityValue.text = "${value.toInt()}%" + setControlOpacity(value.toInt()) + } + ) } inputScaleValue.text = "${inputScaleSlider.value.toInt()}%" inputOpacityValue.text = "${inputOpacitySlider.value.toInt()}%" @@ -445,7 +567,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { } private fun setInsets() { - ViewCompat.setOnApplyWindowInsetsListener(binding.inGameMenu) { v: View, windowInsets: WindowInsetsCompat -> + ViewCompat.setOnApplyWindowInsetsListener( + binding.inGameMenu + ) { v: View, windowInsets: WindowInsetsCompat -> val cutInsets: Insets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout()) var left = 0 var right = 0 @@ -565,8 +689,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { state = State.PAUSED } - State.PAUSED -> Log.warning("[EmulationFragment] Surface cleared while emulation paused.") - else -> Log.warning("[EmulationFragment] Surface cleared while emulation stopped.") + State.PAUSED -> Log.warning( + "[EmulationFragment] Surface cleared while emulation paused." + ) + else -> Log.warning( + "[EmulationFragment] Surface cleared while emulation stopped." + ) } } } @@ -586,7 +714,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { State.PAUSED -> { Log.debug("[EmulationFragment] Resuming emulation.") NativeLibrary.surfaceChanged(surface) - NativeLibrary.unPauseEmulation() + NativeLibrary.unpauseEmulation() } else -> Log.debug("[EmulationFragment] Bug, run called while already running.") @@ -601,13 +729,5 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { companion object { private val perfStatsUpdateHandler = Handler(Looper.myLooper()!!) - - fun newInstance(game: Game): EmulationFragment { - val args = Bundle() - args.putParcelable(EmulationActivity.EXTRA_SELECTED_GAME, game) - val fragment = EmulationFragment() - fragment.arguments = args - return fragment - } } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt index bdc3375016..5a36ffad4b 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt @@ -68,67 +68,109 @@ class HomeSettingsFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { mainActivity = requireActivity() as MainActivity - val optionsList: MutableList<HomeSetting> = mutableListOf( - HomeSetting( - R.string.advanced_settings, - R.string.settings_description, - R.drawable.ic_settings - ) { SettingsActivity.launch(requireContext(), SettingsFile.FILE_NAME_CONFIG, "") }, - HomeSetting( - R.string.open_user_folder, - R.string.open_user_folder_description, - R.drawable.ic_folder_open - ) { openFileManager() }, - HomeSetting( - R.string.preferences_theme, - R.string.theme_and_color_description, - R.drawable.ic_palette - ) { SettingsActivity.launch(requireContext(), Settings.SECTION_THEME, "") }, - HomeSetting( - R.string.install_gpu_driver, - R.string.install_gpu_driver_description, - R.drawable.ic_exit - ) { driverInstaller() }, - HomeSetting( - R.string.install_amiibo_keys, - R.string.install_amiibo_keys_description, - R.drawable.ic_nfc - ) { mainActivity.getAmiiboKey.launch(arrayOf("*/*")) }, - HomeSetting( - R.string.select_games_folder, - R.string.select_games_folder_description, - R.drawable.ic_add - ) { mainActivity.getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data) }, - HomeSetting( - R.string.manage_save_data, - R.string.import_export_saves_description, - R.drawable.ic_save - ) { ImportExportSavesFragment().show(parentFragmentManager, ImportExportSavesFragment.TAG) }, - HomeSetting( - R.string.install_prod_keys, - R.string.install_prod_keys_description, - R.drawable.ic_unlock - ) { mainActivity.getProdKey.launch(arrayOf("*/*")) }, - HomeSetting( - R.string.install_firmware, - R.string.install_firmware_description, - R.drawable.ic_firmware - ) { mainActivity.getFirmware.launch(arrayOf("application/zip")) }, - HomeSetting( - R.string.share_log, - R.string.share_log_description, - R.drawable.ic_log - ) { shareLog() }, - HomeSetting( - R.string.about, - R.string.about_description, - R.drawable.ic_info_outline - ) { - exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true) - parentFragmentManager.primaryNavigationFragment?.findNavController() - ?.navigate(R.id.action_homeSettingsFragment_to_aboutFragment) + val optionsList: MutableList<HomeSetting> = mutableListOf<HomeSetting>().apply { + add( + HomeSetting( + R.string.advanced_settings, + R.string.settings_description, + R.drawable.ic_settings + ) { SettingsActivity.launch(requireContext(), SettingsFile.FILE_NAME_CONFIG, "") } + ) + add( + HomeSetting( + R.string.open_user_folder, + R.string.open_user_folder_description, + R.drawable.ic_folder_open + ) { openFileManager() } + ) + add( + HomeSetting( + R.string.preferences_theme, + R.string.theme_and_color_description, + R.drawable.ic_palette + ) { SettingsActivity.launch(requireContext(), Settings.SECTION_THEME, "") } + ) + + if (GpuDriverHelper.supportsCustomDriverLoading()) { + add( + HomeSetting( + R.string.install_gpu_driver, + R.string.install_gpu_driver_description, + R.drawable.ic_exit + ) { driverInstaller() } + ) } - ) + + add( + HomeSetting( + R.string.install_amiibo_keys, + R.string.install_amiibo_keys_description, + R.drawable.ic_nfc + ) { mainActivity.getAmiiboKey.launch(arrayOf("*/*")) } + ) + add( + HomeSetting( + R.string.install_game_content, + R.string.install_game_content_description, + R.drawable.ic_system_update_alt + ) { mainActivity.installGameUpdate.launch(arrayOf("*/*")) } + ) + add( + HomeSetting( + R.string.select_games_folder, + R.string.select_games_folder_description, + R.drawable.ic_add + ) { + mainActivity.getGamesDirectory.launch( + Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data + ) + } + ) + add( + HomeSetting( + R.string.manage_save_data, + R.string.import_export_saves_description, + R.drawable.ic_save + ) { + ImportExportSavesFragment().show( + parentFragmentManager, + ImportExportSavesFragment.TAG + ) + } + ) + add( + HomeSetting( + R.string.install_prod_keys, + R.string.install_prod_keys_description, + R.drawable.ic_unlock + ) { mainActivity.getProdKey.launch(arrayOf("*/*")) } + ) + add( + HomeSetting( + R.string.install_firmware, + R.string.install_firmware_description, + R.drawable.ic_firmware + ) { mainActivity.getFirmware.launch(arrayOf("application/zip")) } + ) + add( + HomeSetting( + R.string.share_log, + R.string.share_log_description, + R.drawable.ic_log + ) { shareLog() } + ) + add( + HomeSetting( + R.string.about, + R.string.about_description, + R.drawable.ic_info_outline + ) { + exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true) + parentFragmentManager.primaryNavigationFragment?.findNavController() + ?.navigate(R.id.action_homeSettingsFragment_to_aboutFragment) + } + ) + } if (!BuildConfig.PREMIUM) { optionsList.add( @@ -215,7 +257,11 @@ class HomeSettingsFragment : Fragment() { val intent = Intent(action) intent.addCategory(Intent.CATEGORY_DEFAULT) intent.data = DocumentsContract.buildRootUri(authority, DocumentProvider.ROOT_ID) - intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION or Intent.FLAG_GRANT_PREFIX_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION) + intent.addFlags( + Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION or + Intent.FLAG_GRANT_PREFIX_URI_PERMISSION or + Intent.FLAG_GRANT_WRITE_URI_PERMISSION + ) return intent } @@ -297,7 +343,9 @@ class HomeSettingsFragment : Fragment() { } private fun setInsets() = - ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view: View, windowInsets: WindowInsetsCompat -> + ViewCompat.setOnApplyWindowInsetsListener( + binding.root + ) { view: View, windowInsets: WindowInsetsCompat -> val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout()) val spacingNavigation = resources.getDimensionPixelSize(R.dimen.spacing_navigation) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ImportExportSavesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ImportExportSavesFragment.kt index 36e63bb9e6..e1495ee8c4 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ImportExportSavesFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ImportExportSavesFragment.kt @@ -15,6 +15,14 @@ import androidx.appcompat.app.AppCompatActivity import androidx.documentfile.provider.DocumentFile import androidx.fragment.app.DialogFragment import com.google.android.material.dialog.MaterialAlertDialogBuilder +import java.io.BufferedOutputStream +import java.io.File +import java.io.FileOutputStream +import java.io.FilenameFilter +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter +import java.util.zip.ZipEntry +import java.util.zip.ZipOutputStream import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -24,14 +32,6 @@ import org.yuzu.yuzu_emu.YuzuApplication import org.yuzu.yuzu_emu.features.DocumentProvider import org.yuzu.yuzu_emu.getPublicFilesDir import org.yuzu.yuzu_emu.utils.FileUtil -import java.io.BufferedOutputStream -import java.io.File -import java.io.FileOutputStream -import java.io.FilenameFilter -import java.time.LocalDateTime -import java.time.format.DateTimeFormatter -import java.util.zip.ZipEntry -import java.util.zip.ZipOutputStream class ImportExportSavesFragment : DialogFragment() { private val context = YuzuApplication.appContext @@ -98,7 +98,7 @@ class ImportExportSavesFragment : DialogFragment() { val outputZipFile = File( tempFolder, "yuzu saves - ${ - LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")) + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")) }.zip" ) outputZipFile.createNewFile() @@ -106,12 +106,14 @@ class ImportExportSavesFragment : DialogFragment() { saveFolder.walkTopDown().forEach { file -> val zipFileName = file.absolutePath.removePrefix(savesFolderRoot).removePrefix("/") - if (zipFileName == "") + if (zipFileName == "") { return@forEach + } val entry = ZipEntry("$zipFileName${(if (file.isDirectory) "/" else "")}") zos.putNextEntry(entry) - if (file.isFile) + if (file.isFile) { file.inputStream().use { fis -> fis.copyTo(zos) } + } } } lastZipCreated = outputZipFile @@ -137,7 +139,8 @@ class ImportExportSavesFragment : DialogFragment() { withContext(Dispatchers.Main) { val file = DocumentFile.fromSingleUri( - context, DocumentsContract.buildDocumentUri( + context, + DocumentsContract.buildDocumentUri( DocumentProvider.AUTHORITY, "${DocumentProvider.ROOT_ID}/temp/${lastZipFile.name}" ) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt index c7880d8ccd..739b26f995 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt @@ -14,7 +14,6 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding import org.yuzu.yuzu_emu.model.TaskViewModel - class IndeterminateProgressDialogFragment : DialogFragment() { private val taskViewModel: TaskViewModel by activityViewModels() diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LicensesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LicensesFragment.kt index 59141e8236..b6e9129f7d 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LicensesFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LicensesFragment.kt @@ -113,7 +113,9 @@ class LicensesFragment : Fragment() { } private fun setInsets() = - ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _: View, windowInsets: WindowInsetsCompat -> + ViewCompat.setOnApplyWindowInsetsListener( + binding.root + ) { _: View, windowInsets: WindowInsetsCompat -> val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout()) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LongMessageDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LongMessageDialogFragment.kt new file mode 100644 index 0000000000..b29b627e9e --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LongMessageDialogFragment.kt @@ -0,0 +1,62 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.fragments + +import android.app.Dialog +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import androidx.fragment.app.DialogFragment +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import org.yuzu.yuzu_emu.R + +class LongMessageDialogFragment : DialogFragment() { + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + val titleId = requireArguments().getInt(TITLE) + val description = requireArguments().getString(DESCRIPTION) + val helpLinkId = requireArguments().getInt(HELP_LINK) + + val dialog = MaterialAlertDialogBuilder(requireContext()) + .setPositiveButton(R.string.close, null) + .setTitle(titleId) + .setMessage(description) + + if (helpLinkId != 0) { + dialog.setNeutralButton(R.string.learn_more) { _, _ -> + openLink(getString(helpLinkId)) + } + } + + return dialog.show() + } + + private fun openLink(link: String) { + val intent = Intent(Intent.ACTION_VIEW, Uri.parse(link)) + startActivity(intent) + } + + companion object { + const val TAG = "LongMessageDialogFragment" + + private const val TITLE = "Title" + private const val DESCRIPTION = "Description" + private const val HELP_LINK = "Link" + + fun newInstance( + titleId: Int, + description: String, + helpLinkId: Int = 0 + ): LongMessageDialogFragment { + val dialog = LongMessageDialogFragment() + val bundle = Bundle() + bundle.apply { + putInt(TITLE, titleId) + putString(DESCRIPTION, description) + putInt(HELP_LINK, helpLinkId) + } + dialog.arguments = bundle + return dialog + } + } +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt index adbe3696b4..f54dccc69d 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt @@ -20,6 +20,7 @@ import androidx.fragment.app.activityViewModels import androidx.preference.PreferenceManager import info.debatty.java.stringsimilarity.Jaccard import info.debatty.java.stringsimilarity.JaroWinkler +import java.util.Locale import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.YuzuApplication import org.yuzu.yuzu_emu.adapters.GameAdapter @@ -28,9 +29,6 @@ import org.yuzu.yuzu_emu.layout.AutofitGridLayoutManager import org.yuzu.yuzu_emu.model.Game import org.yuzu.yuzu_emu.model.GamesViewModel import org.yuzu.yuzu_emu.model.HomeViewModel -import org.yuzu.yuzu_emu.utils.FileUtil -import org.yuzu.yuzu_emu.utils.Log -import java.util.Locale class SearchFragment : Fragment() { private var _binding: FragmentSearchBinding? = null @@ -129,16 +127,13 @@ class SearchFragment : Fragment() { R.id.chip_homebrew -> baseList.filter { it.isHomebrew } - R.id.chip_retail -> baseList.filter { - FileUtil.hasExtension(it.path, "xci") - || FileUtil.hasExtension(it.path, "nsp") - } + R.id.chip_retail -> baseList.filter { !it.isHomebrew } else -> baseList } - if (binding.searchText.text.toString().isEmpty() - && binding.chipGroup.checkedChipId != View.NO_ID + if (binding.searchText.text.toString().isEmpty() && + binding.chipGroup.checkedChipId != View.NO_ID ) { gamesViewModel.setSearchedGames(filteredList) return @@ -173,14 +168,16 @@ class SearchFragment : Fragment() { private fun focusSearch() { if (_binding != null) { binding.searchText.requestFocus() - val imm = - requireActivity().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager? + val imm = requireActivity() + .getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager? imm?.showSoftInput(binding.searchText, InputMethodManager.SHOW_IMPLICIT) } } private fun setInsets() = - ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view: View, windowInsets: WindowInsetsCompat -> + ViewCompat.setOnApplyWindowInsetsListener( + binding.root + ) { view: View, windowInsets: WindowInsetsCompat -> val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout()) val extraListSpacing = resources.getDimensionPixelSize(R.dimen.spacing_med) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt index 2587733800..6c4ddaf6b6 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt @@ -25,6 +25,7 @@ import androidx.navigation.findNavController import androidx.preference.PreferenceManager import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback import com.google.android.material.transition.MaterialFadeThrough +import java.io.File import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.YuzuApplication import org.yuzu.yuzu_emu.adapters.SetupAdapter @@ -35,7 +36,6 @@ import org.yuzu.yuzu_emu.model.SetupPage import org.yuzu.yuzu_emu.ui.main.MainActivity import org.yuzu.yuzu_emu.utils.DirectoryInitialization import org.yuzu.yuzu_emu.utils.GameHelper -import java.io.File class SetupFragment : Fragment() { private var _binding: FragmentSetupBinding? = null @@ -82,7 +82,8 @@ class SetupFragment : Fragment() { requireActivity().finish() } } - }) + } + ) requireActivity().window.navigationBarColor = ContextCompat.getColor(requireContext(), android.R.color.transparent) @@ -148,14 +149,20 @@ class SetupFragment : Fragment() { R.drawable.ic_add, true, R.string.add_games, - { mainActivity.getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data) }, + { + mainActivity.getGamesDirectory.launch( + Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data + ) + }, true, R.string.add_games_warning, R.string.add_games_warning_description, R.string.add_games_warning_help, { val preferences = - PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) + PreferenceManager.getDefaultSharedPreferences( + YuzuApplication.appContext + ) preferences.getString(GameHelper.KEY_GAME_PATH, "")!!.isNotEmpty() } ) @@ -260,7 +267,9 @@ class SetupFragment : Fragment() { @RequiresApi(Build.VERSION_CODES.TIRAMISU) private val permissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { - if (!it && !shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)) { + if (!it && + !shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS) + ) { PermissionDeniedDialogFragment().show( childFragmentManager, PermissionDeniedDialogFragment.TAG @@ -315,7 +324,9 @@ class SetupFragment : Fragment() { } private fun setInsets() = - ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view: View, windowInsets: WindowInsetsCompat -> + ViewCompat.setOnApplyWindowInsetsListener( + binding.root + ) { view: View, windowInsets: WindowInsetsCompat -> val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout()) view.setPadding( diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/layout/AutofitGridLayoutManager.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/layout/AutofitGridLayoutManager.kt index be5e4c86c7..bdd6ea628f 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/layout/AutofitGridLayoutManager.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/layout/AutofitGridLayoutManager.kt @@ -44,7 +44,9 @@ class AutofitGridLayoutManager( override fun onLayoutChildren(recycler: Recycler, state: RecyclerView.State) { val width = width val height = height - if (columnWidth > 0 && width > 0 && height > 0 && (isColumnWidthChanged || lastWidth != width || lastHeight != height)) { + if (columnWidth > 0 && width > 0 && height > 0 && + (isColumnWidthChanged || lastWidth != width || lastHeight != height) + ) { val totalSpace: Int = if (orientation == VERTICAL) { width - paddingRight - paddingLeft } else { diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt index 3d6782c499..6527c64ab5 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt @@ -4,9 +4,9 @@ package org.yuzu.yuzu_emu.model import android.os.Parcelable +import java.util.HashSet import kotlinx.parcelize.Parcelize import kotlinx.serialization.Serializable -import java.util.HashSet @Parcelize @Serializable @@ -23,21 +23,27 @@ class Game( val keyLastPlayedTime get() = "${gameId}_LastPlayed" override fun equals(other: Any?): Boolean { - if (other !is Game) + if (other !is Game) { return false + } + + return hashCode() == other.hashCode() + } - return title == other.title - && description == other.description - && regions == other.regions - && path == other.path - && gameId == other.gameId - && company == other.company - && isHomebrew == other.isHomebrew + override fun hashCode(): Int { + var result = title.hashCode() + result = 31 * result + description.hashCode() + result = 31 * result + regions.hashCode() + result = 31 * result + path.hashCode() + result = 31 * result + gameId.hashCode() + result = 31 * result + company.hashCode() + result = 31 * result + isHomebrew.hashCode() + return result } companion object { val extensions: Set<String> = HashSet( - listOf(".xci", ".nsp", ".nca", ".nro") + listOf("xci", "nsp", "nca", "nro") ) } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt index d9b301210d..1fe42f9229 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt @@ -10,6 +10,7 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import androidx.preference.PreferenceManager +import java.util.Locale import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -20,7 +21,6 @@ import kotlinx.serialization.json.Json import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.YuzuApplication import org.yuzu.yuzu_emu.utils.GameHelper -import java.util.Locale @OptIn(ExperimentalSerializationApi::class) class GamesViewModel : ViewModel() { @@ -99,8 +99,9 @@ class GamesViewModel : ViewModel() { } fun reloadGames(directoryChanged: Boolean) { - if (isReloading.value == true) + if (isReloading.value == true) { return + } _isReloading.postValue(true) viewModelScope.launch { diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt index aa424c768e..6251ec7838 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt @@ -6,7 +6,6 @@ package org.yuzu.yuzu_emu.overlay import android.app.Activity import android.content.Context import android.content.SharedPreferences -import android.content.res.Configuration import android.graphics.Bitmap import android.graphics.Canvas import android.graphics.Point @@ -24,6 +23,8 @@ import android.view.WindowInsets import androidx.core.content.ContextCompat import androidx.preference.PreferenceManager import androidx.window.layout.WindowMetricsCalculator +import kotlin.math.max +import kotlin.math.min import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.NativeLibrary.ButtonType import org.yuzu.yuzu_emu.NativeLibrary.StickType @@ -31,14 +32,13 @@ import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.YuzuApplication import org.yuzu.yuzu_emu.features.settings.model.Settings import org.yuzu.yuzu_emu.utils.EmulationMenuSettings -import kotlin.math.max -import kotlin.math.min /** * Draws the interactive input overlay on top of the * [SurfaceView] that is rendering emulation. */ -class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context, attrs), +class InputOverlay(context: Context, attrs: AttributeSet?) : + SurfaceView(context, attrs), OnTouchListener { private val overlayButtons: MutableSet<InputOverlayDrawableButton> = HashSet() private val overlayDpads: MutableSet<InputOverlayDrawableDpad> = HashSet() @@ -51,12 +51,14 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context private lateinit var windowInsets: WindowInsets + var orientation = LANDSCAPE + override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { super.onLayout(changed, left, top, right, bottom) windowInsets = rootWindowInsets - if (!preferences.getBoolean(Settings.PREF_OVERLAY_INIT, false)) { + if (!preferences.getBoolean("${Settings.PREF_OVERLAY_INIT}$orientation", false)) { defaultOverlay() } @@ -93,7 +95,11 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context var shouldUpdateView = false val playerIndex = - if (NativeLibrary.isHandheldOnly()) NativeLibrary.ConsoleDevice else NativeLibrary.Player1Device + if (NativeLibrary.isHandheldOnly()) { + NativeLibrary.ConsoleDevice + } else { + NativeLibrary.Player1Device + } for (button in overlayButtons) { if (!button.updateStatus(event)) { @@ -156,8 +162,9 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context shouldUpdateView = true } - if (shouldUpdateView) + if (shouldUpdateView) { invalidate() + } if (!preferences.getBoolean(Settings.PREF_TOUCH_ENABLED, true)) { return true @@ -233,10 +240,6 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context val fingerPositionX = event.getX(pointerIndex).toInt() val fingerPositionY = event.getY(pointerIndex).toInt() - // TODO: Provide support for portrait layout - //val orientation = - // if (resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) "-Portrait" else "" - for (button in overlayButtons) { // Determine the button state to apply based on the MotionEvent action flag. when (event.action and MotionEvent.ACTION_MASK) { @@ -245,9 +248,9 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context // If no button is being moved now, remember the currently touched button to move. if (buttonBeingConfigured == null && button.bounds.contains( - fingerPositionX, - fingerPositionY - ) + fingerPositionX, + fingerPositionY + ) ) { buttonBeingConfigured = button buttonBeingConfigured!!.onConfigureTouch(event) @@ -266,7 +269,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context buttonBeingConfigured!!.buttonId, buttonBeingConfigured!!.bounds.centerX(), buttonBeingConfigured!!.bounds.centerY(), - "" + orientation ) buttonBeingConfigured = null } @@ -299,7 +302,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context dpadBeingConfigured!!.upId, dpadBeingConfigured!!.bounds.centerX(), dpadBeingConfigured!!.bounds.centerY(), - "" + orientation ) dpadBeingConfigured = null } @@ -311,9 +314,9 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_DOWN -> if (joystickBeingConfigured == null && joystick.bounds.contains( - fingerPositionX, - fingerPositionY - ) + fingerPositionX, + fingerPositionY + ) ) { joystickBeingConfigured = joystick joystickBeingConfigured!!.onConfigureTouch(event) @@ -330,7 +333,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context joystickBeingConfigured!!.buttonId, joystickBeingConfigured!!.bounds.centerX(), joystickBeingConfigured!!.bounds.centerY(), - "" + orientation ) joystickBeingConfigured = null } @@ -533,8 +536,6 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context overlayButtons.clear() overlayDpads.clear() overlayJoysticks.clear() - val orientation = - if (resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) "-Portrait" else "" // Add all the enabled overlay items back to the HashSet. if (EmulationMenuSettings.showOverlay) { @@ -548,8 +549,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context val min = windowSize.first val max = windowSize.second PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext).edit() - .putFloat("$sharedPrefsId$orientation-X", (x - min.x).toFloat() / max.x) - .putFloat("$sharedPrefsId$orientation-Y", (y - min.y).toFloat() / max.y) + .putFloat("$sharedPrefsId-X$orientation", (x - min.x).toFloat() / max.x) + .putFloat("$sharedPrefsId-Y$orientation", (y - min.y).toFloat() / max.y) .apply() } @@ -558,145 +559,250 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context } private fun defaultOverlay() { - if (!preferences.getBoolean(Settings.PREF_OVERLAY_INIT, false)) { - defaultOverlayLandscape() + if (!preferences.getBoolean("${Settings.PREF_OVERLAY_INIT}$orientation", false)) { + defaultOverlayByLayout(orientation) } resetButtonPlacement() preferences.edit() - .putBoolean(Settings.PREF_OVERLAY_INIT, true) + .putBoolean("${Settings.PREF_OVERLAY_INIT}$orientation", true) .apply() } fun resetButtonPlacement() { - defaultOverlayLandscape() + defaultOverlayByLayout(orientation) refreshControls() } - private fun defaultOverlayLandscape() { + private val landscapeResources = arrayOf( + R.integer.SWITCH_BUTTON_A_X, + R.integer.SWITCH_BUTTON_A_Y, + R.integer.SWITCH_BUTTON_B_X, + R.integer.SWITCH_BUTTON_B_Y, + R.integer.SWITCH_BUTTON_X_X, + R.integer.SWITCH_BUTTON_X_Y, + R.integer.SWITCH_BUTTON_Y_X, + R.integer.SWITCH_BUTTON_Y_Y, + R.integer.SWITCH_TRIGGER_ZL_X, + R.integer.SWITCH_TRIGGER_ZL_Y, + R.integer.SWITCH_TRIGGER_ZR_X, + R.integer.SWITCH_TRIGGER_ZR_Y, + R.integer.SWITCH_BUTTON_DPAD_X, + R.integer.SWITCH_BUTTON_DPAD_Y, + R.integer.SWITCH_TRIGGER_L_X, + R.integer.SWITCH_TRIGGER_L_Y, + R.integer.SWITCH_TRIGGER_R_X, + R.integer.SWITCH_TRIGGER_R_Y, + R.integer.SWITCH_BUTTON_PLUS_X, + R.integer.SWITCH_BUTTON_PLUS_Y, + R.integer.SWITCH_BUTTON_MINUS_X, + R.integer.SWITCH_BUTTON_MINUS_Y, + R.integer.SWITCH_BUTTON_HOME_X, + R.integer.SWITCH_BUTTON_HOME_Y, + R.integer.SWITCH_BUTTON_CAPTURE_X, + R.integer.SWITCH_BUTTON_CAPTURE_Y, + R.integer.SWITCH_STICK_R_X, + R.integer.SWITCH_STICK_R_Y, + R.integer.SWITCH_STICK_L_X, + R.integer.SWITCH_STICK_L_Y + ) + + private val portraitResources = arrayOf( + R.integer.SWITCH_BUTTON_A_X_PORTRAIT, + R.integer.SWITCH_BUTTON_A_Y_PORTRAIT, + R.integer.SWITCH_BUTTON_B_X_PORTRAIT, + R.integer.SWITCH_BUTTON_B_Y_PORTRAIT, + R.integer.SWITCH_BUTTON_X_X_PORTRAIT, + R.integer.SWITCH_BUTTON_X_Y_PORTRAIT, + R.integer.SWITCH_BUTTON_Y_X_PORTRAIT, + R.integer.SWITCH_BUTTON_Y_Y_PORTRAIT, + R.integer.SWITCH_TRIGGER_ZL_X_PORTRAIT, + R.integer.SWITCH_TRIGGER_ZL_Y_PORTRAIT, + R.integer.SWITCH_TRIGGER_ZR_X_PORTRAIT, + R.integer.SWITCH_TRIGGER_ZR_Y_PORTRAIT, + R.integer.SWITCH_BUTTON_DPAD_X_PORTRAIT, + R.integer.SWITCH_BUTTON_DPAD_Y_PORTRAIT, + R.integer.SWITCH_TRIGGER_L_X_PORTRAIT, + R.integer.SWITCH_TRIGGER_L_Y_PORTRAIT, + R.integer.SWITCH_TRIGGER_R_X_PORTRAIT, + R.integer.SWITCH_TRIGGER_R_Y_PORTRAIT, + R.integer.SWITCH_BUTTON_PLUS_X_PORTRAIT, + R.integer.SWITCH_BUTTON_PLUS_Y_PORTRAIT, + R.integer.SWITCH_BUTTON_MINUS_X_PORTRAIT, + R.integer.SWITCH_BUTTON_MINUS_Y_PORTRAIT, + R.integer.SWITCH_BUTTON_HOME_X_PORTRAIT, + R.integer.SWITCH_BUTTON_HOME_Y_PORTRAIT, + R.integer.SWITCH_BUTTON_CAPTURE_X_PORTRAIT, + R.integer.SWITCH_BUTTON_CAPTURE_Y_PORTRAIT, + R.integer.SWITCH_STICK_R_X_PORTRAIT, + R.integer.SWITCH_STICK_R_Y_PORTRAIT, + R.integer.SWITCH_STICK_L_X_PORTRAIT, + R.integer.SWITCH_STICK_L_Y_PORTRAIT + ) + + private val foldableResources = arrayOf( + R.integer.SWITCH_BUTTON_A_X_FOLDABLE, + R.integer.SWITCH_BUTTON_A_Y_FOLDABLE, + R.integer.SWITCH_BUTTON_B_X_FOLDABLE, + R.integer.SWITCH_BUTTON_B_Y_FOLDABLE, + R.integer.SWITCH_BUTTON_X_X_FOLDABLE, + R.integer.SWITCH_BUTTON_X_Y_FOLDABLE, + R.integer.SWITCH_BUTTON_Y_X_FOLDABLE, + R.integer.SWITCH_BUTTON_Y_Y_FOLDABLE, + R.integer.SWITCH_TRIGGER_ZL_X_FOLDABLE, + R.integer.SWITCH_TRIGGER_ZL_Y_FOLDABLE, + R.integer.SWITCH_TRIGGER_ZR_X_FOLDABLE, + R.integer.SWITCH_TRIGGER_ZR_Y_FOLDABLE, + R.integer.SWITCH_BUTTON_DPAD_X_FOLDABLE, + R.integer.SWITCH_BUTTON_DPAD_Y_FOLDABLE, + R.integer.SWITCH_TRIGGER_L_X_FOLDABLE, + R.integer.SWITCH_TRIGGER_L_Y_FOLDABLE, + R.integer.SWITCH_TRIGGER_R_X_FOLDABLE, + R.integer.SWITCH_TRIGGER_R_Y_FOLDABLE, + R.integer.SWITCH_BUTTON_PLUS_X_FOLDABLE, + R.integer.SWITCH_BUTTON_PLUS_Y_FOLDABLE, + R.integer.SWITCH_BUTTON_MINUS_X_FOLDABLE, + R.integer.SWITCH_BUTTON_MINUS_Y_FOLDABLE, + R.integer.SWITCH_BUTTON_HOME_X_FOLDABLE, + R.integer.SWITCH_BUTTON_HOME_Y_FOLDABLE, + R.integer.SWITCH_BUTTON_CAPTURE_X_FOLDABLE, + R.integer.SWITCH_BUTTON_CAPTURE_Y_FOLDABLE, + R.integer.SWITCH_STICK_R_X_FOLDABLE, + R.integer.SWITCH_STICK_R_Y_FOLDABLE, + R.integer.SWITCH_STICK_L_X_FOLDABLE, + R.integer.SWITCH_STICK_L_Y_FOLDABLE + ) + + private fun getResourceValue(orientation: String, position: Int): Float { + return when (orientation) { + PORTRAIT -> resources.getInteger(portraitResources[position]).toFloat() / 1000 + FOLDABLE -> resources.getInteger(foldableResources[position]).toFloat() / 1000 + else -> resources.getInteger(landscapeResources[position]).toFloat() / 1000 + } + } + + private fun defaultOverlayByLayout(orientation: String) { // Each value represents the position of the button in relation to the screen size without insets. preferences.edit() .putFloat( - ButtonType.BUTTON_A.toString() + "-X", - resources.getInteger(R.integer.SWITCH_BUTTON_A_X).toFloat() / 1000 + ButtonType.BUTTON_A.toString() + "-X$orientation", + getResourceValue(orientation, 0) ) .putFloat( - ButtonType.BUTTON_A.toString() + "-Y", - resources.getInteger(R.integer.SWITCH_BUTTON_A_Y).toFloat() / 1000 + ButtonType.BUTTON_A.toString() + "-Y$orientation", + getResourceValue(orientation, 1) ) .putFloat( - ButtonType.BUTTON_B.toString() + "-X", - resources.getInteger(R.integer.SWITCH_BUTTON_B_X).toFloat() / 1000 + ButtonType.BUTTON_B.toString() + "-X$orientation", + getResourceValue(orientation, 2) ) .putFloat( - ButtonType.BUTTON_B.toString() + "-Y", - resources.getInteger(R.integer.SWITCH_BUTTON_B_Y).toFloat() / 1000 + ButtonType.BUTTON_B.toString() + "-Y$orientation", + getResourceValue(orientation, 3) ) .putFloat( - ButtonType.BUTTON_X.toString() + "-X", - resources.getInteger(R.integer.SWITCH_BUTTON_X_X).toFloat() / 1000 + ButtonType.BUTTON_X.toString() + "-X$orientation", + getResourceValue(orientation, 4) ) .putFloat( - ButtonType.BUTTON_X.toString() + "-Y", - resources.getInteger(R.integer.SWITCH_BUTTON_X_Y).toFloat() / 1000 + ButtonType.BUTTON_X.toString() + "-Y$orientation", + getResourceValue(orientation, 5) ) .putFloat( - ButtonType.BUTTON_Y.toString() + "-X", - resources.getInteger(R.integer.SWITCH_BUTTON_Y_X).toFloat() / 1000 + ButtonType.BUTTON_Y.toString() + "-X$orientation", + getResourceValue(orientation, 6) ) .putFloat( - ButtonType.BUTTON_Y.toString() + "-Y", - resources.getInteger(R.integer.SWITCH_BUTTON_Y_Y).toFloat() / 1000 + ButtonType.BUTTON_Y.toString() + "-Y$orientation", + getResourceValue(orientation, 7) ) .putFloat( - ButtonType.TRIGGER_ZL.toString() + "-X", - resources.getInteger(R.integer.SWITCH_TRIGGER_ZL_X).toFloat() / 1000 + ButtonType.TRIGGER_ZL.toString() + "-X$orientation", + getResourceValue(orientation, 8) ) .putFloat( - ButtonType.TRIGGER_ZL.toString() + "-Y", - resources.getInteger(R.integer.SWITCH_TRIGGER_ZL_Y).toFloat() / 1000 + ButtonType.TRIGGER_ZL.toString() + "-Y$orientation", + getResourceValue(orientation, 9) ) .putFloat( - ButtonType.TRIGGER_ZR.toString() + "-X", - resources.getInteger(R.integer.SWITCH_TRIGGER_ZR_X).toFloat() / 1000 + ButtonType.TRIGGER_ZR.toString() + "-X$orientation", + getResourceValue(orientation, 10) ) .putFloat( - ButtonType.TRIGGER_ZR.toString() + "-Y", - resources.getInteger(R.integer.SWITCH_TRIGGER_ZR_Y).toFloat() / 1000 + ButtonType.TRIGGER_ZR.toString() + "-Y$orientation", + getResourceValue(orientation, 11) ) .putFloat( - ButtonType.DPAD_UP.toString() + "-X", - resources.getInteger(R.integer.SWITCH_BUTTON_DPAD_X).toFloat() / 1000 + ButtonType.DPAD_UP.toString() + "-X$orientation", + getResourceValue(orientation, 12) ) .putFloat( - ButtonType.DPAD_UP.toString() + "-Y", - resources.getInteger(R.integer.SWITCH_BUTTON_DPAD_Y).toFloat() / 1000 + ButtonType.DPAD_UP.toString() + "-Y$orientation", + getResourceValue(orientation, 13) ) .putFloat( - ButtonType.TRIGGER_L.toString() + "-X", - resources.getInteger(R.integer.SWITCH_TRIGGER_L_X).toFloat() / 1000 + ButtonType.TRIGGER_L.toString() + "-X$orientation", + getResourceValue(orientation, 14) ) .putFloat( - ButtonType.TRIGGER_L.toString() + "-Y", - resources.getInteger(R.integer.SWITCH_TRIGGER_L_Y).toFloat() / 1000 + ButtonType.TRIGGER_L.toString() + "-Y$orientation", + getResourceValue(orientation, 15) ) .putFloat( - ButtonType.TRIGGER_R.toString() + "-X", - resources.getInteger(R.integer.SWITCH_TRIGGER_R_X).toFloat() / 1000 + ButtonType.TRIGGER_R.toString() + "-X$orientation", + getResourceValue(orientation, 16) ) .putFloat( - ButtonType.TRIGGER_R.toString() + "-Y", - resources.getInteger(R.integer.SWITCH_TRIGGER_R_Y).toFloat() / 1000 + ButtonType.TRIGGER_R.toString() + "-Y$orientation", + getResourceValue(orientation, 17) ) .putFloat( - ButtonType.BUTTON_PLUS.toString() + "-X", - resources.getInteger(R.integer.SWITCH_BUTTON_PLUS_X).toFloat() / 1000 + ButtonType.BUTTON_PLUS.toString() + "-X$orientation", + getResourceValue(orientation, 18) ) .putFloat( - ButtonType.BUTTON_PLUS.toString() + "-Y", - resources.getInteger(R.integer.SWITCH_BUTTON_PLUS_Y).toFloat() / 1000 + ButtonType.BUTTON_PLUS.toString() + "-Y$orientation", + getResourceValue(orientation, 19) ) .putFloat( - ButtonType.BUTTON_MINUS.toString() + "-X", - resources.getInteger(R.integer.SWITCH_BUTTON_MINUS_X).toFloat() / 1000 + ButtonType.BUTTON_MINUS.toString() + "-X$orientation", + getResourceValue(orientation, 20) ) .putFloat( - ButtonType.BUTTON_MINUS.toString() + "-Y", - resources.getInteger(R.integer.SWITCH_BUTTON_MINUS_Y).toFloat() / 1000 + ButtonType.BUTTON_MINUS.toString() + "-Y$orientation", + getResourceValue(orientation, 21) ) .putFloat( - ButtonType.BUTTON_HOME.toString() + "-X", - resources.getInteger(R.integer.SWITCH_BUTTON_HOME_X).toFloat() / 1000 + ButtonType.BUTTON_HOME.toString() + "-X$orientation", + getResourceValue(orientation, 22) ) .putFloat( - ButtonType.BUTTON_HOME.toString() + "-Y", - resources.getInteger(R.integer.SWITCH_BUTTON_HOME_Y).toFloat() / 1000 + ButtonType.BUTTON_HOME.toString() + "-Y$orientation", + getResourceValue(orientation, 23) ) .putFloat( - ButtonType.BUTTON_CAPTURE.toString() + "-X", - resources.getInteger(R.integer.SWITCH_BUTTON_CAPTURE_X) - .toFloat() / 1000 + ButtonType.BUTTON_CAPTURE.toString() + "-X$orientation", + getResourceValue(orientation, 24) ) .putFloat( - ButtonType.BUTTON_CAPTURE.toString() + "-Y", - resources.getInteger(R.integer.SWITCH_BUTTON_CAPTURE_Y) - .toFloat() / 1000 + ButtonType.BUTTON_CAPTURE.toString() + "-Y$orientation", + getResourceValue(orientation, 25) ) .putFloat( - ButtonType.STICK_R.toString() + "-X", - resources.getInteger(R.integer.SWITCH_STICK_R_X).toFloat() / 1000 + ButtonType.STICK_R.toString() + "-X$orientation", + getResourceValue(orientation, 26) ) .putFloat( - ButtonType.STICK_R.toString() + "-Y", - resources.getInteger(R.integer.SWITCH_STICK_R_Y).toFloat() / 1000 + ButtonType.STICK_R.toString() + "-Y$orientation", + getResourceValue(orientation, 27) ) .putFloat( - ButtonType.STICK_L.toString() + "-X", - resources.getInteger(R.integer.SWITCH_STICK_L_X).toFloat() / 1000 + ButtonType.STICK_L.toString() + "-X$orientation", + getResourceValue(orientation, 28) ) .putFloat( - ButtonType.STICK_L.toString() + "-Y", - resources.getInteger(R.integer.SWITCH_STICK_L_Y).toFloat() / 1000 + ButtonType.STICK_L.toString() + "-Y$orientation", + getResourceValue(orientation, 29) ) .apply() } @@ -709,13 +815,17 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context private val preferences: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) + const val LANDSCAPE = "" + const val PORTRAIT = "_Portrait" + const val FOLDABLE = "_Foldable" + /** * Resizes a [Bitmap] by a given scale factor * * @param context Context for getting the vector drawable * @param drawableId The ID of the drawable to scale. * @param scale The scale factor for the bitmap. - * @return The scaled [Bitmap] + * @return The scaled [Bitmap] */ private fun getBitmap(context: Context, drawableId: Int, scale: Float): Bitmap { val vectorDrawable = ContextCompat.getDrawable(context, drawableId) as VectorDrawable @@ -749,14 +859,13 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context * Gets the safe screen size for drawing the overlay * * @param context Context for getting the window metrics - * @return A pair of points, the first being the top left corner of the safe area, + * @return A pair of points, the first being the top left corner of the safe area, * the second being the bottom right corner of the safe area */ private fun getSafeScreenSize(context: Context): Pair<Point, Point> { // Get screen size - val windowMetrics = - WindowMetricsCalculator.getOrCreate() - .computeCurrentWindowMetrics(context as Activity) + val windowMetrics = WindowMetricsCalculator.getOrCreate() + .computeCurrentWindowMetrics(context as Activity) var maxY = windowMetrics.bounds.height().toFloat() var maxX = windowMetrics.bounds.width().toFloat() var minY = 0 @@ -768,10 +877,16 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { val insets = context.windowManager.currentWindowMetrics.windowInsets.displayCutout if (insets != null) { - if (insets.boundingRectTop.bottom != 0 && insets.boundingRectTop.bottom > maxY / 2) - insets.boundingRectTop.bottom.toFloat() else maxY - if (insets.boundingRectRight.left != 0 && insets.boundingRectRight.left > maxX / 2) - insets.boundingRectRight.left.toFloat() else maxX + if (insets.boundingRectTop.bottom != 0 && + insets.boundingRectTop.bottom > maxY / 2 + ) { + maxY = insets.boundingRectTop.bottom.toFloat() + } + if (insets.boundingRectRight.left != 0 && + insets.boundingRectRight.left > maxX / 2 + ) { + maxX = insets.boundingRectRight.left.toFloat() + } minX = insets.boundingRectLeft.right - insets.boundingRectLeft.left minY = insets.boundingRectBottom.top - insets.boundingRectBottom.bottom @@ -878,8 +993,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context // The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay. // These were set in the input overlay configuration menu. - val xKey = "$buttonId$orientation-X" - val yKey = "$buttonId$orientation-Y" + val xKey = "$buttonId-X$orientation" + val yKey = "$buttonId-Y$orientation" val drawableXPercent = sPrefs.getFloat(xKey, 0f) val drawableYPercent = sPrefs.getFloat(yKey, 0f) val drawableX = (drawableXPercent * max.x + min.x).toInt() @@ -959,8 +1074,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context // The X and Y coordinates of the InputOverlayDrawableDpad on the InputOverlay. // These were set in the input overlay configuration menu. - val drawableXPercent = sPrefs.getFloat("${ButtonType.DPAD_UP}$orientation-X", 0f) - val drawableYPercent = sPrefs.getFloat("${ButtonType.DPAD_UP}$orientation-Y", 0f) + val drawableXPercent = sPrefs.getFloat("${ButtonType.DPAD_UP}-X$orientation", 0f) + val drawableYPercent = sPrefs.getFloat("${ButtonType.DPAD_UP}-Y$orientation", 0f) val drawableX = (drawableXPercent * max.x + min.x).toInt() val drawableY = (drawableYPercent * max.y + min.y).toInt() val width = overlayDrawable.width @@ -1026,8 +1141,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context // The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay. // These were set in the input overlay configuration menu. - val drawableXPercent = sPrefs.getFloat("$button$orientation-X", 0f) - val drawableYPercent = sPrefs.getFloat("$button$orientation-Y", 0f) + val drawableXPercent = sPrefs.getFloat("$button-X$orientation", 0f) + val drawableYPercent = sPrefs.getFloat("$button-Y$orientation", 0f) val drawableX = (drawableXPercent * max.x + min.x).toInt() val drawableY = (drawableYPercent * max.y + min.y).toInt() val outerScale = 1.66f diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableDpad.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableDpad.kt index 43d664d218..8aef6f5a5d 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableDpad.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableDpad.kt @@ -133,7 +133,10 @@ class InputOverlayDrawableDpad( downButtonState = axisY > VIRT_AXIS_DEADZONE leftButtonState = axisX < -VIRT_AXIS_DEADZONE rightButtonState = axisX > VIRT_AXIS_DEADZONE - return oldUpState != upButtonState || oldDownState != downButtonState || oldLeftState != leftButtonState || oldRightState != rightButtonState + return oldUpState != upButtonState || + oldDownState != downButtonState || + oldLeftState != leftButtonState || + oldRightState != rightButtonState } return false } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt index f1d32192a9..fb48f584db 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt @@ -9,12 +9,12 @@ import android.graphics.Canvas import android.graphics.Rect import android.graphics.drawable.BitmapDrawable import android.view.MotionEvent -import org.yuzu.yuzu_emu.NativeLibrary -import org.yuzu.yuzu_emu.utils.EmulationMenuSettings import kotlin.math.atan2 import kotlin.math.cos import kotlin.math.sin import kotlin.math.sqrt +import org.yuzu.yuzu_emu.NativeLibrary +import org.yuzu.yuzu_emu.utils.EmulationMenuSettings /** * Custom [BitmapDrawable] that is capable @@ -241,14 +241,22 @@ class InputOverlayDrawableJoystick( private fun setInnerBounds() { var x = virtBounds.centerX() + (xAxis * (virtBounds.width() / 2)).toInt() var y = virtBounds.centerY() + (yAxis * (virtBounds.height() / 2)).toInt() - if (x > virtBounds.centerX() + virtBounds.width() / 2) x = - virtBounds.centerX() + virtBounds.width() / 2 - if (x < virtBounds.centerX() - virtBounds.width() / 2) x = - virtBounds.centerX() - virtBounds.width() / 2 - if (y > virtBounds.centerY() + virtBounds.height() / 2) y = - virtBounds.centerY() + virtBounds.height() / 2 - if (y < virtBounds.centerY() - virtBounds.height() / 2) y = - virtBounds.centerY() - virtBounds.height() / 2 + if (x > virtBounds.centerX() + virtBounds.width() / 2) { + x = + virtBounds.centerX() + virtBounds.width() / 2 + } + if (x < virtBounds.centerX() - virtBounds.width() / 2) { + x = + virtBounds.centerX() - virtBounds.width() / 2 + } + if (y > virtBounds.centerY() + virtBounds.height() / 2) { + y = + virtBounds.centerY() + virtBounds.height() / 2 + } + if (y < virtBounds.centerY() - virtBounds.height() / 2) { + y = + virtBounds.centerY() - virtBounds.height() / 2 + } val width = pressedStateInnerBitmap.bounds.width() / 2 val height = pressedStateInnerBitmap.bounds.height() / 2 defaultStateInnerBitmap.setBounds( diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt index 97eef40d20..b0156dca5e 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt @@ -99,7 +99,9 @@ class GamesFragment : Fragment() { } shouldSwapData.observe(viewLifecycleOwner) { shouldSwapData -> if (shouldSwapData) { - (binding.gridGames.adapter as GameAdapter).submitList(gamesViewModel.games.value!!) + (binding.gridGames.adapter as GameAdapter).submitList( + gamesViewModel.games.value!! + ) gamesViewModel.setShouldSwapData(false) } } @@ -128,7 +130,9 @@ class GamesFragment : Fragment() { } private fun setInsets() = - ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view: View, windowInsets: WindowInsetsCompat -> + ViewCompat.setOnApplyWindowInsetsListener( + binding.root + ) { view: View, windowInsets: WindowInsetsCompat -> val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout()) val extraListSpacing = resources.getDimensionPixelSize(R.dimen.spacing_large) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt index 3fca0a7e61..f7d7aed1e4 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt @@ -4,6 +4,7 @@ package org.yuzu.yuzu_emu.ui.main import android.content.Intent +import android.net.Uri import android.os.Bundle import android.view.View import android.view.ViewGroup.MarginLayoutParams @@ -26,6 +27,9 @@ import androidx.preference.PreferenceManager import com.google.android.material.color.MaterialColors import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.navigation.NavigationBarView +import java.io.File +import java.io.FilenameFilter +import java.io.IOException import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -39,13 +43,11 @@ import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment +import org.yuzu.yuzu_emu.fragments.LongMessageDialogFragment import org.yuzu.yuzu_emu.fragments.MessageDialogFragment import org.yuzu.yuzu_emu.model.GamesViewModel import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.utils.* -import java.io.File -import java.io.FilenameFilter -import java.io.IOException class MainActivity : AppCompatActivity(), ThemeProvider { private lateinit var binding: ActivityMainBinding @@ -86,7 +88,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider { ThemeHelper.SYSTEM_BAR_ALPHA ) ) - if (InsetsHelper.getSystemGestureType(applicationContext) != InsetsHelper.GESTURE_NAVIGATION) { + if (InsetsHelper.getSystemGestureType(applicationContext) != + InsetsHelper.GESTURE_NAVIGATION + ) { binding.navigationBarShade.setBackgroundColor( ThemeHelper.getColorWithOpacity( MaterialColors.getColor( @@ -172,7 +176,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider { binding.navigationView.height.toFloat() * 2 translationY(0f) } else { - if (ViewCompat.getLayoutDirection(binding.navigationView) == ViewCompat.LAYOUT_DIRECTION_LTR) { + if (ViewCompat.getLayoutDirection(binding.navigationView) == + ViewCompat.LAYOUT_DIRECTION_LTR + ) { binding.navigationView.translationX = binding.navigationView.width.toFloat() * -2 translationX(0f) @@ -189,7 +195,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider { if (smallLayout) { translationY(binding.navigationView.height.toFloat() * 2) } else { - if (ViewCompat.getLayoutDirection(binding.navigationView) == ViewCompat.LAYOUT_DIRECTION_LTR) { + if (ViewCompat.getLayoutDirection(binding.navigationView) == + ViewCompat.LAYOUT_DIRECTION_LTR + ) { translationX(binding.navigationView.width.toFloat() * -2) } else { translationX(binding.navigationView.width.toFloat() * 2) @@ -234,7 +242,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider { } private fun setInsets() = - ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _: View, windowInsets: WindowInsetsCompat -> + ViewCompat.setOnApplyWindowInsetsListener( + binding.root + ) { _: View, windowInsets: WindowInsetsCompat -> val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) val mlpStatusShade = binding.statusBarShade.layoutParams as MarginLayoutParams mlpStatusShade.height = insets.top @@ -256,8 +266,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider { val getGamesDirectory = registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result -> - if (result == null) + if (result == null) { return@registerForActivityResult + } contentResolver.takePersistableUriPermission( result, @@ -281,10 +292,11 @@ class MainActivity : AppCompatActivity(), ThemeProvider { val getProdKey = registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> - if (result == null) + if (result == null) { return@registerForActivityResult + } - if (!FileUtil.hasExtension(result, "keys")) { + if (FileUtil.getExtension(result) != "keys") { MessageDialogFragment.newInstance( R.string.reading_keys_failure, R.string.install_prod_keys_failure_extension_description @@ -324,8 +336,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider { val getFirmware = registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> - if (result == null) + if (result == null) { return@registerForActivityResult + } val inputZip = contentResolver.openInputStream(result) if (inputZip == null) { @@ -376,10 +389,11 @@ class MainActivity : AppCompatActivity(), ThemeProvider { val getAmiiboKey = registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> - if (result == null) + if (result == null) { return@registerForActivityResult + } - if (!FileUtil.hasExtension(result, "bin")) { + if (FileUtil.getExtension(result) != "bin") { MessageDialogFragment.newInstance( R.string.reading_keys_failure, R.string.install_amiibo_keys_failure_extension_description @@ -418,8 +432,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider { val getDriver = registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> - if (result == null) + if (result == null) { return@registerForActivityResult + } val takeFlags = Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION @@ -467,4 +482,111 @@ class MainActivity : AppCompatActivity(), ThemeProvider { } } } + + val installGameUpdate = registerForActivityResult( + ActivityResultContracts.OpenMultipleDocuments() + ) { documents: List<Uri> -> + if (documents.isNotEmpty()) { + IndeterminateProgressDialogFragment.newInstance( + this@MainActivity, + R.string.install_game_content + ) { + var installSuccess = 0 + var installOverwrite = 0 + var errorBaseGame = 0 + var errorExtension = 0 + var errorOther = 0 + var errorTotal = 0 + lifecycleScope.launch { + documents.forEach { + when (NativeLibrary.installFileToNand(it.toString())) { + NativeLibrary.InstallFileToNandResult.Success -> { + installSuccess += 1 + } + + NativeLibrary.InstallFileToNandResult.SuccessFileOverwritten -> { + installOverwrite += 1 + } + + NativeLibrary.InstallFileToNandResult.ErrorBaseGame -> { + errorBaseGame += 1 + } + + NativeLibrary.InstallFileToNandResult.ErrorFilenameExtension -> { + errorExtension += 1 + } + + else -> { + errorOther += 1 + } + } + } + withContext(Dispatchers.Main) { + val separator = System.getProperty("line.separator") ?: "\n" + val installResult = StringBuilder() + if (installSuccess > 0) { + installResult.append( + getString( + R.string.install_game_content_success_install, + installSuccess + ) + ) + installResult.append(separator) + } + if (installOverwrite > 0) { + installResult.append( + getString( + R.string.install_game_content_success_overwrite, + installOverwrite + ) + ) + installResult.append(separator) + } + errorTotal = errorBaseGame + errorExtension + errorOther + if (errorTotal > 0) { + installResult.append(separator) + installResult.append( + getString( + R.string.install_game_content_failed_count, + errorTotal + ) + ) + installResult.append(separator) + if (errorBaseGame > 0) { + installResult.append(separator) + installResult.append( + getString(R.string.install_game_content_failure_base) + ) + installResult.append(separator) + } + if (errorExtension > 0) { + installResult.append(separator) + installResult.append( + getString(R.string.install_game_content_failure_file_extension) + ) + installResult.append(separator) + } + if (errorOther > 0) { + installResult.append( + getString(R.string.install_game_content_failure_description) + ) + installResult.append(separator) + } + LongMessageDialogFragment.newInstance( + R.string.install_game_content_failure, + installResult.toString().trim(), + R.string.install_game_content_help_link + ).show(supportFragmentManager, LongMessageDialogFragment.TAG) + } else { + LongMessageDialogFragment.newInstance( + R.string.install_game_content_success, + installResult.toString().trim() + ).show(supportFragmentManager, LongMessageDialogFragment.TAG) + } + } + } + return@newInstance installSuccess + installOverwrite + errorTotal + }.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG) + } + } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ControllerMappingHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ControllerMappingHelper.kt index 791cea9049..eeefcdf205 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ControllerMappingHelper.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ControllerMappingHelper.kt @@ -19,7 +19,9 @@ class ControllerMappingHelper { // The two analog triggers generate analog motion events as well as a keycode. // We always prefer to use the analog values, so throw away the button press keyCode == KeyEvent.KEYCODE_BUTTON_L2 || keyCode == KeyEvent.KEYCODE_BUTTON_R2 - } else false + } else { + false + } } /** diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt index 36c479e6c5..2ee63697eb 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt @@ -4,8 +4,8 @@ package org.yuzu.yuzu_emu.utils import android.content.Context -import org.yuzu.yuzu_emu.NativeLibrary import java.io.IOException +import org.yuzu.yuzu_emu.NativeLibrary object DirectoryInitialization { private var userPath: String? = null diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DocumentsTree.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DocumentsTree.kt index cc8ea6b9d4..cf226ad945 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DocumentsTree.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DocumentsTree.kt @@ -5,10 +5,10 @@ package org.yuzu.yuzu_emu.utils import android.net.Uri import androidx.documentfile.provider.DocumentFile -import org.yuzu.yuzu_emu.YuzuApplication -import org.yuzu.yuzu_emu.model.MinimalDocumentFile import java.io.File import java.util.* +import org.yuzu.yuzu_emu.YuzuApplication +import org.yuzu.yuzu_emu.model.MinimalDocumentFile class DocumentsTree { private var root: DocumentsNode? = null @@ -29,13 +29,20 @@ class DocumentsTree { val node = resolvePath(filepath) return if (node == null || node.isDirectory) { 0 - } else FileUtil.getFileSize(YuzuApplication.appContext, node.uri.toString()) + } else { + FileUtil.getFileSize(YuzuApplication.appContext, node.uri.toString()) + } } fun exists(filepath: String): Boolean { return resolvePath(filepath) != null } + fun isDirectory(filepath: String): Boolean { + val node = resolvePath(filepath) + return node != null && node.isDirectory + } + private fun resolvePath(filepath: String): DocumentsNode? { val tokens = StringTokenizer(filepath, File.separator, false) var iterator = root @@ -106,7 +113,9 @@ class DocumentsTree { fun isNativePath(path: String): Boolean { return if (path.isNotEmpty()) { path[0] == '/' - } else false + } else { + false + } } } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/EmulationMenuSettings.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/EmulationMenuSettings.kt index e1e7a59d7c..7e8f058c14 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/EmulationMenuSettings.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/EmulationMenuSettings.kt @@ -11,14 +11,6 @@ object EmulationMenuSettings { private val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) - // These must match what is defined in src/core/settings.h - const val LayoutOption_Default = 0 - const val LayoutOption_SingleScreen = 1 - const val LayoutOption_LargeScreen = 2 - const val LayoutOption_SideScreen = 3 - const val LayoutOption_MobilePortrait = 4 - const val LayoutOption_MobileLandscape = 5 - var joystickRelCenter: Boolean get() = preferences.getBoolean(Settings.PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER, true) set(value) { @@ -41,16 +33,6 @@ object EmulationMenuSettings { .apply() } - var landscapeScreenLayout: Int - get() = preferences.getInt( - Settings.PREF_MENU_SETTINGS_LANDSCAPE, - LayoutOption_MobileLandscape - ) - set(value) { - preferences.edit() - .putInt(Settings.PREF_MENU_SETTINGS_LANDSCAPE, value) - .apply() - } var showFps: Boolean get() = preferences.getBoolean(Settings.PREF_MENU_SETTINGS_SHOW_FPS, false) set(value) { diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt index 492b1ad91e..142af5f264 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt @@ -7,10 +7,7 @@ import android.content.Context import android.database.Cursor import android.net.Uri import android.provider.DocumentsContract -import android.provider.OpenableColumns import androidx.documentfile.provider.DocumentFile -import org.yuzu.yuzu_emu.YuzuApplication -import org.yuzu.yuzu_emu.model.MinimalDocumentFile import java.io.BufferedInputStream import java.io.File import java.io.FileOutputStream @@ -19,6 +16,8 @@ import java.io.InputStream import java.net.URLDecoder import java.util.zip.ZipEntry import java.util.zip.ZipInputStream +import org.yuzu.yuzu_emu.YuzuApplication +import org.yuzu.yuzu_emu.model.MinimalDocumentFile object FileUtil { const val PATH_TREE = "tree" @@ -185,19 +184,18 @@ object FileUtil { /** * Get file display name from given path - * @param path content uri path + * @param uri content uri * @return String display name */ - fun getFilename(context: Context, path: String): String { - val resolver = context.contentResolver + fun getFilename(uri: Uri): String { + val resolver = YuzuApplication.appContext.contentResolver val columns = arrayOf( DocumentsContract.Document.COLUMN_DISPLAY_NAME ) var filename = "" var c: Cursor? = null try { - val mUri = Uri.parse(path) - c = resolver.query(mUri, columns, null, null, null) + c = resolver.query(uri, columns, null, null, null) c!!.moveToNext() filename = c.getString(0) } catch (e: Exception) { @@ -326,25 +324,9 @@ object FileUtil { } } - fun hasExtension(path: String, extension: String): Boolean = - path.substring(path.lastIndexOf(".") + 1).contains(extension) - - fun hasExtension(uri: Uri, extension: String): Boolean { - val fileName: String? - val cursor = YuzuApplication.appContext.contentResolver.query(uri, null, null, null, null) - val nameIndex = cursor?.getColumnIndex(OpenableColumns.DISPLAY_NAME) - cursor?.moveToFirst() - - if (nameIndex == null) { - return false - } - - fileName = cursor.getString(nameIndex) - cursor.close() - - if (fileName == null) { - return false - } - return fileName.substring(fileName.lastIndexOf(".") + 1).contains(extension) + fun getExtension(uri: Uri): String { + val fileName = getFilename(uri) + return fileName.substring(fileName.lastIndexOf(".") + 1) + .lowercase() } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ForegroundService.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ForegroundService.kt index dc9b7c7447..086d176065 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ForegroundService.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ForegroundService.kt @@ -54,7 +54,7 @@ class ForegroundService : Service() { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { if (intent == null) { - return START_NOT_STICKY; + return START_NOT_STICKY } if (intent.action == ACTION_STOP) { NotificationManagerCompat.from(this).cancel(EMULATION_RUNNING_NOTIFICATION) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt index 42b2076184..f8e7eeca79 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt @@ -11,7 +11,6 @@ import kotlinx.serialization.json.Json import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.YuzuApplication import org.yuzu.yuzu_emu.model.Game -import java.util.* object GameHelper { const val KEY_GAME_PATH = "game_path" @@ -33,15 +32,9 @@ object GameHelper { val children = FileUtil.listFiles(context, gamesUri) for (file in children) { if (!file.isDirectory) { - val filename = file.uri.toString() - val extensionStart = filename.lastIndexOf('.') - if (extensionStart > 0) { - val fileExtension = filename.substring(extensionStart) - - // Check that the file has an extension we care about before trying to read out of it. - if (Game.extensions.contains(fileExtension.lowercase(Locale.getDefault()))) { - games.add(getGame(filename)) - } + // Check that the file has an extension we care about before trying to read out of it. + if (Game.extensions.contains(FileUtil.getExtension(file.uri))) { + games.add(getGame(file.uri)) } } } @@ -59,21 +52,19 @@ object GameHelper { return games.toList() } - private fun getGame(filePath: String): Game { + private fun getGame(uri: Uri): Game { + val filePath = uri.toString() var name = NativeLibrary.getTitle(filePath) // If the game's title field is empty, use the filename. if (name.isEmpty()) { - name = filePath.substring(filePath.lastIndexOf("/") + 1) + name = FileUtil.getFilename(uri) } var gameId = NativeLibrary.getGameId(filePath) // If the game's ID field is empty, use the filename without extension. if (gameId.isEmpty()) { - gameId = filePath.substring( - filePath.lastIndexOf("/") + 1, - filePath.lastIndexOf(".") - ) + gameId = name.substring(0, name.lastIndexOf(".")) } val newGame = Game( diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt index 528011d7f8..1d4695a2af 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt @@ -5,14 +5,14 @@ package org.yuzu.yuzu_emu.utils import android.content.Context import android.net.Uri -import org.yuzu.yuzu_emu.NativeLibrary -import org.yuzu.yuzu_emu.utils.FileUtil.copyUriToInternalStorage import java.io.BufferedInputStream import java.io.File import java.io.FileInputStream import java.io.FileOutputStream import java.io.IOException import java.util.zip.ZipInputStream +import org.yuzu.yuzu_emu.NativeLibrary +import org.yuzu.yuzu_emu.utils.FileUtil.copyUriToInternalStorage object GpuDriverHelper { private const val META_JSON_FILENAME = "meta.json" @@ -113,6 +113,8 @@ object GpuDriverHelper { initializeDriverParameters(context) } + external fun supportsCustomDriverLoading(): Boolean + // Parse the custom driver metadata to retrieve the name. val customDriverName: String? get() { diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverMetadata.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverMetadata.kt index 70bdb40973..a4e64070a8 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverMetadata.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverMetadata.kt @@ -3,12 +3,12 @@ package org.yuzu.yuzu_emu.utils -import org.json.JSONException -import org.json.JSONObject import java.io.IOException import java.nio.charset.StandardCharsets import java.nio.file.Files import java.nio.file.Paths +import org.json.JSONException +import org.json.JSONObject class GpuDriverMetadata(metadataFilePath: String) { var name: String? = null diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt index 24e999b292..e963dfbc17 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt @@ -5,8 +5,8 @@ package org.yuzu.yuzu_emu.utils import android.view.KeyEvent import android.view.MotionEvent -import org.yuzu.yuzu_emu.NativeLibrary import kotlin.math.sqrt +import org.yuzu.yuzu_emu.NativeLibrary class InputHandler { fun initialize() { @@ -68,7 +68,11 @@ class InputHandler { 6 -> NativeLibrary.Player6Device 7 -> NativeLibrary.Player7Device 8 -> NativeLibrary.Player8Device - else -> if (NativeLibrary.isHandheldOnly()) NativeLibrary.ConsoleDevice else NativeLibrary.Player1Device + else -> if (NativeLibrary.isHandheldOnly()) { + NativeLibrary.ConsoleDevice + } else { + NativeLibrary.Player1Device + } } } @@ -107,7 +111,11 @@ class InputHandler { } private fun getAxisToButton(axis: Float): Int { - return if (axis > 0.5f) NativeLibrary.ButtonState.PRESSED else NativeLibrary.ButtonState.RELEASED + return if (axis > 0.5f) { + NativeLibrary.ButtonState.PRESSED + } else { + NativeLibrary.ButtonState.RELEASED + } } private fun setAxisDpadState(playerNumber: Int, xAxis: Float, yAxis: Float) { @@ -287,7 +295,6 @@ class InputHandler { } } - private fun setJoyconAxisInput(event: MotionEvent, axis: Int) { // Joycon support is half dead. Right joystick doesn't work val playerNumber = getPlayerNumber(event.device.controllerNumber) @@ -355,6 +362,4 @@ class InputHandler { ) } } - - -}
\ No newline at end of file +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InsetsHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InsetsHelper.kt index 19c53c4814..595f0d2847 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InsetsHelper.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InsetsHelper.kt @@ -4,9 +4,7 @@ package org.yuzu.yuzu_emu.utils import android.annotation.SuppressLint -import android.app.Activity import android.content.Context -import android.graphics.Rect object InsetsHelper { const val THREE_BUTTON_NAVIGATION = 0 @@ -20,12 +18,8 @@ object InsetsHelper { resources.getIdentifier("config_navBarInteractionMode", "integer", "android") return if (resourceId != 0) { resources.getInteger(resourceId) - } else 0 - } - - fun getBottomPaddingRequired(activity: Activity): Int { - val visibleFrame = Rect() - activity.window.decorView.getWindowVisibleDisplayFrame(visibleFrame) - return visibleFrame.bottom - visibleFrame.top - activity.resources.displayMetrics.heightPixels + } else { + 0 + } } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt new file mode 100644 index 0000000000..18e5fa0b0d --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt @@ -0,0 +1,59 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.utils + +import android.app.ActivityManager +import android.content.Context +import org.yuzu.yuzu_emu.R +import java.util.Locale + +class MemoryUtil(val context: Context) { + + private val Long.floatForm: String + get() = String.format(Locale.ROOT, "%.2f", this.toDouble()) + + private fun bytesToSizeUnit(size: Long): String { + return when { + size < Kb -> "${size.floatForm} ${context.getString(R.string.memory_byte)}" + size < Mb -> "${(size / Kb).floatForm} ${context.getString(R.string.memory_kilobyte)}" + size < Gb -> "${(size / Mb).floatForm} ${context.getString(R.string.memory_megabyte)}" + size < Tb -> "${(size / Gb).floatForm} ${context.getString(R.string.memory_gigabyte)}" + size < Pb -> "${(size / Tb).floatForm} ${context.getString(R.string.memory_terabyte)}" + size < Eb -> "${(size / Pb).floatForm} ${context.getString(R.string.memory_petabyte)}" + else -> "${(size / Eb).floatForm} ${context.getString(R.string.memory_exabyte)}" + } + } + + private val totalMemory = + with(context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager) { + val memInfo = ActivityManager.MemoryInfo() + getMemoryInfo(memInfo) + memInfo.totalMem + } + + fun isLessThan(minimum: Int, size: Long): Boolean { + return when (size) { + Kb -> totalMemory < Mb && totalMemory < minimum + Mb -> totalMemory < Gb && (totalMemory / Mb) < minimum + Gb -> totalMemory < Tb && (totalMemory / Gb) < minimum + Tb -> totalMemory < Pb && (totalMemory / Tb) < minimum + Pb -> totalMemory < Eb && (totalMemory / Pb) < minimum + Eb -> totalMemory / Eb < minimum + else -> totalMemory < Kb && totalMemory < minimum + } + } + + fun getDeviceRAM(): String { + return bytesToSizeUnit(totalMemory) + } + + companion object { + const val Kb: Long = 1024 + const val Mb = Kb * 1024 + const val Gb = Mb * 1024 + const val Tb = Gb * 1024 + const val Pb = Tb * 1024 + const val Eb = Pb * 1024 + } +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NfcReader.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NfcReader.kt index 344dd8a0ad..68ed66565a 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NfcReader.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NfcReader.kt @@ -13,8 +13,8 @@ import android.nfc.tech.NfcA import android.os.Build import android.os.Handler import android.os.Looper -import org.yuzu.yuzu_emu.NativeLibrary import java.io.IOException +import org.yuzu.yuzu_emu.NativeLibrary class NfcReader(private val activity: Activity) { private var nfcAdapter: NfcAdapter? = null @@ -25,10 +25,13 @@ class NfcReader(private val activity: Activity) { pendingIntent = PendingIntent.getActivity( activity, - 0, Intent(activity, activity.javaClass), - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) + 0, + Intent(activity, activity.javaClass), + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE - else PendingIntent.FLAG_UPDATE_CURRENT + } else { + PendingIntent.FLAG_UPDATE_CURRENT + } ) val tagDetected = IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED) @@ -45,9 +48,9 @@ class NfcReader(private val activity: Activity) { fun onNewIntent(intent: Intent) { val action = intent.action - if (NfcAdapter.ACTION_TAG_DISCOVERED != action - && NfcAdapter.ACTION_TECH_DISCOVERED != action - && NfcAdapter.ACTION_NDEF_DISCOVERED != action + if (NfcAdapter.ACTION_TAG_DISCOVERED != action && + NfcAdapter.ACTION_TECH_DISCOVERED != action && + NfcAdapter.ACTION_NDEF_DISCOVERED != action ) { return } @@ -84,7 +87,7 @@ class NfcReader(private val activity: Activity) { } private fun ntag215ReadAll(amiibo: NfcA): ByteArray? { - val bufferSize = amiibo.maxTransceiveLength; + val bufferSize = amiibo.maxTransceiveLength val tagSize = 0x21C val pageSize = 4 val lastPage = tagSize / pageSize - 1 @@ -103,7 +106,7 @@ class NfcReader(private val activity: Activity) { val data = ntag215FastRead(amiibo, dataStart, dataEnd - 1) System.arraycopy(data, 0, tagData, i, (dataEnd - dataStart) * pageSize) } catch (e: IOException) { - return null; + return null } } return tagData diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/SerializableHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/SerializableHelper.kt index 87ee7f2e61..00e58faec7 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/SerializableHelper.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/SerializableHelper.kt @@ -11,30 +11,34 @@ import java.io.Serializable object SerializableHelper { inline fun <reified T : Serializable> Bundle.serializable(key: String): T? { - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { getSerializable(key, T::class.java) - else + } else { getSerializable(key) as? T + } } inline fun <reified T : Serializable> Intent.serializable(key: String): T? { - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { getSerializableExtra(key, T::class.java) - else + } else { getSerializableExtra(key) as? T + } } inline fun <reified T : Parcelable> Bundle.parcelable(key: String): T? { - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { getParcelable(key, T::class.java) - else + } else { getParcelable(key) as? T + } } inline fun <reified T : Parcelable> Intent.parcelable(key: String): T? { - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { getParcelableExtra(key, T::class.java) - else + } else { getParcelableExtra(key) as? T + } } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt index e55767c0fe..f312e24cf1 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt @@ -3,21 +3,19 @@ package org.yuzu.yuzu_emu.utils -import android.app.Activity import android.content.res.Configuration import android.graphics.Color import androidx.annotation.ColorInt import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatDelegate -import androidx.core.content.ContextCompat import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsControllerCompat import androidx.preference.PreferenceManager +import kotlin.math.roundToInt import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.YuzuApplication import org.yuzu.yuzu_emu.features.settings.model.Settings import org.yuzu.yuzu_emu.ui.main.ThemeProvider -import kotlin.math.roundToInt object ThemeHelper { const val SYSTEM_BAR_ALPHA = 0.9f @@ -36,8 +34,8 @@ object ThemeHelper { // Using a specific night mode check because this could apply incorrectly when using the // light app mode, dark system mode, and black backgrounds. Launching the settings activity // will then show light mode colors/navigation bars but with black backgrounds. - if (preferences.getBoolean(Settings.PREF_BLACK_BACKGROUNDS, false) - && isNightMode(activity) + if (preferences.getBoolean(Settings.PREF_BLACK_BACKGROUNDS, false) && + isNightMode(activity) ) { activity.setTheme(R.style.ThemeOverlay_Yuzu_Dark) } @@ -46,8 +44,10 @@ object ThemeHelper { @ColorInt fun getColorWithOpacity(@ColorInt color: Int, alphaFactor: Float): Int { return Color.argb( - (alphaFactor * Color.alpha(color)).roundToInt(), Color.red(color), - Color.green(color), Color.blue(color) + (alphaFactor * Color.alpha(color)).roundToInt(), + Color.red(color), + Color.green(color), + Color.blue(color) ) } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/views/FixedRatioSurfaceView.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/views/FixedRatioSurfaceView.kt index c8ef8c1fdb..685ccaa76e 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/views/FixedRatioSurfaceView.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/views/FixedRatioSurfaceView.kt @@ -38,9 +38,11 @@ class FixedRatioSurfaceView @JvmOverloads constructor( newWidth = width newHeight = (width / aspectRatio).roundToInt() } - setMeasuredDimension(newWidth, newHeight) + val left = (width - newWidth) / 2 + val top = (height - newHeight) / 2 + setLeftTopRightBottom(left, top, left + newWidth, top + newHeight) } else { - setMeasuredDimension(width, height) + setLeftTopRightBottom(0, 0, width, height) } } } diff --git a/src/android/app/src/main/jni/CMakeLists.txt b/src/android/app/src/main/jni/CMakeLists.txt index 0417815770..e2ed08e9fc 100644 --- a/src/android/app/src/main/jni/CMakeLists.txt +++ b/src/android/app/src/main/jni/CMakeLists.txt @@ -14,7 +14,6 @@ add_library(yuzu-android SHARED id_cache.cpp id_cache.h native.cpp - native.h ) set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR}) diff --git a/src/android/app/src/main/jni/config.cpp b/src/android/app/src/main/jni/config.cpp index 2d622a048c..43e8aa72a2 100644 --- a/src/android/app/src/main/jni/config.cpp +++ b/src/android/app/src/main/jni/config.cpp @@ -235,9 +235,13 @@ void Config::ReadValues() { Settings::values.async_presentation = config->GetBoolean("Renderer", "async_presentation", true); - // Enable force_max_clock by default on Android + // Disable force_max_clock by default on Android Settings::values.renderer_force_max_clock = - config->GetBoolean("Renderer", "force_max_clock", true); + config->GetBoolean("Renderer", "force_max_clock", false); + + // Disable use_reactive_flushing by default on Android + Settings::values.use_reactive_flushing = + config->GetBoolean("Renderer", "use_reactive_flushing", false); // Audio ReadSetting("Audio", Settings::values.sink_id); diff --git a/src/android/app/src/main/jni/default_ini.h b/src/android/app/src/main/jni/default_ini.h index c5dfaff54e..d81422a748 100644 --- a/src/android/app/src/main/jni/default_ini.h +++ b/src/android/app/src/main/jni/default_ini.h @@ -251,7 +251,7 @@ backend = # 0: Off, 1 (default): On async_presentation = -# Enable graphics API debugging mode. +# Forces the GPU to run at the maximum possible clocks (thermal constraints will still be applied). # 0 (default): Disabled, 1: Enabled force_max_clock = @@ -328,6 +328,10 @@ shader_backend = # 0 (default): Off, 1: On use_asynchronous_shaders = +# Uses reactive flushing instead of predictive flushing. Allowing a more accurate syncing of memory. +# 0 (default): Off, 1: On +use_reactive_flushing = + # NVDEC emulation. # 0: Disabled, 1: CPU Decoding, 2 (default): GPU Decoding nvdec_emulation = diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 7ebed5e6aa..8bc6a4a044 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -14,6 +14,7 @@ #include <android/api-level.h> #include <android/native_window_jni.h> #include <core/loader/nro.h> +#include <jni.h> #include "common/detached_tasks.h" #include "common/dynamic_library.h" @@ -28,7 +29,10 @@ #include "core/core.h" #include "core/cpu_manager.h" #include "core/crypto/key_manager.h" +#include "core/file_sys/card_image.h" #include "core/file_sys/registered_cache.h" +#include "core/file_sys/submission_package.h" +#include "core/file_sys/vfs.h" #include "core/file_sys/vfs_real.h" #include "core/frontend/applets/cabinet.h" #include "core/frontend/applets/controller.h" @@ -56,6 +60,9 @@ #include "video_core/rasterizer_interface.h" #include "video_core/renderer_base.h" +#define jconst [[maybe_unused]] const auto +#define jauto [[maybe_unused]] auto + namespace { class EmulationSession final { @@ -94,6 +101,74 @@ public: m_native_window = native_window; } + int InstallFileToNand(std::string filename) { + jconst copy_func = [](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest, + std::size_t block_size) { + if (src == nullptr || dest == nullptr) { + return false; + } + if (!dest->Resize(src->GetSize())) { + return false; + } + + using namespace Common::Literals; + [[maybe_unused]] std::vector<u8> buffer(1_MiB); + + for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) { + jconst read = src->Read(buffer.data(), buffer.size(), i); + dest->Write(buffer.data(), read, i); + } + return true; + }; + + enum InstallResult { + Success = 0, + SuccessFileOverwritten = 1, + InstallError = 2, + ErrorBaseGame = 3, + ErrorFilenameExtension = 4, + }; + + m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); + m_system.GetFileSystemController().CreateFactories(*m_vfs); + + [[maybe_unused]] std::shared_ptr<FileSys::NSP> nsp; + if (filename.ends_with("nsp")) { + nsp = std::make_shared<FileSys::NSP>(m_vfs->OpenFile(filename, FileSys::Mode::Read)); + if (nsp->IsExtractedType()) { + return InstallError; + } + } else if (filename.ends_with("xci")) { + jconst xci = + std::make_shared<FileSys::XCI>(m_vfs->OpenFile(filename, FileSys::Mode::Read)); + nsp = xci->GetSecurePartitionNSP(); + } else { + return ErrorFilenameExtension; + } + + if (!nsp) { + return InstallError; + } + + if (nsp->GetStatus() != Loader::ResultStatus::Success) { + return InstallError; + } + + jconst res = m_system.GetFileSystemController().GetUserNANDContents()->InstallEntry( + *nsp, true, copy_func); + + switch (res) { + case FileSys::InstallResult::Success: + return Success; + case FileSys::InstallResult::OverwriteExisting: + return SuccessFileOverwritten; + case FileSys::InstallResult::ErrorBaseInstall: + return ErrorBaseGame; + default: + return InstallError; + } + } + void InitializeGpuDriver(const std::string& hook_lib_dir, const std::string& custom_driver_dir, const std::string& custom_driver_name, const std::string& file_redirect_dir) { @@ -131,6 +206,11 @@ public: return m_is_running; } + bool IsPaused() const { + std::scoped_lock lock(m_mutex); + return m_is_running && m_is_paused; + } + const Core::PerfStatsResults& PerfStats() const { std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex); return m_perf_stats; @@ -154,14 +234,15 @@ public: m_window = std::make_unique<EmuWindow_Android>(&m_input_subsystem, m_native_window, m_vulkan_library); + m_system.SetFilesystem(m_vfs); + // Initialize system. - auto android_keyboard = std::make_unique<SoftwareKeyboard::AndroidKeyboard>(); + jauto android_keyboard = std::make_unique<SoftwareKeyboard::AndroidKeyboard>(); m_software_keyboard = android_keyboard.get(); m_system.SetShuttingDown(false); m_system.ApplySettings(); + Settings::LogSettings(); m_system.HIDCore().ReloadInputDevices(); - m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); - m_system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); m_system.SetAppletFrontendSet({ nullptr, // Amiibo Settings nullptr, // Controller Selector @@ -173,7 +254,8 @@ public: std::move(android_keyboard), // Software Keyboard nullptr, // Web Browser }); - m_system.GetFileSystemController().CreateFactories(*m_system.GetFilesystem()); + m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); + m_system.GetFileSystemController().CreateFactories(*m_vfs); // Initialize account manager m_profile_manager = std::make_unique<Service::Account::ProfileManager>(); @@ -215,11 +297,13 @@ public: void PauseEmulation() { std::scoped_lock lock(m_mutex); m_system.Pause(); + m_is_paused = true; } void UnPauseEmulation() { std::scoped_lock lock(m_mutex); m_system.Run(); + m_is_paused = false; } void HaltEmulation() { @@ -251,7 +335,7 @@ public: while (true) { { - std::unique_lock lock(m_mutex); + [[maybe_unused]] std::unique_lock lock(m_mutex); if (m_cv.wait_for(lock, std::chrono::milliseconds(800), [&]() { return !m_is_running; })) { // Emulation halted. @@ -283,7 +367,7 @@ public: } bool IsHandheldOnly() { - const auto npad_style_set = m_system.HIDCore().GetSupportedStyleTag(); + jconst npad_style_set = m_system.HIDCore().GetSupportedStyleTag(); if (npad_style_set.fullkey == 1) { return false; @@ -296,17 +380,17 @@ public: return !Settings::values.use_docked_mode.GetValue(); } - void SetDeviceType(int index, int type) { - auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); + void SetDeviceType([[maybe_unused]] int index, int type) { + jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); controller->SetNpadStyleIndex(static_cast<Core::HID::NpadStyleIndex>(type)); } - void OnGamepadConnectEvent(int index) { - auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); + void OnGamepadConnectEvent([[maybe_unused]] int index) { + jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); // Ensure that player1 is configured correctly and handheld disconnected if (controller->GetNpadIdType() == Core::HID::NpadIdType::Player1) { - auto handheld = + jauto handheld = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); if (controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) { @@ -318,7 +402,8 @@ public: // Ensure that handheld is configured correctly and player 1 disconnected if (controller->GetNpadIdType() == Core::HID::NpadIdType::Handheld) { - auto player1 = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); + jauto player1 = + m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); if (controller->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::Handheld) { player1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld); @@ -332,8 +417,8 @@ public: } } - void OnGamepadDisconnectEvent(int index) { - auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); + void OnGamepadDisconnectEvent([[maybe_unused]] int index) { + jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); controller->Disconnect(); } @@ -349,7 +434,7 @@ private: }; RomMetadata GetRomMetadata(const std::string& path) { - if (auto search = m_rom_metadata_cache.find(path); search != m_rom_metadata_cache.end()) { + if (jauto search = m_rom_metadata_cache.find(path); search != m_rom_metadata_cache.end()) { return search->second; } @@ -357,14 +442,14 @@ private: } RomMetadata CacheRomMetadata(const std::string& path) { - const auto file = Core::GetGameFileFromPath(m_vfs, path); - auto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0); + jconst file = Core::GetGameFileFromPath(m_vfs, path); + jauto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0); RomMetadata entry; loader->ReadTitle(entry.title); loader->ReadIcon(entry.icon); if (loader->GetFileType() == Loader::FileType::NRO) { - auto loader_nro = dynamic_cast<Loader::AppLoader_NRO*>(loader.get()); + jauto loader_nro = dynamic_cast<Loader::AppLoader_NRO*>(loader.get()); entry.isHomebrew = loader_nro->IsHomebrew(); } else { entry.isHomebrew = false; @@ -398,9 +483,10 @@ private: InputCommon::InputSubsystem m_input_subsystem; Common::DetachedTasks m_detached_tasks; Core::PerfStatsResults m_perf_stats{}; - std::shared_ptr<FileSys::RealVfsFilesystem> m_vfs; + std::shared_ptr<FileSys::VfsFilesystem> m_vfs; Core::SystemResultStatus m_load_result{Core::SystemResultStatus::ErrorNotInitialized}; bool m_is_running{}; + bool m_is_paused{}; SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{}; std::unique_ptr<Service::Account::ProfileManager> m_profile_manager; @@ -434,7 +520,7 @@ static Core::SystemResultStatus RunEmulation(const std::string& filepath) { SCOPE_EXIT({ EmulationSession::GetInstance().ShutdownEmulation(); }); - const auto result = EmulationSession::GetInstance().InitializeEmulation(filepath); + jconst result = EmulationSession::GetInstance().InitializeEmulation(filepath); if (result != Core::SystemResultStatus::Success) { return result; } @@ -446,72 +532,104 @@ static Core::SystemResultStatus RunEmulation(const std::string& filepath) { extern "C" { -void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceChanged(JNIEnv* env, - [[maybe_unused]] jclass clazz, - jobject surf) { +void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceChanged(JNIEnv* env, jobject instance, + [[maybe_unused]] jobject surf) { EmulationSession::GetInstance().SetNativeWindow(ANativeWindow_fromSurface(env, surf)); EmulationSession::GetInstance().SurfaceChanged(); } -void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceDestroyed(JNIEnv* env, - [[maybe_unused]] jclass clazz) { +void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceDestroyed(JNIEnv* env, jobject instance) { ANativeWindow_release(EmulationSession::GetInstance().NativeWindow()); EmulationSession::GetInstance().SetNativeWindow(nullptr); EmulationSession::GetInstance().SurfaceChanged(); } -void Java_org_yuzu_yuzu_1emu_NativeLibrary_setAppDirectory(JNIEnv* env, - [[maybe_unused]] jclass clazz, - jstring j_directory) { +void Java_org_yuzu_yuzu_1emu_NativeLibrary_setAppDirectory(JNIEnv* env, jobject instance, + [[maybe_unused]] jstring j_directory) { Common::FS::SetAppDirectory(GetJString(env, j_directory)); } -void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeGpuDriver( - JNIEnv* env, [[maybe_unused]] jclass clazz, jstring hook_lib_dir, jstring custom_driver_dir, - jstring custom_driver_name, jstring file_redirect_dir) { +int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject instance, + [[maybe_unused]] jstring j_file) { + return EmulationSession::GetInstance().InstallFileToNand(GetJString(env, j_file)); +} + +void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeGpuDriver(JNIEnv* env, jclass clazz, + jstring hook_lib_dir, + jstring custom_driver_dir, + jstring custom_driver_name, + jstring file_redirect_dir) { EmulationSession::GetInstance().InitializeGpuDriver( GetJString(env, hook_lib_dir), GetJString(env, custom_driver_dir), GetJString(env, custom_driver_name), GetJString(env, file_redirect_dir)); } -jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadKeys(JNIEnv* env, - [[maybe_unused]] jclass clazz) { +[[maybe_unused]] static bool CheckKgslPresent() { + constexpr auto KgslPath{"/dev/kgsl-3d0"}; + + return access(KgslPath, F_OK) == 0; +} + +[[maybe_unused]] bool SupportsCustomDriver() { + return android_get_device_api_level() >= 28 && CheckKgslPresent(); +} + +jboolean JNICALL Java_org_yuzu_yuzu_1emu_utils_GpuDriverHelper_supportsCustomDriverLoading( + JNIEnv* env, jobject instance) { +#ifdef ARCHITECTURE_arm64 + // If the KGSL device exists custom drivers can be loaded using adrenotools + return SupportsCustomDriver(); +#else + return false; +#endif +} + +jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadKeys(JNIEnv* env, jclass clazz) { Core::Crypto::KeyManager::Instance().ReloadKeys(); return static_cast<jboolean>(Core::Crypto::KeyManager::Instance().AreKeysLoaded()); } -void Java_org_yuzu_yuzu_1emu_NativeLibrary_unPauseEmulation([[maybe_unused]] JNIEnv* env, - [[maybe_unused]] jclass clazz) { +void Java_org_yuzu_yuzu_1emu_NativeLibrary_unpauseEmulation(JNIEnv* env, jclass clazz) { EmulationSession::GetInstance().UnPauseEmulation(); } -void Java_org_yuzu_yuzu_1emu_NativeLibrary_pauseEmulation([[maybe_unused]] JNIEnv* env, - [[maybe_unused]] jclass clazz) { +void Java_org_yuzu_yuzu_1emu_NativeLibrary_pauseEmulation(JNIEnv* env, jclass clazz) { EmulationSession::GetInstance().PauseEmulation(); } -void Java_org_yuzu_yuzu_1emu_NativeLibrary_stopEmulation([[maybe_unused]] JNIEnv* env, - [[maybe_unused]] jclass clazz) { +void Java_org_yuzu_yuzu_1emu_NativeLibrary_stopEmulation(JNIEnv* env, jclass clazz) { EmulationSession::GetInstance().HaltEmulation(); } -void Java_org_yuzu_yuzu_1emu_NativeLibrary_resetRomMetadata([[maybe_unused]] JNIEnv* env, - [[maybe_unused]] jclass clazz) { +void Java_org_yuzu_yuzu_1emu_NativeLibrary_resetRomMetadata(JNIEnv* env, jclass clazz) { EmulationSession::GetInstance().ResetRomMetadata(); } -jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isRunning([[maybe_unused]] JNIEnv* env, - [[maybe_unused]] jclass clazz) { +jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isRunning(JNIEnv* env, jclass clazz) { return static_cast<jboolean>(EmulationSession::GetInstance().IsRunning()); } -jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHandheldOnly([[maybe_unused]] JNIEnv* env, - [[maybe_unused]] jclass clazz) { +jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isPaused(JNIEnv* env, jclass clazz) { + return static_cast<jboolean>(EmulationSession::GetInstance().IsPaused()); +} + +void Java_org_yuzu_yuzu_1emu_NativeLibrary_muteAduio(JNIEnv* env, jclass clazz) { + Settings::values.audio_muted = true; +} + +void Java_org_yuzu_yuzu_1emu_NativeLibrary_unmuteAudio(JNIEnv* env, jclass clazz) { + Settings::values.audio_muted = false; +} + +jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isMuted(JNIEnv* env, jclass clazz) { + return static_cast<jboolean>(Settings::values.audio_muted.GetValue()); +} + +jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHandheldOnly(JNIEnv* env, jclass clazz) { return EmulationSession::GetInstance().IsHandheldOnly(); } -jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_setDeviceType([[maybe_unused]] JNIEnv* env, - [[maybe_unused]] jclass clazz, +jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_setDeviceType(JNIEnv* env, jclass clazz, jint j_device, jint j_type) { if (EmulationSession::GetInstance().IsRunning()) { EmulationSession::GetInstance().SetDeviceType(j_device, j_type); @@ -519,8 +637,7 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_setDeviceType([[maybe_unused]] JN return static_cast<jboolean>(true); } -jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadConnectEvent([[maybe_unused]] JNIEnv* env, - [[maybe_unused]] jclass clazz, +jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadConnectEvent(JNIEnv* env, jclass clazz, jint j_device) { if (EmulationSession::GetInstance().IsRunning()) { EmulationSession::GetInstance().OnGamepadConnectEvent(j_device); @@ -528,17 +645,16 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadConnectEvent([[maybe_unu return static_cast<jboolean>(true); } -jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadDisconnectEvent( - [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jint j_device) { +jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadDisconnectEvent(JNIEnv* env, jclass clazz, + jint j_device) { if (EmulationSession::GetInstance().IsRunning()) { EmulationSession::GetInstance().OnGamepadDisconnectEvent(j_device); } return static_cast<jboolean>(true); } -jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadButtonEvent([[maybe_unused]] JNIEnv* env, - [[maybe_unused]] jclass clazz, - [[maybe_unused]] jint j_device, - jint j_button, jint action) { +jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadButtonEvent(JNIEnv* env, jclass clazz, + jint j_device, jint j_button, + jint action) { if (EmulationSession::GetInstance().IsRunning()) { // Ensure gamepad is connected EmulationSession::GetInstance().OnGamepadConnectEvent(j_device); @@ -548,8 +664,7 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadButtonEvent([[maybe_unus return static_cast<jboolean>(true); } -jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadJoystickEvent([[maybe_unused]] JNIEnv* env, - [[maybe_unused]] jclass clazz, +jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadJoystickEvent(JNIEnv* env, jclass clazz, jint j_device, jint stick_id, jfloat x, jfloat y) { if (EmulationSession::GetInstance().IsRunning()) { @@ -559,9 +674,8 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadJoystickEvent([[maybe_un } jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadMotionEvent( - [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jint j_device, - jlong delta_timestamp, jfloat gyro_x, jfloat gyro_y, jfloat gyro_z, jfloat accel_x, - jfloat accel_y, jfloat accel_z) { + JNIEnv* env, jclass clazz, jint j_device, jlong delta_timestamp, jfloat gyro_x, jfloat gyro_y, + jfloat gyro_z, jfloat accel_x, jfloat accel_y, jfloat accel_z) { if (EmulationSession::GetInstance().IsRunning()) { EmulationSession::GetInstance().Window().OnGamepadMotionEvent( j_device, delta_timestamp, gyro_x, gyro_y, gyro_z, accel_x, accel_y, accel_z); @@ -569,8 +683,7 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadMotionEvent( return static_cast<jboolean>(true); } -jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onReadNfcTag([[maybe_unused]] JNIEnv* env, - [[maybe_unused]] jclass clazz, +jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onReadNfcTag(JNIEnv* env, jclass clazz, jbyteArray j_data) { jboolean isCopy{false}; std::span<u8> data(reinterpret_cast<u8*>(env->GetByteArrayElements(j_data, &isCopy)), @@ -582,108 +695,92 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onReadNfcTag([[maybe_unused]] JNI return static_cast<jboolean>(true); } -jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onRemoveNfcTag([[maybe_unused]] JNIEnv* env, - [[maybe_unused]] jclass clazz) { +jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onRemoveNfcTag(JNIEnv* env, jclass clazz) { if (EmulationSession::GetInstance().IsRunning()) { EmulationSession::GetInstance().Window().OnRemoveNfcTag(); } return static_cast<jboolean>(true); } -void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchPressed([[maybe_unused]] JNIEnv* env, - [[maybe_unused]] jclass clazz, jint id, +void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchPressed(JNIEnv* env, jclass clazz, jint id, jfloat x, jfloat y) { if (EmulationSession::GetInstance().IsRunning()) { EmulationSession::GetInstance().Window().OnTouchPressed(id, x, y); } } -void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchMoved([[maybe_unused]] JNIEnv* env, - [[maybe_unused]] jclass clazz, jint id, +void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchMoved(JNIEnv* env, jclass clazz, jint id, jfloat x, jfloat y) { if (EmulationSession::GetInstance().IsRunning()) { EmulationSession::GetInstance().Window().OnTouchMoved(id, x, y); } } -void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchReleased([[maybe_unused]] JNIEnv* env, - [[maybe_unused]] jclass clazz, jint id) { +void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchReleased(JNIEnv* env, jclass clazz, jint id) { if (EmulationSession::GetInstance().IsRunning()) { EmulationSession::GetInstance().Window().OnTouchReleased(id); } } -jbyteArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getIcon([[maybe_unused]] JNIEnv* env, - [[maybe_unused]] jclass clazz, - [[maybe_unused]] jstring j_filename) { - auto icon_data = EmulationSession::GetInstance().GetRomIcon(GetJString(env, j_filename)); +jbyteArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getIcon(JNIEnv* env, jclass clazz, + jstring j_filename) { + jauto icon_data = EmulationSession::GetInstance().GetRomIcon(GetJString(env, j_filename)); jbyteArray icon = env->NewByteArray(static_cast<jsize>(icon_data.size())); env->SetByteArrayRegion(icon, 0, env->GetArrayLength(icon), reinterpret_cast<jbyte*>(icon_data.data())); return icon; } -jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getTitle([[maybe_unused]] JNIEnv* env, - [[maybe_unused]] jclass clazz, - [[maybe_unused]] jstring j_filename) { - auto title = EmulationSession::GetInstance().GetRomTitle(GetJString(env, j_filename)); +jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getTitle(JNIEnv* env, jclass clazz, + jstring j_filename) { + jauto title = EmulationSession::GetInstance().GetRomTitle(GetJString(env, j_filename)); return env->NewStringUTF(title.c_str()); } -jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getDescription([[maybe_unused]] JNIEnv* env, - [[maybe_unused]] jclass clazz, +jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getDescription(JNIEnv* env, jclass clazz, jstring j_filename) { return j_filename; } -jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getGameId([[maybe_unused]] JNIEnv* env, - [[maybe_unused]] jclass clazz, +jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getGameId(JNIEnv* env, jclass clazz, jstring j_filename) { return j_filename; } -jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getRegions([[maybe_unused]] JNIEnv* env, - [[maybe_unused]] jclass clazz, - [[maybe_unused]] jstring j_filename) { +jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getRegions(JNIEnv* env, jclass clazz, + jstring j_filename) { return env->NewStringUTF(""); } -jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCompany([[maybe_unused]] JNIEnv* env, - [[maybe_unused]] jclass clazz, - [[maybe_unused]] jstring j_filename) { +jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCompany(JNIEnv* env, jclass clazz, + jstring j_filename) { return env->NewStringUTF(""); } -jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHomebrew([[maybe_unused]] JNIEnv* env, - [[maybe_unused]] jclass clazz, - [[maybe_unused]] jstring j_filename) { +jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHomebrew(JNIEnv* env, jclass clazz, + jstring j_filename) { return EmulationSession::GetInstance().GetIsHomebrew(GetJString(env, j_filename)); } -void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmulation - [[maybe_unused]] (JNIEnv* env, [[maybe_unused]] jclass clazz) { +void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmulation(JNIEnv* env, jclass clazz) { // Create the default config.ini. Config{}; // Initialize the emulated system. EmulationSession::GetInstance().System().Initialize(); } -jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore([[maybe_unused]] JNIEnv* env, - [[maybe_unused]] jclass clazz) { +jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore(JNIEnv* env, jclass clazz) { return {}; } void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2Ljava_lang_String_2Z( - [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, [[maybe_unused]] jstring j_file, - [[maybe_unused]] jstring j_savestate, [[maybe_unused]] jboolean j_delete_savestate) {} + JNIEnv* env, jclass clazz, jstring j_file, jstring j_savestate, jboolean j_delete_savestate) {} -void Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadSettings([[maybe_unused]] JNIEnv* env, - [[maybe_unused]] jclass clazz) { +void Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadSettings(JNIEnv* env, jclass clazz) { Config{}; } -jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getUserSetting([[maybe_unused]] JNIEnv* env, - [[maybe_unused]] jclass clazz, +jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getUserSetting(JNIEnv* env, jclass clazz, jstring j_game_id, jstring j_section, jstring j_key) { std::string_view game_id = env->GetStringUTFChars(j_game_id, 0); @@ -697,8 +794,7 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getUserSetting([[maybe_unused]] JN return env->NewStringUTF(""); } -void Java_org_yuzu_yuzu_1emu_NativeLibrary_setUserSetting([[maybe_unused]] JNIEnv* env, - [[maybe_unused]] jclass clazz, +void Java_org_yuzu_yuzu_1emu_NativeLibrary_setUserSetting(JNIEnv* env, jclass clazz, jstring j_game_id, jstring j_section, jstring j_key, jstring j_value) { std::string_view game_id = env->GetStringUTFChars(j_game_id, 0); @@ -712,20 +808,18 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_setUserSetting([[maybe_unused]] JNIEn env->ReleaseStringUTFChars(j_value, value.data()); } -void Java_org_yuzu_yuzu_1emu_NativeLibrary_initGameIni([[maybe_unused]] JNIEnv* env, - [[maybe_unused]] jclass clazz, +void Java_org_yuzu_yuzu_1emu_NativeLibrary_initGameIni(JNIEnv* env, jclass clazz, jstring j_game_id) { std::string_view game_id = env->GetStringUTFChars(j_game_id, 0); env->ReleaseStringUTFChars(j_game_id, game_id.data()); } -jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats([[maybe_unused]] JNIEnv* env, - [[maybe_unused]] jclass clazz) { +jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats(JNIEnv* env, jclass clazz) { jdoubleArray j_stats = env->NewDoubleArray(4); if (EmulationSession::GetInstance().IsRunning()) { - const auto results = EmulationSession::GetInstance().PerfStats(); + jconst results = EmulationSession::GetInstance().PerfStats(); // Converting the structure into an array makes it easier to pass it to the frontend double stats[4] = {results.system_fps, results.average_game_fps, results.frametime, @@ -737,11 +831,11 @@ jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats([[maybe_unused]] return j_stats; } -void Java_org_yuzu_yuzu_1emu_utils_DirectoryInitialization_setSysDirectory( - [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jstring j_path) {} +void Java_org_yuzu_yuzu_1emu_utils_DirectoryInitialization_setSysDirectory(JNIEnv* env, + jclass clazz, + jstring j_path) {} -void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2([[maybe_unused]] JNIEnv* env, - [[maybe_unused]] jclass clazz, +void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2(JNIEnv* env, jclass clazz, jstring j_path) { const std::string path = GetJString(env, j_path); @@ -752,8 +846,7 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2([[maybe_unus } } -void Java_org_yuzu_yuzu_1emu_NativeLibrary_logDeviceInfo([[maybe_unused]] JNIEnv* env, - [[maybe_unused]] jclass clazz) { +void Java_org_yuzu_yuzu_1emu_NativeLibrary_logDeviceInfo(JNIEnv* env, jclass clazz) { LOG_INFO(Frontend, "yuzu Version: {}-{}", Common::g_scm_branch, Common::g_scm_desc); LOG_INFO(Frontend, "Host OS: Android API level {}", android_get_device_api_level()); } diff --git a/src/android/app/src/main/jni/native.h b/src/android/app/src/main/jni/native.h deleted file mode 100644 index 24dcbbcb8e..0000000000 --- a/src/android/app/src/main/jni/native.h +++ /dev/null @@ -1,165 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <jni.h> - -// Function calls from the Java side -#ifdef __cplusplus -extern "C" { -#endif - -JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_UnPauseEmulation(JNIEnv* env, - jclass clazz); - -JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_PauseEmulation(JNIEnv* env, - jclass clazz); - -JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_StopEmulation(JNIEnv* env, - jclass clazz); - -JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_ResetRomMetadata(JNIEnv* env, - jclass clazz); - -JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_IsRunning(JNIEnv* env, - jclass clazz); - -JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_isHandheldOnly(JNIEnv* env, - jclass clazz); - -JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_setDeviceType(JNIEnv* env, - jclass clazz, - jstring j_device, - jstring j_type); - -JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadConnectEvent( - JNIEnv* env, jclass clazz, jstring j_device); - -JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadDisconnectEvent( - JNIEnv* env, jclass clazz, jstring j_device); - -JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadEvent( - JNIEnv* env, jclass clazz, jstring j_device, jint j_button, jint action); - -JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadMoveEvent( - JNIEnv* env, jclass clazz, jstring j_device, jint axis, jfloat x, jfloat y); - -JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadAxisEvent( - JNIEnv* env, jclass clazz, jstring j_device, jint axis_id, jfloat axis_val); - -JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onReadNfcTag(JNIEnv* env, - jclass clazz, - jbyteArray j_data); - -JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onRemoveNfcTag(JNIEnv* env, - jclass clazz); - -JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchEvent(JNIEnv* env, - jclass clazz, - jfloat x, jfloat y, - jboolean pressed); - -JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchMoved(JNIEnv* env, jclass clazz, - jfloat x, jfloat y); - -JNIEXPORT jbyteArray JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetIcon(JNIEnv* env, - jclass clazz, - jstring j_file); - -JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetTitle(JNIEnv* env, jclass clazz, - jstring j_filename); - -JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetDescription(JNIEnv* env, - jclass clazz, - jstring j_filename); - -JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetGameId(JNIEnv* env, jclass clazz, - jstring j_filename); - -JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetRegions(JNIEnv* env, - jclass clazz, - jstring j_filename); - -JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetCompany(JNIEnv* env, - jclass clazz, - jstring j_filename); - -JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetGitRevision(JNIEnv* env, - jclass clazz); - -JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SetAppDirectory(JNIEnv* env, - jclass clazz, - jstring j_directory); - -JNIEXPORT void JNICALL -Java_org_yuzu_yuzu_1emu_NativeLibrary_Java_org_yuzu_yuzu_1emu_NativeLibrary_InitializeGpuDriver( - JNIEnv* env, jclass clazz, jstring hook_lib_dir, jstring custom_driver_dir, - jstring custom_driver_name, jstring file_redirect_dir); - -JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_ReloadKeys(JNIEnv* env, - jclass clazz); - -JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_utils_DirectoryInitialization_SetSysDirectory( - JNIEnv* env, jclass clazz, jstring path_); - -JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SetSysDirectory(JNIEnv* env, - jclass clazz, - jstring path); - -JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_InitializeEmulation(JNIEnv* env, - jclass clazz); - -JNIEXPORT jint JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_DefaultCPUCore(JNIEnv* env, - jclass clazz); -JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SetProfiling(JNIEnv* env, jclass clazz, - jboolean enable); - -JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_WriteProfileResults(JNIEnv* env, - jclass clazz); - -JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_NotifyOrientationChange( - JNIEnv* env, jclass clazz, jint layout_option, jint rotation); - -JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_Run__Ljava_lang_String_2( - JNIEnv* env, jclass clazz, jstring j_path); - -JNIEXPORT void JNICALL -Java_org_yuzu_yuzu_1emu_NativeLibrary_Run__Ljava_lang_String_2Ljava_lang_String_2Z( - JNIEnv* env, jclass clazz, jstring j_file, jstring j_savestate, jboolean j_delete_savestate); - -JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SurfaceChanged(JNIEnv* env, - jclass clazz, - jobject surf); - -JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SurfaceDestroyed(JNIEnv* env, - jclass clazz); - -JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_InitGameIni(JNIEnv* env, jclass clazz, - jstring j_game_id); - -JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_ReloadSettings(JNIEnv* env, - jclass clazz); - -JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SetUserSetting( - JNIEnv* env, jclass clazz, jstring j_game_id, jstring j_section, jstring j_key, - jstring j_value); - -JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetUserSetting( - JNIEnv* env, jclass clazz, jstring game_id, jstring section, jstring key); - -JNIEXPORT jdoubleArray JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetPerfStats(JNIEnv* env, - jclass clazz); - -JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_LogDeviceInfo(JNIEnv* env, - jclass clazz); - -JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SubmitInlineKeyboardText( - JNIEnv* env, jclass clazz, jstring j_text); - -JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SubmitInlineKeyboardInput( - JNIEnv* env, jclass clazz, jint j_key_code); - -#ifdef __cplusplus -} -#endif diff --git a/src/android/app/src/main/res/drawable/ic_pip_mute.xml b/src/android/app/src/main/res/drawable/ic_pip_mute.xml new file mode 100644 index 0000000000..a271c5fe8f --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_pip_mute.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24"> + <path + android:fillColor="@android:color/white" + android:pathData="M7,9v6h4l5,5V4l-5,5H7z" /> +</vector> diff --git a/src/android/app/src/main/res/drawable/ic_pip_pause.xml b/src/android/app/src/main/res/drawable/ic_pip_pause.xml new file mode 100644 index 0000000000..4a7d4ea03d --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_pip_pause.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24"> + <path + android:fillColor="@android:color/white" + android:pathData="M6,19h4L10,5L6,5v14zM14,5v14h4L18,5h-4z" /> +</vector> diff --git a/src/android/app/src/main/res/drawable/ic_pip_play.xml b/src/android/app/src/main/res/drawable/ic_pip_play.xml new file mode 100644 index 0000000000..2303a4623f --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_pip_play.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24"> + <path + android:fillColor="@android:color/white" + android:pathData="M8,5v14l11,-7z" /> +</vector> diff --git a/src/android/app/src/main/res/drawable/ic_pip_unmute.xml b/src/android/app/src/main/res/drawable/ic_pip_unmute.xml new file mode 100644 index 0000000000..f7ed0862e2 --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_pip_unmute.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24"> + <path + android:fillColor="@android:color/white" + android:pathData="M3,9v6h4l5,5L12,4L7,9L3,9zM16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02zM14,3.23v2.06c2.89,0.86 5,3.54 5,6.71s-2.11,5.85 -5,6.71v2.06c4.01,-0.91 7,-4.49 7,-8.77s-2.99,-7.86 -7,-8.77z" /> +</vector> diff --git a/src/android/app/src/main/res/drawable/ic_system_update_alt.xml b/src/android/app/src/main/res/drawable/ic_system_update_alt.xml new file mode 100644 index 0000000000..0f6adfdb86 --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_system_update_alt.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="48dp" + android:height="48dp" + android:viewportWidth="960" + android:viewportHeight="960"> + <path + android:fillColor="#FF000000" + android:pathData="M140,800q-24,0 -42,-18t-18,-42v-520q0,-24 18,-42t42,-18h250v60L140,220v520h680v-520L570,220v-60h250q24,0 42,18t18,42v520q0,24 -18,42t-42,18L140,800ZM480,615L280,415l43,-43 127,127v-339h60v339l127,-127 43,43 -200,200Z"/> +</vector> diff --git a/src/android/app/src/main/res/layout/activity_emulation.xml b/src/android/app/src/main/res/layout/activity_emulation.xml index f6360a65b1..139065d3d6 100644 --- a/src/android/app/src/main/res/layout/activity_emulation.xml +++ b/src/android/app/src/main/res/layout/activity_emulation.xml @@ -1,13 +1,9 @@ -<FrameLayout +<androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/frame_content" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:id="@+id/fragment_container" + android:name="androidx.navigation.fragment.NavHostFragment" android:layout_width="match_parent" android:layout_height="match_parent" - android:keepScreenOn="true"> - - <FrameLayout - android:id="@+id/frame_emulation_fragment" - android:layout_width="match_parent" - android:layout_height="match_parent" /> - -</FrameLayout> + android:keepScreenOn="true" + app:defaultNavHost="true" /> diff --git a/src/android/app/src/main/res/layout/fragment_emulation.xml b/src/android/app/src/main/res/layout/fragment_emulation.xml index 09b789b6b0..e54a10e8ff 100644 --- a/src/android/app/src/main/res/layout/fragment_emulation.xml +++ b/src/android/app/src/main/res/layout/fragment_emulation.xml @@ -12,49 +12,65 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - <!-- This is what everything is rendered to during emulation --> - <org.yuzu.yuzu_emu.views.FixedRatioSurfaceView - android:id="@+id/surface_emulation" + <FrameLayout + android:id="@+id/emulation_container" android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_gravity="center" - android:focusable="false" - android:focusableInTouchMode="false" /> + android:layout_height="match_parent"> + + <!-- This is what everything is rendered to during emulation --> + <org.yuzu.yuzu_emu.views.FixedRatioSurfaceView + android:id="@+id/surface_emulation" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_gravity="center" + android:focusable="false" + android:focusableInTouchMode="false" /> + + </FrameLayout> <FrameLayout - android:id="@+id/overlay_container" + android:id="@+id/input_container" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="bottom"> - <!-- This is the onscreen input overlay --> - <org.yuzu.yuzu_emu.overlay.InputOverlay - android:id="@+id/surface_input_overlay" + <!-- This is the onscreen input overlay --> + <org.yuzu.yuzu_emu.overlay.InputOverlay + android:id="@+id/surface_input_overlay" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_gravity="center" + android:focusable="true" + android:focusableInTouchMode="true" /> + + <Button + style="@style/Widget.Material3.Button.ElevatedButton" + android:id="@+id/done_control_config" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:text="@string/emulation_done" + android:visibility="gone" /> + + </FrameLayout> + + <FrameLayout + android:id="@+id/overlay_container" android:layout_width="match_parent" - android:layout_height="match_parent" - android:focusable="true" - android:focusableInTouchMode="true" /> + android:layout_height="match_parent"> - <TextView - android:id="@+id/show_fps_text" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="left" - android:clickable="false" - android:focusable="false" - android:shadowColor="@android:color/black" - android:textColor="@android:color/white" - android:textSize="12sp" - tools:ignore="RtlHardcoded" /> + <TextView + android:id="@+id/show_fps_text" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="left" + android:clickable="false" + android:focusable="false" + android:shadowColor="@android:color/black" + android:textColor="@android:color/white" + android:textSize="12sp" + tools:ignore="RtlHardcoded" /> - <Button - style="@style/Widget.Material3.Button.ElevatedButton" - android:id="@+id/done_control_config" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center" - android:text="@string/emulation_done" - android:visibility="gone" /> </FrameLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout> diff --git a/src/android/app/src/main/res/layout/list_item_setting_switch.xml b/src/android/app/src/main/res/layout/list_item_setting_switch.xml index 599d845ade..a5767adee1 100644 --- a/src/android/app/src/main/res/layout/list_item_setting_switch.xml +++ b/src/android/app/src/main/res/layout/list_item_setting_switch.xml @@ -1,16 +1,16 @@ <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" - xmlns:app="http://schemas.android.com/apk/res-auto" android:background="?android:attr/selectableItemBackground" android:clickable="true" android:focusable="true" android:minHeight="72dp" + android:paddingVertical="@dimen/spacing_large" android:paddingStart="@dimen/spacing_large" - android:paddingEnd="24dp" - android:paddingVertical="@dimen/spacing_large"> + android:paddingEnd="24dp"> <com.google.android.material.materialswitch.MaterialSwitch android:id="@+id/switch_widget" @@ -19,32 +19,35 @@ android:layout_alignParentEnd="true" android:layout_centerVertical="true" /> - <com.google.android.material.textview.MaterialTextView - style="@style/TextAppearance.Material3.BodySmall" - android:id="@+id/text_setting_description" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentStart="true" - android:layout_alignStart="@+id/text_setting_name" - android:layout_below="@+id/text_setting_name" - android:layout_marginEnd="@dimen/spacing_large" - android:layout_marginTop="@dimen/spacing_small" - android:layout_toStartOf="@+id/switch_widget" - android:textAlignment="viewStart" - tools:text="@string/frame_limit_enable_description" /> - - <com.google.android.material.textview.MaterialTextView - style="@style/TextAppearance.Material3.HeadlineMedium" - android:id="@+id/text_setting_name" - android:layout_width="0dp" + <LinearLayout + android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_alignParentStart="true" android:layout_alignParentTop="true" + android:layout_centerVertical="true" android:layout_marginEnd="@dimen/spacing_large" android:layout_toStartOf="@+id/switch_widget" - android:textSize="16sp" - android:textAlignment="viewStart" - app:lineHeight="28dp" - tools:text="@string/frame_limit_enable" /> + android:gravity="center_vertical" + android:orientation="vertical"> + + <com.google.android.material.textview.MaterialTextView + android:id="@+id/text_setting_name" + style="@style/TextAppearance.Material3.HeadlineMedium" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAlignment="viewStart" + android:textSize="16sp" + app:lineHeight="28dp" + tools:text="@string/frame_limit_enable" /> + + <com.google.android.material.textview.MaterialTextView + android:id="@+id/text_setting_description" + style="@style/TextAppearance.Material3.BodySmall" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/spacing_small" + android:textAlignment="viewStart" + tools:text="@string/frame_limit_enable_description" /> + + </LinearLayout> </RelativeLayout> diff --git a/src/android/app/src/main/res/layout/list_item_settings_header.xml b/src/android/app/src/main/res/layout/list_item_settings_header.xml index abd24df6f1..cf85bc0da8 100644 --- a/src/android/app/src/main/res/layout/list_item_settings_header.xml +++ b/src/android/app/src/main/res/layout/list_item_settings_header.xml @@ -1,20 +1,14 @@ <?xml version="1.0" encoding="utf-8"?> -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" +<com.google.android.material.textview.MaterialTextView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/text_header_name" + style="@style/TextAppearance.Material3.TitleSmall" android:layout_width="match_parent" - android:layout_height="48dp" - android:paddingVertical="4dp" - android:paddingHorizontal="@dimen/spacing_large"> - - <com.google.android.material.textview.MaterialTextView - style="@style/TextAppearance.Material3.TitleSmall" - android:id="@+id/text_header_name" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_gravity="start|center_vertical" - android:textColor="?attr/colorPrimary" - android:textAlignment="viewStart" - android:textStyle="bold" - tools:text="CPU Settings" /> - -</FrameLayout> + android:layout_height="wrap_content" + android:layout_gravity="start|center_vertical" + android:paddingHorizontal="@dimen/spacing_large" + android:paddingVertical="16dp" + android:textAlignment="viewStart" + android:textColor="?attr/colorPrimary" + android:textStyle="bold" + tools:text="CPU Settings" /> diff --git a/src/android/app/src/main/res/navigation/emulation_navigation.xml b/src/android/app/src/main/res/navigation/emulation_navigation.xml new file mode 100644 index 0000000000..8208f4c2c4 --- /dev/null +++ b/src/android/app/src/main/res/navigation/emulation_navigation.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<navigation xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/emulation_navigation" + app:startDestination="@id/emulationFragment"> + + <fragment + android:id="@+id/emulationFragment" + android:name="org.yuzu.yuzu_emu.fragments.EmulationFragment" + android:label="fragment_emulation" + tools:layout="@layout/fragment_emulation" > + <argument + android:name="game" + app:argType="org.yuzu.yuzu_emu.model.Game" /> + </fragment> + +</navigation> diff --git a/src/android/app/src/main/res/navigation/home_navigation.xml b/src/android/app/src/main/res/navigation/home_navigation.xml index 48072683ea..fcebba7266 100644 --- a/src/android/app/src/main/res/navigation/home_navigation.xml +++ b/src/android/app/src/main/res/navigation/home_navigation.xml @@ -56,4 +56,18 @@ android:name="org.yuzu.yuzu_emu.fragments.LicensesFragment" android:label="LicensesFragment" /> + <activity + android:id="@+id/emulationActivity" + android:name="org.yuzu.yuzu_emu.activities.EmulationActivity" + android:label="EmulationActivity"> + <argument + android:name="game" + app:argType="org.yuzu.yuzu_emu.model.Game" /> + </activity> + + <action + android:id="@+id/action_global_emulationActivity" + app:destination="@id/emulationActivity" + app:launchSingleTop="true" /> + </navigation> diff --git a/src/android/app/src/main/res/values-de/strings.xml b/src/android/app/src/main/res/values-de/strings.xml new file mode 100644 index 0000000000..969223ef8d --- /dev/null +++ b/src/android/app/src/main/res/values-de/strings.xml @@ -0,0 +1,332 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <string name="app_disclaimer">Diese Software kann Spiele für die Nintendo Switch abspielen. Keine Spiele oder Spielekeys sind enthalten.<br /><br />Bevor du beginnst, bitte halte deine <![CDATA[<b> prod.keys </b>]]> auf deinem Gerät bereit. .<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Mehr Infos</a>]]></string> + <string name="emulation_notification_channel_name">Emulation ist aktiv</string> + <string name="emulation_notification_channel_description">Zeigt eine dauerhafte Benachrichtigung an, wenn die Emulation läuft.</string> + <string name="emulation_notification_running">yuzu läuft</string> + <string name="notice_notification_channel_name">Hinweise und Fehler</string> + <string name="notice_notification_channel_description">Zeigt Benachrichtigungen an, wenn etwas schief läuft.</string> + <string name="notification_permission_not_granted">Berechtigung für Benachrichtigungen nicht erlaubt!</string> + + <!-- Setup strings --> + <string name="welcome">Willkommen!</string> + <string name="welcome_description">Erfahre wie man <b>yuzu</b> einrichtet und beginne mit der Emulation.</string> + <string name="get_started">Erste Schritte</string> + <string name="keys">Schlüssel</string> + <string name="keys_description">Wähle deine <b>prod.keys</b> Datei mit dem Button unten aus.</string> + <string name="select_keys">Schlüssel auswählen</string> + <string name="games">Spiele</string> + <string name="games_description">Wähle mit dem Knopf unten den <b>Spiele</b>-Ordner aus.</string> + <string name="done">Fertig</string> + <string name="done_description">Wir können loslegen.\nViel Spaß!</string> + <string name="text_continue">Fortsetzen</string> + <string name="next">Weiter</string> + <string name="back">Zurück</string> + <string name="add_games">Spiele hinzufügen</string> + <string name="add_games_description">Spieleverzeichnis auswählen</string> + + <!-- Home strings --> + <string name="home_games">Spiele</string> + <string name="home_search">Suche</string> + <string name="home_settings">Einstellungen</string> + <string name="empty_gamelist">Es wurden keine Dateien gefunden oder es wurde noch kein Spielverzeichnis ausgewählt.</string> + <string name="search_and_filter_games">Spiele suchen und filtern</string> + <string name="select_games_folder">Spieleverzeichnis auswählen</string> + <string name="select_games_folder_description">Erlaubt yuzu die Spieleliste zu füllen</string> + <string name="add_games_warning">Auswahl des Spieleverzeichnisses überspringen?</string> + <string name="add_games_warning_description">Spiele werden in der Spieleliste nicht angezeigt, wenn kein Ordner ausgewählt ist.</string> + <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> + <string name="home_search_games">Spiele suchen</string> + <string name="games_dir_selected">Spieleverzeichnis ausgewählt</string> + <string name="install_prod_keys">prod.keys installieren</string> + <string name="install_prod_keys_description">Zum Entschlüsseln von Spielen benötigt</string> + <string name="install_prod_keys_warning">Hinzufügen der Schlüssel überspringen?</string> + <string name="install_prod_keys_warning_description">Für die Emulation von Spielen sind gültige Schlüssel erforderlich. Wenn du fortfährst, funktionieren nur Homebrew-Anwendungen.</string> + <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string> + <string name="notifications">Benachrichtigungen</string> + <string name="notifications_description">Erteile mit dem Knopf unten die Berechtigung, Benachrichtigungen zu senden.</string> + <string name="give_permission">Berechtigung erteilen</string> + <string name="notification_warning_description">yuzu wird dich nicht über wichtige Informationen benachrichtigen können.</string> + <string name="permission_denied">Zugriff verweigert</string> + <string name="permission_denied_description">Du hast diese Berechtigung zu oft verweigert und musst sie nun manuell in den Systemeinstellungen erteilen.</string> + <string name="about">Über</string> + <string name="about_description">Build-Version, Credits und mehr</string> + <string name="warning_help">Hilfe</string> + <string name="warning_skip">Überspringen</string> + <string name="warning_cancel">Abbrechen</string> + <string name="install_amiibo_keys">Amiibo-Schlüssel installieren</string> + <string name="install_amiibo_keys_description">Benötigt um Amiibos im Spiel zu verwenden</string> + <string name="invalid_keys_file">Ungültige Schlüsseldatei ausgewählt</string> + <string name="install_keys_success">Schlüssel erfolgreich installiert</string> + <string name="reading_keys_failure">Fehler beim Lesen der Schlüssel</string> + <string name="invalid_keys_error">Ungültige Schlüssel</string> + <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> + <string name="install_gpu_driver">GPU-Treiber installieren</string> + <string name="install_gpu_driver_description">Alternative Treiber für eventuell bessere Leistung oder Genauigkeit installieren</string> + <string name="advanced_settings">Erweiterte Einstellungen</string> + <string name="settings_description">Emulatoreinstellungen konfigurieren</string> + <string name="search_recently_played">Kürzlich gespielt</string> + <string name="search_recently_added">Kürzlich hinzugefügt</string> + <string name="search_retail">Spiele</string> + <string name="search_homebrew">Homebrew</string> + <string name="open_user_folder">yuzu-Ordner öffnen</string> + <string name="open_user_folder_description">yuzu\'s interne Dateien verwalten</string> + <string name="theme_and_color_description">Das Aussehen der App ändern</string> + <string name="no_file_manager">Kein Dateimanager gefunden</string> + <string name="notification_no_directory_link">yuzu-Verzeichnis konnte nicht geöffnet werden</string> + <string name="notification_no_directory_link_description">Bitte suche den Benutzerordner manuell über die Seitenleiste des Dateimanagers.</string> + <string name="manage_save_data">Speicherdaten verwalten</string> + <string name="manage_save_data_description">Speicherdaten gefunden. Bitte wähle unten eine Option aus.</string> + <string name="import_export_saves_description">Speicherdaten importieren oder exportieren</string> + <string name="import_export_saves_no_profile">Keine Speicherdaten gefunden. Bitte starte ein Spiel und versuche es erneut.</string> + <string name="save_file_imported_success">Erfolgreich importiert</string> + <string name="save_file_invalid_zip_structure">Ungültige Speicherverzeichnisstruktur</string> + <string name="save_file_invalid_zip_structure_description">Der erste Unterordnername muss die Titel-ID des Spiels sein.</string> + <string name="import_saves">Importieren</string> + <string name="export_saves">Exportieren</string> + + <!-- About screen strings --> + <string name="gaia_is_not_real">Gaia ist nicht real</string> + <string name="copied_to_clipboard">In die Zwischenablage kopiert</string> + <string name="about_app_description">Ein quelloffener Switch-Emulator</string> + <string name="contributors">Beitragende</string> + <string name="contributors_description">Gemacht mit \u2764 vom yuzu Team</string> + <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> + <string name="build">Build</string> + <string name="support_link">https://discord.gg/u77vRWY</string> + <string name="website_link">https://yuzu-emu.org/</string> + <string name="github_link">https://github.com/yuzu-emu</string> + + <!-- Early access upgrade strings --> + <string name="early_access">Early Access</string> + <string name="get_early_access">Early Access bekommen</string> + <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string> + <string name="get_early_access_description">Neueste Features, frühzeitiger Zugriff auf Updates und mehr</string> + <string name="early_access_benefits">Early Access Vorteile</string> + <string name="cutting_edge_features">Neueste Features</string> + <string name="early_access_updates">Früherer Zugriff auf Updates</string> + <string name="no_manual_installation">Keine manuelle Installation</string> + <string name="prioritized_support">Priorisierte Unterstützung</string> + <string name="our_eternal_gratitude">Unsere ewige Dankbarkeit</string> + <string name="are_you_interested">Bist du interessiert?</string> + + <!-- General settings strings --> + <string name="frame_limit_enable">Geschwindigkeitsbegrenzung aktivieren</string> + <string name="frame_limit_enable_description">Wenn aktiviert, wird die Emulationsgeschwindigkeit auf einen Prozentsatz der normalen Geschwindigkeit begrenzt.</string> + <string name="frame_limit_slider">Geschwindkeitsbegrenzung in Prozent</string> + <string name="frame_limit_slider_description">Legt den Prozentsatz der Bergrenzung der Emulationsgeschwindigkeit fest. Mit dem Standardwert von 100% wird die Emulation auf die normale Geschwindigkeit begrenzt. Höhere oder niedrigere Werte erhöhen oder verringern die Geschwindigkeitsbegrenzung.</string> + <string name="cpu_accuracy">CPU-Genauigkeit</string> + + <!-- System settings strings --> + <string name="use_docked_mode">Dock-Modus</string> + <string name="use_docked_mode_description">Emuliert im Dock-Modus, was die Auflösung verbessert, aber die Leistung senkt.</string> + <string name="emulated_region">Emulierte Region</string> + <string name="emulated_language">Emulierte Sprache</string> + <string name="select_rtc_date">RTC-Datum auswählen</string> + <string name="select_rtc_time">RTC-Zeit auswählen</string> + <string name="use_custom_rtc">Benutzerdefinierte RTC aktivieren</string> + <string name="use_custom_rtc_description">Mit dieser Einstellung kann eine benutzerdefinierte Echtzeituhr unabhängig von der aktuellen Systemzeit verwendet werden.</string> + <string name="set_custom_rtc">Benutzerdefinierte RTC einstellen</string> + + <!-- Graphics settings strings --> + <string name="renderer_api">API</string> + <string name="renderer_accuracy">Genauigkeitsstufe</string> + <string name="renderer_resolution">Auflösung</string> + <string name="renderer_vsync">VSync-Modus</string> + <string name="renderer_aspect_ratio">Seitenverhältnis</string> + <string name="renderer_scaling_filter">Fensteranpassungsfilter</string> + <string name="renderer_anti_aliasing">Kantenglättungs-Methode</string> + <string name="renderer_force_max_clock">Maximale Taktfrequenz erzwingen (nur Adreno)</string> + <string name="renderer_force_max_clock_description">Erzwingt den Betrieb der GPU mit der maximal möglichen Taktfrequenz (Temperaturbeschränkungen werden weiterhin angewendet).</string> + <string name="renderer_asynchronous_shaders">Asynchrone Shader nutzen</string> + <string name="renderer_asynchronous_shaders_description">Kompiliert Shader asynchron, was Ruckler reduziert, aber zu Glitches führen kann.</string> + <string name="renderer_debug">Grafik-Debugging aktivieren</string> + <string name="renderer_debug_description">Wenn aktiviert, schaltet die Grafik-API in einen langsameren Debugging-Modus.</string> + <string name="use_disk_shader_cache">Nutze Festplatten-Shader-Cache</string> + <string name="use_disk_shader_cache_description">Ruckeln wird durch das Speichern und Laden von generierten Shadern auf der Festplatte reduziert.</string> + + <!-- Audio settings strings --> + <string name="audio_volume">Lautstärke</string> + <string name="audio_volume_description">Legt die Lautstärke der Audioausgabe fest.</string> + + <!-- Miscellaneous --> + <string name="slider_default">Standard</string> + <string name="ini_saved">Einstellungen gespeichert</string> + <string name="gameid_saved">Einstellungen für %1$s gespeichert</string> + <string name="error_saving">Fehler beim Speichern von %1$s.ini: %2$s</string> + <string name="loading">Lädt...</string> + <string name="reset_setting_confirmation">Möchtest du diese Einstellung auf den Standardwert zurücksetzen?</string> + <string name="reset_to_default">Auf Standard zurücksetzen</string> + <string name="reset_all_settings">Alle Einstellungen zurücksetzen?</string> + <string name="reset_all_settings_description">Alle erweiterten Einstellungen werden auf ihren Standardwert zurückgesetzt. Dies kann nicht rückgängig gemacht werden.</string> + <string name="settings_reset">Einstellungen zurückgesetzt</string> + <string name="close">Schließen</string> + <string name="learn_more">Mehr erfahren</string> + + <!-- GPU driver installation --> + <string name="select_gpu_driver">GPU-Treiber auswählen</string> + <string name="select_gpu_driver_title">Möchtest du deinen aktuellen GPU-Treiber ersetzen?</string> + <string name="select_gpu_driver_install">Installieren</string> + <string name="select_gpu_driver_default">Standard</string> + <string name="select_gpu_driver_install_success">%s wurde installiert</string> + <string name="select_gpu_driver_use_default">Standard GPU-Treiber wird verwendet</string> + <string name="select_gpu_driver_error">Ungültiger Treiber ausgewählt, Standard-Treiber wird verwendet!</string> + <string name="system_gpu_driver">System GPU-Treiber</string> + <string name="installing_driver">Treiber wird installiert...</string> + + <!-- Preferences Screen --> + <string name="preferences_settings">Einstellungen</string> + <string name="preferences_general">Allgemein</string> + <string name="preferences_system">System</string> + <string name="preferences_graphics">Grafik</string> + <string name="preferences_audio">Audio</string> + <string name="preferences_theme">Theme und Farbe</string> + + <!-- ROM loading errors --> + <string name="loader_error_encrypted">Das ROM ist verschlüsselt</string> + <string name="loader_error_encrypted_keys_description"><![CDATA[Bitte stelle sicher dass die <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> Datei installiert ist, damit Spiele entschlüsselt werden können.]]></string> + <string name="loader_error_video_core">Bei der Initialisierung des Videokerns ist ein Fehler aufgetreten</string> + <string name="loader_error_video_core_description">Dies wird normalerweise durch einen inkompatiblen GPU-Treiber verursacht. Die Installation eines passenden GPU-Treibers kann dieses Problem beheben.</string> + <string name="loader_error_invalid_format">ROM konnte nicht geladen werden</string> + <string name="loader_error_file_not_found">ROM-Datei existiert nicht</string> + + <!-- Emulation Menu --> + <string name="emulation_exit">Emulation beenden</string> + <string name="emulation_done">Fertig</string> + <string name="emulation_fps_counter">FPS Zähler</string> + <string name="emulation_toggle_controls">Steuerung umschalten</string> + <string name="emulation_rel_stick_center">Relative Stick-Mitte</string> + <string name="emulation_dpad_slide">DPad Slide</string> + <string name="emulation_haptics">Haptik</string> + <string name="emulation_show_overlay">Overlay anzeigen</string> + <string name="emulation_toggle_all">Alle umschalten</string> + <string name="emulation_control_adjust">Overlay anpassen</string> + <string name="emulation_control_scale">Größe</string> + <string name="emulation_control_opacity">Transparenz</string> + <string name="emulation_touch_overlay_reset">Overlay zurücksetzen</string> + <string name="emulation_touch_overlay_edit">Overlay bearbeiten</string> + <string name="emulation_pause">Emulation pausieren</string> + <string name="emulation_unpause">Emulation fortsetzen</string> + <string name="emulation_input_overlay">Overlay-Optionen</string> + <string name="emulation_game_loading">Spiel lädt…</string> + + <string name="load_settings">Lädt Einstellungen...</string> + + <!-- Software keyboard --> + <string name="software_keyboard">Software-Tastatur</string> + + <!-- Errors and warnings --> + <string name="abort_button">Abbrechen</string> + <string name="continue_button">Fortsetzen</string> + <string name="system_archive_not_found">Systemarchiv nicht gefunden</string> + <string name="system_archive_general">Ein System-Archiv</string> + <string name="save_load_error">Speicher-/Ladefehler</string> + <string name="fatal_error">Schwerwiegender Fehler</string> + <string name="fatal_error_message">Ein schwerwiegender Fehler ist aufgetreten. Einzelheiten wurden im Log protokolliert.\nDas Fortsetzen der Emulation kann zu Abstürzen und Bugs führen.</string> + <string name="performance_warning">Das Deaktivieren dieser Einstellung führt zu erheblichen Leistungsverlusten! Für ein optimales Erlebnis wird empfohlen, sie aktiviert zu lassen.</string> + + <!-- Region Names --> + <string name="region_japan">Japan</string> + <string name="region_usa">USA</string> + <string name="region_europe">Europa</string> + <string name="region_australia">Australien</string> + <string name="region_china">China</string> + <string name="region_korea">Korea</string> + <string name="region_taiwan">Taiwan</string> + + <!-- Language Names --> + <string name="language_japanese">Japanisch (日本語)</string> + <string name="language_english">Englisch</string> + <string name="language_french">Französisch (Français)</string> + <string name="langauge_german">Deutsch (German)</string> + <string name="language_italian">Italienisch (Italiano)</string> + <string name="language_spanish">Spanisch (Español)</string> + <string name="language_chinese">Chinesisch (简体中文)</string> + <string name="language_korean">Koreanisch (한국어)</string> + <string name="language_dutch">Niederländisch (Nederlands)</string> + <string name="language_portuguese">Portugiesisch (Português)</string> + <string name="language_russian">Russisch (Русский)</string> + <string name="language_taiwanese">Taiwanesisch (台湾)</string> + <string name="language_british_english">Britisches Englisch</string> + <string name="language_canadian_french">Kanadisches Französisch (Français canadien)</string> + <string name="language_latin_american_spanish">Lateinamerikanisches Spanisch (Español latinoamericano)</string> + <string name="language_simplified_chinese">Vereinfachtes Chinesisch (简体中文)</string> + <string name="language_traditional_chinese">Traditionelles Chinesisch (正體中文)</string> + <string name="language_brazilian_portuguese">Brasilianisches Portugiesisch (Português do Brasil)</string> + + <!-- Renderer APIs --> + <string name="renderer_vulkan">Vulkan</string> + <string name="renderer_none">Keiner</string> + + <!-- Renderer Accuracy --> + <string name="renderer_accuracy_normal">Normal</string> + <string name="renderer_accuracy_high">Hoch</string> + <string name="renderer_accuracy_extreme">Extrem (Langsam)</string> + + <!-- Resolutions --> + <string name="resolution_half">0.5X (360p/540p)</string> + <string name="resolution_three_quarter">0.75X (540p/810p)</string> + <string name="resolution_one">1X (720p/1080p)</string> + <string name="resolution_two">2X (1440p/2160p) (Langsam)</string> + <string name="resolution_three">3X (2160p/3240p) (Langsam)</string> + <string name="resolution_four">4X (2880p/4320p) (Langsam)</string> + + <!-- Renderer VSync --> + <string name="renderer_vsync_immediate">Direkt (Aus)</string> + <string name="renderer_vsync_mailbox">Mailbox</string> + <string name="renderer_vsync_fifo">FIFO (An)</string> + <string name="renderer_vsync_fifo_relaxed">FIFO Relaxed</string> + + <!-- Scaling Filters --> + <string name="scaling_filter_nearest_neighbor">Nächste-Nachbarn</string> + <string name="scaling_filter_bilinear">Bilinear</string> + <string name="scaling_filter_bicubic">Bikubisch</string> + <string name="scaling_filter_gaussian">Gaussian</string> + <string name="scaling_filter_scale_force">ScaleForce</string> + <string name="scaling_filter_fsr">AMD FidelityFX™ Super Resolution</string> + + <!-- Anti-Aliasing --> + <string name="anti_aliasing_none">Keiner</string> + <string name="anti_aliasing_fxaa">FXAA</string> + <string name="anti_aliasing_smaa">SMAA</string> + + <!-- Aspect Ratios --> + <string name="ratio_default">Standard (16:9)</string> + <string name="ratio_force_four_three">4:3 erzwingen</string> + <string name="ratio_force_twenty_one_nine">21:9 erzwingen</string> + <string name="ratio_force_sixteen_ten">Erzwinge 16:10</string> + <string name="ratio_stretch">Auf Fenster anpassen</string> + + <!-- CPU Accuracy --> + <string name="cpu_accuracy_accurate">Akkurat</string> + <string name="cpu_accuracy_unsafe">Unsicher</string> + <string name="cpu_accuracy_paranoid">Paranoid (Langsam)</string> + + <!-- Gamepad Buttons --> + <string name="gamepad_d_pad">Steuerkreuz</string> + <string name="gamepad_left_stick">Linker Analogstick</string> + <string name="gamepad_right_stick">Rechter Analogstick</string> + <string name="gamepad_home">Home</string> + <string name="gamepad_screenshot">Screenshot</string> + + <!-- Disk shader cache --> + <string name="preparing_shaders">Shader werden vorbereitet</string> + <string name="building_shaders">Shader werden erstellt</string> + + <!-- Theme options --> + <string name="change_app_theme">App-Theme ändern</string> + <string name="theme_default">Standard</string> + <string name="theme_material_you">Material You</string> + + <!-- Theme Modes --> + <string name="change_theme_mode">Theme-Modus ändern</string> + <string name="theme_mode_follow_system">System folgen</string> + <string name="theme_mode_light">Hell</string> + <string name="theme_mode_dark">Dunkel</string> + + <!-- Black backgrounds theme --> + <string name="use_black_backgrounds">Schwarze Hintergünde verwenden</string> + <string name="use_black_backgrounds_description">Bei Verwendung des dunklen Themes, schwarze Hintergründe verwenden.</string> + +</resources> diff --git a/src/android/app/src/main/res/values-es/strings.xml b/src/android/app/src/main/res/values-es/strings.xml new file mode 100644 index 0000000000..986e80e502 --- /dev/null +++ b/src/android/app/src/main/res/values-es/strings.xml @@ -0,0 +1,337 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <string name="app_disclaimer">Este software ejecuta juegos para la videoconsola Nintendo Switch. Los videojuegos o keys no vienen incluidos.<br /><br />Antes de empezar, por favor, localice el archivo <![CDATA[<b> prod.keys </b>]]>en el almacenamiento de su dispositivo..<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Saber más</a>]]></string> + <string name="emulation_notification_channel_name">Emulación activa</string> + <string name="emulation_notification_channel_description">Muestra una notificación persistente cuando la emulación está activa.</string> + <string name="emulation_notification_running">yuzu esta ejecutándose</string> + <string name="notice_notification_channel_name">Avisos y errores</string> + <string name="notice_notification_channel_description">Mostrar notificaciones cuándo algo vaya mal.</string> + <string name="notification_permission_not_granted">¡Permisos de notificación no concedidos!</string> + + <!-- Setup strings --> + <string name="welcome">¡Bienvenido!</string> + <string name="welcome_description">Aprende cómo configurar <b>yuzu</b> y avanza a la emulación.</string> + <string name="get_started">Empezar</string> + <string name="keys">Claves</string> + <string name="keys_description">Selecciona el archivo <b>prod.keys</b> utilizando el botón de abajo.</string> + <string name="select_keys">Seleccionar las claves</string> + <string name="games">Juegos</string> + <string name="games_description">Selecciona la carpeta <b>Games</b> utilizando el botón de abajo</string> + <string name="done">Hecho</string> + <string name="done_description">Todo listo.\n¡Disfrute de sus juegos!</string> + <string name="text_continue">Continuar</string> + <string name="next">Siguiente</string> + <string name="back">Atrás</string> + <string name="add_games">Añadir Juegos</string> + <string name="add_games_description">Selecciona la carpeta de juegos</string> + + <!-- Home strings --> + <string name="home_games">Juegos</string> + <string name="home_search">Buscar</string> + <string name="home_settings">Ajustes</string> + <string name="empty_gamelist">No se ha encontrado ningún archivo o aún no se ha seleccionado ningún directorio de juegos.</string> + <string name="search_and_filter_games">Busca y filtra juegos</string> + <string name="select_games_folder">Seleccionar carpeta de juegos</string> + <string name="select_games_folder_description">Permite que yuzu llene la lista de juegos</string> + <string name="add_games_warning">¿Omitir la selección de la carpeta de juegos?</string> + <string name="add_games_warning_description">No se mostrará ningún juego si no se ha seleccionado una carpeta de juegos.</string> + <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> + <string name="home_search_games">Buscar Juegos</string> + <string name="games_dir_selected">Directorio de juegos seleccionado</string> + <string name="install_prod_keys">Instalar prod.keys</string> + <string name="install_prod_keys_description">Requerido para descifrar juegos</string> + <string name="install_prod_keys_warning">¿Omitir agregar claves?</string> + <string name="install_prod_keys_warning_description">Se requieren claves válidas para emular juegos. Solo las aplicaciones homebrew funcionarán si continúas.</string> + <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string> + <string name="notifications">Notificaciones</string> + <string name="notifications_description">Otorgue el permiso de notificación con el botón de abajo.</string> + <string name="give_permission">Conceder permiso</string> + <string name="notification_warning">¿Omitir conceder el permiso de notificación?</string> + <string name="notification_warning_description">yuzu no podrá notificarte información importante.</string> + <string name="permission_denied">Permiso denegado</string> + <string name="permission_denied_description">Negó este permiso demasiadas veces y ahora debe otorgarlo manualmente en la configuración del sistema.</string> + <string name="about">Acerca de</string> + <string name="about_description">Versión, créditos y más</string> + <string name="warning_help">Ayuda</string> + <string name="warning_skip">Siguiente</string> + <string name="warning_cancel">Cancelar</string> + <string name="install_amiibo_keys">Instalar clave de Amiiboo</string> + <string name="install_amiibo_keys_description">Necesario para usar Amiibo en el juego</string> + <string name="invalid_keys_file">Archivo de claves inválido seleccionado</string> + <string name="install_keys_success">Claves instaladas correctamente</string> + <string name="reading_keys_failure">Error al leer las claves de cifrado</string> + <string name="invalid_keys_error">Claves de cifrado no válidas</string> + <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> + <string name="install_keys_failure_description">El archivo seleccionado es incorrecto o está corrupto. Vuelva a redumpear sus claves.</string> + <string name="install_gpu_driver">Instalar driver de GPU</string> + <string name="install_gpu_driver_description">Instale drivers alternativos para obtener un rendimiento o una precisión potencialmente mejores</string> + <string name="advanced_settings">Opciones avanzadas</string> + <string name="settings_description">Configurar las opciones del emulador</string> + <string name="search_recently_played">Jugado recientemente</string> + <string name="search_recently_added">Añadido recientemente</string> + <string name="search_retail">Juegos</string> + <string name="search_homebrew">Homebrew</string> + <string name="open_user_folder">Abrir la carpeta de yuzu</string> + <string name="open_user_folder_description">Administrar los archivos internos de yuzu</string> + <string name="theme_and_color_description">Modificar la apariencia de la aplicación</string> + <string name="no_file_manager">Explorador de archivos no encontrado</string> + <string name="notification_no_directory_link">No se pudo abrir la carpeta yuzu</string> + <string name="notification_no_directory_link_description">Por favor, busque la carpeta user con el panel lateral del explorador de archivos de forma manual.</string> + <string name="manage_save_data">Administrar datos de guardado</string> + <string name="manage_save_data_description">Guardar los datos encontrados. Por favor, seleccione una opción de abajo.</string> + <string name="import_export_saves_description">Importar o exportar archivos de guardado</string> + <string name="import_export_saves_no_profile">No se han encontrado datos de guardado. Por favor, ejecute un juego y vuelva a intentarlo.</string> + <string name="save_file_imported_success">Importado correctamente</string> + <string name="save_file_invalid_zip_structure">Estructura del directorio de guardado no válido</string> + <string name="save_file_invalid_zip_structure_description">El nombre de la primera subcarpeta debe ser el Title ID del juego.</string> + <string name="import_saves">Importar</string> + <string name="export_saves">Exportar</string> + + <!-- About screen strings --> + <string name="gaia_is_not_real">Gaia no es real</string> + <string name="copied_to_clipboard">Copiado al portapapeles</string> + <string name="about_app_description">Un emulador de Switch de código abierto</string> + <string name="contributors">Contribuidores</string> + <string name="contributors_description">Hecho con \u2764 del equipo yuzu</string> + <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> + <string name="build">Versión</string> + <string name="support_link">https://discord.gg/u77vRWY</string> + <string name="website_link">https://yuzu-emu.org/</string> + <string name="github_link">https://github.com/yuzu-emu</string> + + <!-- Early access upgrade strings --> + <string name="early_access">Early Access</string> + <string name="get_early_access">Conseguir Early Access</string> + <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string> + <string name="get_early_access_description">Funciones de vanguardia, acceso anticipado a actualizaciones y más</string> + <string name="early_access_benefits">Beneficios Early Access</string> + <string name="cutting_edge_features">Características de vanguardia</string> + <string name="early_access_updates">Acceso anticipado a las actualizaciones</string> + <string name="no_manual_installation">Sin instalación manual</string> + <string name="prioritized_support">Soporte prioritario</string> + <string name="helping_game_preservation">Ayudarás a la preservación de juegos</string> + <string name="our_eternal_gratitude">Nuestra eterna gratitud</string> + <string name="are_you_interested">¿Estás interesado?</string> + + <!-- General settings strings --> + <string name="frame_limit_enable">Activar limite de velocidad</string> + <string name="frame_limit_enable_description">Cuando está habilitado, la velocidad de emulación se limitará a un porcentaje específico de la velocidad normal.</string> + <string name="frame_limit_slider">Limitar porcentaje de velocidad</string> + <string name="frame_limit_slider_description">Especifica el porcentaje para limitar la velocidad de emulación. Con el valor predeterminado del 100 %, la emulación se limitará a la velocidad normal. Valores más altos o más bajos aumentarán o disminuirán el límite de velocidad.</string> + <string name="cpu_accuracy">Precisión de CPU</string> + + <!-- System settings strings --> + <string name="use_docked_mode">Modo sobremesa</string> + <string name="use_docked_mode_description">Emula en modo sobremesa, lo que aumenta la resolución perjudicando el rendimiento.</string> + <string name="emulated_region">Región emulada</string> + <string name="emulated_language">Idioma emulado</string> + <string name="select_rtc_date">Seleccionar Fecha RTC</string> + <string name="select_rtc_time">Seleccionar Tiempo RTC</string> + <string name="use_custom_rtc">Habilitar RTC Personalizado</string> + <string name="use_custom_rtc_description">Esta configuración le permite configurar un reloj de tiempo real personalizado diferente a la hora actual de su sistema</string> + <string name="set_custom_rtc">Establecer RTC Personalizado</string> + + <!-- Graphics settings strings --> + <string name="renderer_api">API</string> + <string name="renderer_accuracy">Nivel de precisión</string> + <string name="renderer_resolution">Resolución</string> + <string name="renderer_vsync">Modo VSync</string> + <string name="renderer_aspect_ratio">Relación de aspecto</string> + <string name="renderer_scaling_filter">Filtro de adaptación de ventana</string> + <string name="renderer_anti_aliasing">Metodo Anti Aliasing</string> + <string name="renderer_force_max_clock">Forzar velocidad al máximo (solo Adreno)</string> + <string name="renderer_force_max_clock_description">Fuerza a la GPU a ejecutarse a la velocidad máxima de reloj posible (se seguirán aplicando restricciones térmicas).</string> + <string name="renderer_asynchronous_shaders">Usar shaders asíncronos</string> + <string name="renderer_asynchronous_shaders_description">Compila shaders de forma asincrónica, lo que reducirá los parones pero puede introducir fallos.</string> + <string name="renderer_debug">Habilitar la depuración de gráficos</string> + <string name="renderer_debug_description">Cuando esté marcado, la API de gráficos entra en un modo de depuración más lento.</string> + <string name="use_disk_shader_cache">Usar caché de shaders en disco</string> + <string name="use_disk_shader_cache_description">Reduzca los parones almacenando y cargando shaders generados en el disco.</string> + + <!-- Audio settings strings --> + <string name="audio_volume">Volumen</string> + <string name="audio_volume_description">Especifica el volumen de la salida de audio.</string> + + <!-- Miscellaneous --> + <string name="slider_default">Predeterminado</string> + <string name="ini_saved">Configuración guardada</string> + <string name="gameid_saved">Configuración guardada para %1$s</string> + <string name="error_saving">Error guardando %1$s.ini: %2$s</string> + <string name="loading">Cargando...</string> + <string name="reset_setting_confirmation">¿Desea restablecer esta configuración a su valor predeterminado?</string> + <string name="reset_to_default">Restablecer a predeterminado</string> + <string name="reset_all_settings">¿Restablecer todas las configuraciones?</string> + <string name="reset_all_settings_description">Todas las configuraciones avanzadas se restablecerán a su configuración predeterminada. Esto no se puede deshacer.</string> + <string name="settings_reset">Reiniciar la configuracion</string> + <string name="close">Cerrar</string> + <string name="learn_more">Más información</string> + + <!-- GPU driver installation --> + <string name="select_gpu_driver">Seleccionar driver GPU</string> + <string name="select_gpu_driver_title">¿Quiere reemplazar el driver de GPU actual?</string> + <string name="select_gpu_driver_install">Instalar</string> + <string name="select_gpu_driver_default">Predeterminado</string> + <string name="select_gpu_driver_install_success">Instalado %s</string> + <string name="select_gpu_driver_use_default">Usando el driver de GPU por defecto </string> + <string name="select_gpu_driver_error">¡Driver no válido, utilizando el predeterminado del sistema!</string> + <string name="system_gpu_driver">Driver GPU del sistema</string> + <string name="installing_driver">Instalando driver...</string> + + <!-- Preferences Screen --> + <string name="preferences_settings">Ajustes</string> + <string name="preferences_general">General</string> + <string name="preferences_system">Sistema</string> + <string name="preferences_graphics">Gráficos</string> + <string name="preferences_audio">Audio</string> + <string name="preferences_theme">Tema y color</string> + + <!-- ROM loading errors --> + <string name="loader_error_encrypted">Su ROM está encriptada</string> + <string name="loader_error_encrypted_roms_description"><![CDATA[Por favor, siga las guías para redumpear <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-cartridge-games\">cartuchos de juegos</a> o <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-installed-titles-eshop\">titulos instalados</a>.]]></string> + <string name="loader_error_encrypted_keys_description"><![CDATA[Por favor, compruebe que su archivo <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> está instalado, para que los juegos sean descifrados.]]></string> + <string name="loader_error_video_core">Ocurrió un error al inicializar el núcleo de video, posiblemente debido a una incompatibilidad con el driver seleccionado</string> + <string name="loader_error_video_core_description">Esto suele deberse a un driver de GPU incompatible. La instalación de un controlador de GPU personalizado puede resolver este problema.</string> + <string name="loader_error_invalid_format">No se pudo cargar la ROM</string> + <string name="loader_error_file_not_found">Archivo ROM no existe</string> + + <!-- Emulation Menu --> + <string name="emulation_exit">Salir de la emulación</string> + <string name="emulation_done">Hecho</string> + <string name="emulation_fps_counter">Contador de FPS</string> + <string name="emulation_toggle_controls">Alternar Controles</string> + <string name="emulation_rel_stick_center">Centro Relativo del Stick</string> + <string name="emulation_dpad_slide">Deslizamiento de la Cruceta</string> + <string name="emulation_haptics">Hápticos</string> + <string name="emulation_show_overlay">Mostrar pantalla</string> + <string name="emulation_toggle_all">Alternar Todo</string> + <string name="emulation_control_adjust">Ajustar pantalla</string> + <string name="emulation_control_scale">Escala</string> + <string name="emulation_control_opacity">Opacidad</string> + <string name="emulation_touch_overlay_reset">Reiniciar pantalla</string> + <string name="emulation_touch_overlay_edit">Editar pantalla</string> + <string name="emulation_pause">Pausar Emulación</string> + <string name="emulation_unpause">Reanudar Emulación</string> + <string name="emulation_input_overlay">Opciones de pantalla </string> + <string name="emulation_game_loading">Cargando juego...</string> + + <string name="load_settings">Cargando configuración...</string> + + <!-- Software keyboard --> + <string name="software_keyboard">Software del teclado</string> + + <!-- Errors and warnings --> + <string name="abort_button">Abortar</string> + <string name="continue_button">Continuar</string> + <string name="system_archive_not_found">Archivo del sistema no encontrado</string> + <string name="system_archive_not_found_message">%s no se ha encontrado. Vacíe los archivos de su sistema.\nContinuar con la emulación puede provocar bloqueos y errores.</string> + <string name="system_archive_general">Un archivo del sistema</string> + <string name="save_load_error">Error de Guardado/Carga</string> + <string name="fatal_error">Error fatal</string> + <string name="fatal_error_message">Ocurrió un error fatal. Consulte el registro para obtener más detalles.\nContinuar con la emulación puede provocar bloqueos y errores.</string> + <string name="performance_warning">¡Desactivar esta configuración reducirá significativamente el rendimiento de la emulación! Para obtener la mejor experiencia, se recomienda dejar esta configuración habilitada.</string> + + <!-- Region Names --> + <string name="region_japan">Japón</string> + <string name="region_usa">EEUU</string> + <string name="region_europe">Europa</string> + <string name="region_australia">Australia</string> + <string name="region_china">China</string> + <string name="region_korea">Corea</string> + <string name="region_taiwan">Taiwán</string> + + <!-- Language Names --> + <string name="language_japanese">Japonés (日本語)</string> + <string name="language_english">Inglés (English)</string> + <string name="language_french">Francés (Français)</string> + <string name="langauge_german">Alemán (deutsch)</string> + <string name="language_italian">Italiano (Italiano)</string> + <string name="language_spanish">Español (Español)</string> + <string name="language_chinese">Chino (简体中文)</string> + <string name="language_korean">Coreano (한국어)</string> + <string name="language_dutch">Holandés (nederlands)</string> + <string name="language_portuguese">Portugués (Português)</string> + <string name="language_russian">Ruso (Русский)</string> + <string name="language_taiwanese">Taiwanés (台湾)</string> + <string name="language_british_english">Inglés británico</string> + <string name="language_canadian_french">Francés Canadiense (Français canadien)</string> + <string name="language_latin_american_spanish">Español Latinoamericano (Español latinoamericano)</string> + <string name="language_simplified_chinese">Chino Simplificado (简体中文)</string> + <string name="language_traditional_chinese">Chino tradicional (正體中文)</string> + <string name="language_brazilian_portuguese">Portugués Brasileño (Português do Brasil)</string> + + <!-- Renderer APIs --> + <string name="renderer_vulkan">Vulkan</string> + <string name="renderer_none">Ninguno</string> + + <!-- Renderer Accuracy --> + <string name="renderer_accuracy_normal">Normal</string> + <string name="renderer_accuracy_high">Alto</string> + <string name="renderer_accuracy_extreme">Extremo (Lento)</string> + + <!-- Resolutions --> + <string name="resolution_half">0.5X (360p/540p)</string> + <string name="resolution_three_quarter">0.75X (540p/810p)</string> + <string name="resolution_one">x1 (720p/1080p)</string> + <string name="resolution_two">2X (1440p/2160p) (Lento)</string> + <string name="resolution_three">3X (2160p/3240p) (Lento)</string> + <string name="resolution_four">4X (2880p/4320p) (Lento)</string> + + <!-- Renderer VSync --> + <string name="renderer_vsync_immediate">Inmediata (Desactivado)</string> + <string name="renderer_vsync_mailbox">Mailbox</string> + <string name="renderer_vsync_fifo">FIFO (Activado)</string> + <string name="renderer_vsync_fifo_relaxed">FIFO Relajado</string> + + <!-- Scaling Filters --> + <string name="scaling_filter_nearest_neighbor">Vecino más próximo</string> + <string name="scaling_filter_bilinear">Bilineal</string> + <string name="scaling_filter_bicubic">Bicúbico</string> + <string name="scaling_filter_gaussian">Gaussiano</string> + <string name="scaling_filter_scale_force">ScaleForce</string> + <string name="scaling_filter_fsr">AMD FidelityFX™ Super Resolución</string> + + <!-- Anti-Aliasing --> + <string name="anti_aliasing_none">Ninguno</string> + <string name="anti_aliasing_fxaa">FXAA</string> + <string name="anti_aliasing_smaa">SMAA</string> + + <!-- Aspect Ratios --> + <string name="ratio_default">Predeterminado (16:9)</string> + <string name="ratio_force_four_three">Forzar 4:3</string> + <string name="ratio_force_twenty_one_nine">Forzar 21:9</string> + <string name="ratio_force_sixteen_ten">Forzar 16:10</string> + <string name="ratio_stretch">Ajustar a la ventana</string> + + <!-- CPU Accuracy --> + <string name="cpu_accuracy_accurate">Preciso</string> + <string name="cpu_accuracy_unsafe">Impreciso</string> + <string name="cpu_accuracy_paranoid">Paranoico (Lento)</string> + + <!-- Gamepad Buttons --> + <string name="gamepad_d_pad">Cruceta</string> + <string name="gamepad_left_stick">Palanca izquierda</string> + <string name="gamepad_right_stick">Palanca derecha</string> + <string name="gamepad_home">Home</string> + <string name="gamepad_screenshot">Captura de pantalla</string> + + <!-- Disk shader cache --> + <string name="preparing_shaders">Preparando shaders</string> + <string name="building_shaders">Construyendo shaders</string> + + <!-- Theme options --> + <string name="change_app_theme">Cambiar Tema</string> + <string name="theme_default">Predeterminado</string> + <string name="theme_material_you">Material You</string> + + <!-- Theme Modes --> + <string name="change_theme_mode">Cambiar modo del tema</string> + <string name="theme_mode_follow_system">Igual al sistema</string> + <string name="theme_mode_light">Claro</string> + <string name="theme_mode_dark">Oscuro</string> + + <!-- Black backgrounds theme --> + <string name="use_black_backgrounds">Usar Fondos Negros</string> + <string name="use_black_backgrounds_description">Cuando utilice el modo oscuro, aplique fondos negros.</string> + +</resources> diff --git a/src/android/app/src/main/res/values-fr/strings.xml b/src/android/app/src/main/res/values-fr/strings.xml new file mode 100644 index 0000000000..14a9b2d5c2 --- /dev/null +++ b/src/android/app/src/main/res/values-fr/strings.xml @@ -0,0 +1,337 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <string name="app_disclaimer">Ce logiciel exécutera des jeux pour la console de jeu Nintendo Switch. Aucun jeux ou clés n\'est inclus.<br /><br />Avant de commencer, veuillez localiser votre fichier <![CDATA[<b> prod.keys </b>]]> sur le stockage de votre appareil.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">En savoir plus</a>]]></string> + <string name="emulation_notification_channel_name">L\'émulation est active</string> + <string name="emulation_notification_channel_description">Affiche une notification persistante lorsque l\'émulation est en cours d\'exécution.</string> + <string name="emulation_notification_running">yuzu est en cours d\'exécution</string> + <string name="notice_notification_channel_name">Avis et erreurs</string> + <string name="notice_notification_channel_description">Affiche des notifications en cas de problème.</string> + <string name="notification_permission_not_granted">Permission de notification non accordée !</string> + + <!-- Setup strings --> + <string name="welcome">Bienvenue !</string> + <string name="welcome_description">Apprenez à configurer <b>yuzu</b> et passez à l\'émulation.</string> + <string name="get_started">Commencer</string> + <string name="keys">Clés</string> + <string name="keys_description">Sélectionnez votre fichier <b>prod.keys</b> avec le bouton ci-dessous.</string> + <string name="select_keys">Sélectionner les clés</string> + <string name="games">Jeux</string> + <string name="games_description">Sélectionnez votre dossier <b>de Jeux</b> avec le bouton ci-dessous.</string> + <string name="done">Terminé</string> + <string name="done_description">Vous êtes prêt.\nProfitez de vos jeux !</string> + <string name="text_continue">Continuer</string> + <string name="next">Suivant</string> + <string name="back">Retour</string> + <string name="add_games">Ajouter des jeux</string> + <string name="add_games_description">Sélectionner votre dossier de jeux</string> + + <!-- Home strings --> + <string name="home_games">Jeux</string> + <string name="home_search">Rechercher</string> + <string name="home_settings">Paramètres</string> + <string name="empty_gamelist">Aucun fichier n\'a été trouvé ou aucun répertoire de jeu n\'a encore été sélectionné.</string> + <string name="search_and_filter_games">Rechercher et filtrer les jeux</string> + <string name="select_games_folder">Sélectionner le dossier de jeux</string> + <string name="select_games_folder_description">Permet à yuzu de remplir la liste des jeux</string> + <string name="add_games_warning">Ne pas sélectionner le dossier des jeux ?</string> + <string name="add_games_warning_description">Les jeux ne seront pas affichés dans la liste des jeux si aucun dossier n\'est sélectionné.</string> + <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> + <string name="home_search_games">Rechercher des jeux</string> + <string name="games_dir_selected">Répertoire de jeux sélectionné</string> + <string name="install_prod_keys">Installer prod.keys</string> + <string name="install_prod_keys_description">Nécessaire pour décrypter les jeux commerciaux.</string> + <string name="install_prod_keys_warning">Sauter l\'ajout des clés ?</string> + <string name="install_prod_keys_warning_description">Des clés valides sont nécessaires pour émuler des jeux commerciaux. Seules les applications homebrew fonctionneront si vous continuez.</string> + <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string> + <string name="notifications">Notifications</string> + <string name="notifications_description">Accordez l\'autorisation de notification avec le bouton ci-dessous.</string> + <string name="give_permission">Donner la permission</string> + <string name="notification_warning">Ne pas accorder la permission de notification ?</string> + <string name="notification_warning_description">yuzu ne pourra pas vous communiquer d\'informations importantes.</string> + <string name="permission_denied">Permission refusée</string> + <string name="permission_denied_description">Vous avez refusé cette permission trop de fois et vous devez maintenant l\'accorder manuellement dans les paramètres système.</string> + <string name="about">À propos</string> + <string name="about_description">Numéro de build, crédits et plus encore</string> + <string name="warning_help">Aide</string> + <string name="warning_skip">Sauter</string> + <string name="warning_cancel">Annuler</string> + <string name="install_amiibo_keys">Installer les clés Amiibo</string> + <string name="install_amiibo_keys_description">Nécessaire pour utiliser les Amiibo en jeu</string> + <string name="invalid_keys_file">Fichier de clés sélectionné invalide</string> + <string name="install_keys_success">Clés installées avec succès</string> + <string name="reading_keys_failure">Erreur lors de la lecture des clés de chiffrement</string> + <string name="invalid_keys_error">Clés de chiffrement invalides</string> + <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> + <string name="install_keys_failure_description">Le fichier sélectionné est incorrect ou corrompu. Veuillez dumper à nouveau vos clés.</string> + <string name="install_gpu_driver">Installer le pilote du GPU</string> + <string name="install_gpu_driver_description">Installez des pilotes alternatifs pour des performances ou une précision potentiellement meilleures</string> + <string name="advanced_settings">Paramètres avancés</string> + <string name="settings_description">Configurer les paramètres de l\'émulateur</string> + <string name="search_recently_played">Joué récemment</string> + <string name="search_recently_added">Ajouté récemment</string> + <string name="search_retail">Commercial</string> + <string name="search_homebrew">Homebrew</string> + <string name="open_user_folder">Ouvrir le dossier de yuzu</string> + <string name="open_user_folder_description">Gérer les fichiers internes de yuzu</string> + <string name="theme_and_color_description">Modifier l\'apparence de l\'application</string> + <string name="no_file_manager">Aucun gestionnaire de fichiers trouvé</string> + <string name="notification_no_directory_link">Impossible d\'ouvrir le répertoire de yuzu</string> + <string name="notification_no_directory_link_description">Veuillez localiser manuellement le dossier utilisateur avec le panneau latéral du gestionnaire de fichiers.</string> + <string name="manage_save_data">Gérer les données de sauvegarde</string> + <string name="manage_save_data_description">Données de sauvegarde trouvées. Veuillez sélectionner une option ci-dessous.</string> + <string name="import_export_saves_description">Importer ou exporter des fichiers de sauvegarde</string> + <string name="import_export_saves_no_profile">Aucune données de sauvegarde trouvées. Veuillez lancer un jeu et réessayer.</string> + <string name="save_file_imported_success">Importé avec succès</string> + <string name="save_file_invalid_zip_structure">Structure de répertoire de sauvegarde non valide</string> + <string name="save_file_invalid_zip_structure_description">Le nom du premier sous-dossier doit être l\'identifiant du titre du jeu.</string> + <string name="import_saves">Importer</string> + <string name="export_saves">Exporter</string> + + <!-- About screen strings --> + <string name="gaia_is_not_real">Gaia n\'est pas réel</string> + <string name="copied_to_clipboard">Copié dans le presse-papier</string> + <string name="about_app_description">Un émulateur Switch open source</string> + <string name="contributors">Contributeurs</string> + <string name="contributors_description">Fait avec \u2764 de l\'équipe yuzu</string> + <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> + <string name="build">Build</string> + <string name="support_link">https://discord.gg/u77vRWY</string> + <string name="website_link">https://yuzu-emu.org/</string> + <string name="github_link">https://github.com/yuzu-emu</string> + + <!-- Early access upgrade strings --> + <string name="early_access">Early Access</string> + <string name="get_early_access">Obtenir l\'Early Access</string> + <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string> + <string name="get_early_access_description">Fonctionnalités de pointe, accès anticipé aux mises à jour, et plus encore</string> + <string name="early_access_benefits">Avantages de l\'Early Access</string> + <string name="cutting_edge_features">Fonctionnalités de pointe</string> + <string name="early_access_updates">Accès anticipé aux mises à jour</string> + <string name="no_manual_installation">Pas d\'installation manuelle</string> + <string name="prioritized_support">Assistance prioritaire</string> + <string name="helping_game_preservation">Contribuer à la préservation des jeux</string> + <string name="our_eternal_gratitude">Notre gratitude éternelle</string> + <string name="are_you_interested">Es tu intéressé ?</string> + + <!-- General settings strings --> + <string name="frame_limit_enable">Activer la vitesse limite</string> + <string name="frame_limit_enable_description">Lorsqu\'elle est activée, la vitesse d\'émulation sera limitée à un pourcentage spécifié de la vitesse normale.</string> + <string name="frame_limit_slider">Limite en pourcentage de vitesse</string> + <string name="frame_limit_slider_description">Spécifie le pourcentage pour limiter la vitesse d\'émulation. Avec la valeur par défaut de 100%, l\'émulation sera limitée à la vitesse normale. Des valeurs supérieures ou inférieures augmenteront ou diminueront la limite de vitesse.</string> + <string name="cpu_accuracy">Précision du CPU</string> + + <!-- System settings strings --> + <string name="use_docked_mode">Mode TV</string> + <string name="use_docked_mode_description">Émuler en mode TV augmente la résolution au détriment des performances.</string> + <string name="emulated_region">Région émulée</string> + <string name="emulated_language">Langue émulée</string> + <string name="select_rtc_date">Sélectionner la date RTC</string> + <string name="select_rtc_time">Sélectionner l\'heure RTC</string> + <string name="use_custom_rtc">Activer l\'horloge RTC personnalisée</string> + <string name="use_custom_rtc_description">Ce paramètre vous permet de définir une horloge en temps réel personnalisée distincte de l\'heure actuelle de votre système.</string> + <string name="set_custom_rtc">Définir l\'horloge RTC personnalisée</string> + + <!-- Graphics settings strings --> + <string name="renderer_api">API</string> + <string name="renderer_accuracy">Niveau de précision</string> + <string name="renderer_resolution">Résolution</string> + <string name="renderer_vsync">Mode VSync</string> + <string name="renderer_aspect_ratio">Format</string> + <string name="renderer_scaling_filter">Filtre de fenêtre adaptatif</string> + <string name="renderer_anti_aliasing">Méthode d\'anticrénelage :</string> + <string name="renderer_force_max_clock">Forcer la fréquence d\'horloge maximale (Adreno uniquement)</string> + <string name="renderer_force_max_clock_description">Force le GPU à fonctionner au maximum d\'horloges possibles (les contraintes thermiques seront toujours appliquées).</string> + <string name="renderer_asynchronous_shaders">Utiliser les shaders asynchrones</string> + <string name="renderer_asynchronous_shaders_description">Compile les shaders de manière asynchrone, ce qui réduira les saccades mais peut entraîner des problèmes visuels.</string> + <string name="renderer_debug">Activer le débogage des graphismes</string> + <string name="renderer_debug_description">Lorsque cette case est cochée, l\'API graphique entre dans un mode de débogage plus lent.</string> + <string name="use_disk_shader_cache">Utiliser les shader cache de disque</string> + <string name="use_disk_shader_cache_description">Réduire les saccades en stockant et en chargeant les shaders générés sur le disque.</string> + + <!-- Audio settings strings --> + <string name="audio_volume">Volume</string> + <string name="audio_volume_description">Spécifie le volume de la sortie audio.</string> + + <!-- Miscellaneous --> + <string name="slider_default">Défaut</string> + <string name="ini_saved">Paramètres enregistrés</string> + <string name="gameid_saved">Paramètres enregistrés pour %1$s</string> + <string name="error_saving">Erreur lors de l\'enregistrement de %1$s.ini: %2$s</string> + <string name="loading">Chargement...</string> + <string name="reset_setting_confirmation">Voulez-vous réinitialiser ce paramètre à sa valeur par défaut ?</string> + <string name="reset_to_default">Réinitialiser par défaut</string> + <string name="reset_all_settings">Réinitialiser tous les réglages ?</string> + <string name="reset_all_settings_description">Tous les paramètres avancés seront réinitialisés à leur configuration par défaut. Ça ne peut pas être annulé.</string> + <string name="settings_reset">Paramètres réinitialisés</string> + <string name="close">Fermer</string> + <string name="learn_more">Plus d\'informations</string> + + <!-- GPU driver installation --> + <string name="select_gpu_driver">Sélectionner le pilote du GPU</string> + <string name="select_gpu_driver_title">Souhaitez vous remplacer votre pilote actuel ?</string> + <string name="select_gpu_driver_install">Installer</string> + <string name="select_gpu_driver_default">Défaut</string> + <string name="select_gpu_driver_install_success">%s Installé</string> + <string name="select_gpu_driver_use_default">Utilisation du pilote de GPU par défaut</string> + <string name="select_gpu_driver_error">Pilote non valide sélectionné, utilisation du paramètre par défaut du système !</string> + <string name="system_gpu_driver">Pilote du GPU du système</string> + <string name="installing_driver">Installation du pilote...</string> + + <!-- Preferences Screen --> + <string name="preferences_settings">Paramètres</string> + <string name="preferences_general">Général</string> + <string name="preferences_system">Système</string> + <string name="preferences_graphics">Vidéo</string> + <string name="preferences_audio">Audio</string> + <string name="preferences_theme">Thème et couleur</string> + + <!-- ROM loading errors --> + <string name="loader_error_encrypted">Votre ROM est cryptée</string> + <string name="loader_error_encrypted_roms_description"><![CDATA[Veuillez suivre les guides pour redumper vos <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-cartridge-games\">cartouches de jeu</a> ou <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-installed-titles-eshop\">titres installés</a>.]]></string> + <string name="loader_error_encrypted_keys_description"><![CDATA[Veuillez vous assurer que votre fichier <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> est installé pour que les jeux puissent être déchiffrés.]]></string> + <string name="loader_error_video_core">Une erreur s\'est produite lors de l\'initialisation du noyau vidéo</string> + <string name="loader_error_video_core_description">Cela est généralement dû à un pilote du GPU incompatible. L\'installation d\'un pilote du GPU personnalisé peut résoudre ce problème.</string> + <string name="loader_error_invalid_format">Impossible de charger la ROM</string> + <string name="loader_error_file_not_found">Le fichier ROM n\'existe pas</string> + + <!-- Emulation Menu --> + <string name="emulation_exit">Quitter l\'émulation</string> + <string name="emulation_done">Terminé</string> + <string name="emulation_fps_counter">Compteur FPS</string> + <string name="emulation_toggle_controls">Activer/Désactiver les contrôles</string> + <string name="emulation_rel_stick_center">Centre du stick relatif</string> + <string name="emulation_dpad_slide">Glissement du DPad</string> + <string name="emulation_haptics">Haptique</string> + <string name="emulation_show_overlay">Afficher l\'overlay</string> + <string name="emulation_toggle_all">Tout basculer</string> + <string name="emulation_control_adjust">Ajuster l\'overlay</string> + <string name="emulation_control_scale">Échelle</string> + <string name="emulation_control_opacity">Opacité</string> + <string name="emulation_touch_overlay_reset">Réinitialiser l\'overlay</string> + <string name="emulation_touch_overlay_edit">Modifier l\'overlay</string> + <string name="emulation_pause">Mettre en pause l\'émulation</string> + <string name="emulation_unpause">Reprendre l\'émulation</string> + <string name="emulation_input_overlay">Options de l\'overlay</string> + <string name="emulation_game_loading">Chargement du jeu...</string> + + <string name="load_settings">Chargement des paramètres…</string> + + <!-- Software keyboard --> + <string name="software_keyboard">Clavier virtuel</string> + + <!-- Errors and warnings --> + <string name="abort_button">Abandonner</string> + <string name="continue_button">Continuer</string> + <string name="system_archive_not_found">Archive système introuvable</string> + <string name="system_archive_not_found_message">%s est manquant. Veuillez dumper vos archives système.\nContinuer peut entraîner des plantages et des bogues.</string> + <string name="system_archive_general">Une archive système</string> + <string name="save_load_error">Erreur de sauvegarde/chargement</string> + <string name="fatal_error">Erreur fatale</string> + <string name="fatal_error_message">Une erreur fatale s\'est produite. Consultez les logs pour plus de détails.\nContinuer l\'émulation peut entraîner des plantages et des bogues.</string> + <string name="performance_warning">La désactivation de ce paramètre réduira considérablement les performances d\'émulation ! Pour une expérience optimale, il est recommandé de laisser ce paramètre activé.</string> + + <!-- Region Names --> + <string name="region_japan">Japon</string> + <string name="region_usa">É.-U.A.</string> + <string name="region_europe">Europe</string> + <string name="region_australia">Australie</string> + <string name="region_china">Chine</string> + <string name="region_korea">Corée</string> + <string name="region_taiwan">Taïwan</string> + + <!-- Language Names --> + <string name="language_japanese">Japonais (日本語)</string> + <string name="language_english">Anglais</string> + <string name="language_french">Français (Français)</string> + <string name="langauge_german">Allemand (Deutsch)</string> + <string name="language_italian">Italien (Italiano)</string> + <string name="language_spanish">Espagnol (Español)</string> + <string name="language_chinese">Chinois (简体中文)</string> + <string name="language_korean">Coréen (한국어)</string> + <string name="language_dutch">Néerlandais (Nederlands)</string> + <string name="language_portuguese">Portugais (Português)</string> + <string name="language_russian">Russe (Русский)</string> + <string name="language_taiwanese">Taïwanais (台湾)</string> + <string name="language_british_english">Anglais Britannique</string> + <string name="language_canadian_french">Français canadien (Français canadien)</string> + <string name="language_latin_american_spanish">Espagnol latino-américain (Español latinoamericano)</string> + <string name="language_simplified_chinese">Chinois simplifié (简体中文)</string> + <string name="language_traditional_chinese">Chinois Traditionnel (正體中文)</string> + <string name="language_brazilian_portuguese">Portugais brésilien (Português do Brasil)</string> + + <!-- Renderer APIs --> + <string name="renderer_vulkan">Vulkan</string> + <string name="renderer_none">Aucune</string> + + <!-- Renderer Accuracy --> + <string name="renderer_accuracy_normal">Normal</string> + <string name="renderer_accuracy_high">Haut</string> + <string name="renderer_accuracy_extreme">Extrême (Lent)</string> + + <!-- Resolutions --> + <string name="resolution_half">0.5X (360p/540p)</string> + <string name="resolution_three_quarter">0.75X (540p/810p)</string> + <string name="resolution_one">1X (720p/1080p)</string> + <string name="resolution_two">2X (1440p/2160p) (Lent)</string> + <string name="resolution_three">3X (2160p/3240p) (Lent)</string> + <string name="resolution_four">4X (2880p/4320p) (Lent)</string> + + <!-- Renderer VSync --> + <string name="renderer_vsync_immediate">Immédiat (Désactivé)</string> + <string name="renderer_vsync_mailbox">Mailbox</string> + <string name="renderer_vsync_fifo">FIFO (Activé)</string> + <string name="renderer_vsync_fifo_relaxed">FIFO souple</string> + + <!-- Scaling Filters --> + <string name="scaling_filter_nearest_neighbor">Plus proche voisin</string> + <string name="scaling_filter_bilinear">Bilinéaire</string> + <string name="scaling_filter_bicubic">Bicubique</string> + <string name="scaling_filter_gaussian">Gaussien</string> + <string name="scaling_filter_scale_force">ScaleForce</string> + <string name="scaling_filter_fsr">AMD FidelityFX™ Super Resolution</string> + + <!-- Anti-Aliasing --> + <string name="anti_aliasing_none">Aucune</string> + <string name="anti_aliasing_fxaa">FXAA</string> + <string name="anti_aliasing_smaa">SMAA</string> + + <!-- Aspect Ratios --> + <string name="ratio_default">Par défaut (16:9)</string> + <string name="ratio_force_four_three">Forcer le 4:3</string> + <string name="ratio_force_twenty_one_nine">Forcer le 21:9</string> + <string name="ratio_force_sixteen_ten">Forcer le 16:10</string> + <string name="ratio_stretch">Étirer à la fenêtre</string> + + <!-- CPU Accuracy --> + <string name="cpu_accuracy_accurate">Précis</string> + <string name="cpu_accuracy_unsafe">Risqué</string> + <string name="cpu_accuracy_paranoid">Paranoïaque (Lent)</string> + + <!-- Gamepad Buttons --> + <string name="gamepad_d_pad">Pavé directionnel</string> + <string name="gamepad_left_stick">Stick Gauche</string> + <string name="gamepad_right_stick">Stick Droit</string> + <string name="gamepad_home">Home</string> + <string name="gamepad_screenshot">Capture d\'écran</string> + + <!-- Disk shader cache --> + <string name="preparing_shaders">Préparation des shaders</string> + <string name="building_shaders">Compilation des shaders</string> + + <!-- Theme options --> + <string name="change_app_theme">Changer le thème de l\'application</string> + <string name="theme_default">Défaut</string> + <string name="theme_material_you">Material You</string> + + <!-- Theme Modes --> + <string name="change_theme_mode">Changer le mode de thème</string> + <string name="theme_mode_follow_system">Automatique</string> + <string name="theme_mode_light">Lumineux</string> + <string name="theme_mode_dark">Sombre</string> + + <!-- Black backgrounds theme --> + <string name="use_black_backgrounds">Utiliser des arrière-plans noirs</string> + <string name="use_black_backgrounds_description">Lorsque vous utilisez le thème sombre, appliquer des arrière-plans noirs.</string> + +</resources> diff --git a/src/android/app/src/main/res/values-it/strings.xml b/src/android/app/src/main/res/values-it/strings.xml new file mode 100644 index 0000000000..47a4cfa317 --- /dev/null +++ b/src/android/app/src/main/res/values-it/strings.xml @@ -0,0 +1,337 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <string name="app_disclaimer">Questo software permette di giocare ai giochi della console Nintendo Switch. Nessun gioco o chiave è inclusa.<br /><br />Prima di iniziare, perfavore individua il file <![CDATA[<b>prod.keys </b>]]> nella memoria del tuo dispositivo.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Scopri di più</a>]]></string> + <string name="emulation_notification_channel_name">L\'emulatore è attivo</string> + <string name="emulation_notification_channel_description">Mostra una notifica persistente quando l\'emulatore è in esecuzione.</string> + <string name="emulation_notification_running">yuzu è in esecuzione</string> + <string name="notice_notification_channel_name">Avvisi ed errori</string> + <string name="notice_notification_channel_description">Mostra le notifiche quando qualcosa va storto.</string> + <string name="notification_permission_not_granted">Autorizzazione di notifica non concessa!</string> + + <!-- Setup strings --> + <string name="welcome">Benvenuto!</string> + <string name="welcome_description">Scopri come configurare <b>yuzu</b> e passare all\'emulazione.</string> + <string name="get_started">Iniziare</string> + <string name="keys">Pulsanti</string> + <string name="keys_description">Seleziona il tuo file <b>prod.keys</b> con il pulsante in basso.</string> + <string name="select_keys">Selezione Pulsanti</string> + <string name="games">Giochi</string> + <string name="games_description">Seleziona la cartella <b>Games</b> con il pulsante in basso.</string> + <string name="done">Fatto</string> + <string name="done_description">È tutto pronto.\nDivertiti a giocare!</string> + <string name="text_continue">Continua</string> + <string name="next">Successivo</string> + <string name="back">Indietro</string> + <string name="add_games">Aggiungi giochi</string> + <string name="add_games_description">Seleziona la cartella dei giochi</string> + + <!-- Home strings --> + <string name="home_games">Giochi</string> + <string name="home_search">Cerca</string> + <string name="home_settings">Impostazioni</string> + <string name="empty_gamelist">Non sono stati trovati file o non è stata ancora selezionata alcuna directory di gioco.</string> + <string name="search_and_filter_games">Cerca e filtra i giochi</string> + <string name="select_games_folder">Seleziona la cartella di gioco</string> + <string name="select_games_folder_description">Consente a yuzu di popolare l\'elenco dei giochi</string> + <string name="add_games_warning">Saltare la selezione della cartella dei giochi?</string> + <string name="add_games_warning_description">I giochi non saranno mostrati nella lista dei giochi se una cartella non è selezionata.</string> + <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> + <string name="home_search_games">Cerca giochi</string> + <string name="games_dir_selected">Cartella dei giochi selezionata</string> + <string name="install_prod_keys">Installa prod.keys</string> + <string name="install_prod_keys_description">Necessario per decrittografare i giochi</string> + <string name="install_prod_keys_warning">Saltare l\'aggiunta delle chiavi?</string> + <string name="install_prod_keys_warning_description">Sono necessarie delle chiavi valide per emulare i giochi. Se continui, funzioneranno solo le app homebrew.</string> + <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string> + <string name="notifications">Notifiche</string> + <string name="notifications_description">Concedi l\'autorizzazione alle notifiche con il pulsante in basso.</string> + <string name="give_permission">Concedere l\'autorizzazione</string> + <string name="notification_warning">Saltare la concessione dell\'autorizzazione alle notifiche?</string> + <string name="notification_warning_description">yuzu non sarà in grado di notificarti informazioni importanti.</string> + <string name="permission_denied">Permesso negato</string> + <string name="permission_denied_description">Hai negato l\'autorizzazione troppe volte ed ora devi concederla manualmente nelle impostazioni di sistema.</string> + <string name="about">Informazioni</string> + <string name="about_description">Versione build, crediti ed altro</string> + <string name="warning_help">Aiuto</string> + <string name="warning_skip">Salta</string> + <string name="warning_cancel">Annulla</string> + <string name="install_amiibo_keys">Installa le chiavi degli Amiibo</string> + <string name="install_amiibo_keys_description">Necessario per usare gli Amiibo in gioco</string> + <string name="invalid_keys_file">Selezionate chiavi non valide</string> + <string name="install_keys_success">Chiavi installate correttamente</string> + <string name="reading_keys_failure">Errore durante la lettura delle chiavi di crittografia</string> + <string name="invalid_keys_error">Chiavi di crittografia non valide</string> + <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> + <string name="install_keys_failure_description">Il file selezionato è incorretto o corrotto. Per favore riesegui il dump delle tue chiavi.</string> + <string name="install_gpu_driver">Installa i driver GPU</string> + <string name="install_gpu_driver_description">Installa driver alternativi per potenziali prestazioni migliori o accuratezza.</string> + <string name="advanced_settings">Impostazioni avanzate</string> + <string name="settings_description">Configura le impostazioni dell\'emulatore</string> + <string name="search_recently_played">Giocato recentemente</string> + <string name="search_recently_added">Aggiunto recentemente</string> + <string name="search_retail">Rivenditore</string> + <string name="search_homebrew">Homebrew</string> + <string name="open_user_folder">Apri la cartella di yuzu</string> + <string name="open_user_folder_description">Gestisci i file interni di yuzu</string> + <string name="theme_and_color_description">Modifica l\'aspetto dell\'app</string> + <string name="no_file_manager">Nessun file manager trovato</string> + <string name="notification_no_directory_link">Impossibile aprire la cartella di yuzu</string> + <string name="notification_no_directory_link_description">Per favore individua la cartella dell\'utente manualmente con il pannello laterale del file manager.</string> + <string name="manage_save_data">Gestisci i salvataggi</string> + <string name="manage_save_data_description">Salvataggio non trovato. Seleziona un\'opzione di seguito.</string> + <string name="import_export_saves_description">Importa o esporta i salvataggi</string> + <string name="import_export_saves_no_profile">Nessun salvataggio trovato. Avvia un gioco e riprova.</string> + <string name="save_file_imported_success">Importato con successo</string> + <string name="save_file_invalid_zip_structure">La struttura della cartella dei salvataggi è invalida</string> + <string name="save_file_invalid_zip_structure_description">La prima sotto cartella <b>deve</b> chiamarsi come l\'ID del titolo del gioco.</string> + <string name="import_saves">Importa</string> + <string name="export_saves">Esporta</string> + + <!-- About screen strings --> + <string name="gaia_is_not_real">Gaia non è reale</string> + <string name="copied_to_clipboard">Copiato negli appunti</string> + <string name="about_app_description">Un emulatore della Switch open-source.</string> + <string name="contributors">Collaboratori</string> + <string name="contributors_description">Realizzato con \u2764 dal team yuzu</string> + <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> + <string name="build">Compilazione</string> + <string name="support_link">https://discord.gg/u77vRWY</string> + <string name="website_link">https://yuzu-emu.org/</string> + <string name="github_link">https://github.com/yuzu-emu</string> + + <!-- Early access upgrade strings --> + <string name="early_access">Accesso Anticipato</string> + <string name="get_early_access">Ottieni l\'accesso anticipato</string> + <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string> + <string name="get_early_access_description">Funzionalità all\'avanguardia, aggiornamenti in anticipo e altro</string> + <string name="early_access_benefits">Vantaggi dell\'accesso anticipato</string> + <string name="cutting_edge_features">Funzionalità all\'avanguardia</string> + <string name="early_access_updates">Accesso anticipato agli aggiornamenti</string> + <string name="no_manual_installation">Non installare manualmente.</string> + <string name="prioritized_support">Supporto prioritario</string> + <string name="helping_game_preservation">Aiuta a preservare il gioco</string> + <string name="our_eternal_gratitude">La nostra gratitudine eterna</string> + <string name="are_you_interested">Sei interessato?</string> + + <!-- General settings strings --> + <string name="frame_limit_enable">Abilita il limite di velocità</string> + <string name="frame_limit_enable_description">Quando abilitato, la velocità di emulazione verrà limitata a una specifica percentuale della velocità normale.</string> + <string name="frame_limit_slider">Limite velocità percentuale</string> + <string name="frame_limit_slider_description">Specifica la percentuale del limite della velocità di emulazione. Con quella preimpostata al 100% l\'emulazione verrà limitata alla velocità normale. Valori più alti o bassi aumenteranno o diminuiranno il limite di velocità.</string> + <string name="cpu_accuracy">Accuratezza della CPU</string> + + <!-- System settings strings --> + <string name="use_docked_mode">Modalità docked</string> + <string name="use_docked_mode_description">Emula in modalità docked, questo aumenta la risoluzione a spese delle performance.</string> + <string name="emulated_region">Regione emulata</string> + <string name="emulated_language">Lingua emulata</string> + <string name="select_rtc_date">Seleziona la data dall\'orologio in tempo reale</string> + <string name="select_rtc_time">Seleziona il tempo dall\'orologio in tempo reale</string> + <string name="use_custom_rtc">Abilità l\'orologio in tempo reale personalizzato</string> + <string name="use_custom_rtc_description">Questa impostazione ti permette di impostare un orologio in tempo reale personalizzato separato da quello del tuo sistema corrente.</string> + <string name="set_custom_rtc">Imposta l\'orologio in tempo reale personalizzato</string> + + <!-- Graphics settings strings --> + <string name="renderer_api">API</string> + <string name="renderer_accuracy">Livello di accuratezza</string> + <string name="renderer_resolution">Risoluzione</string> + <string name="renderer_vsync">Modalità VSync</string> + <string name="renderer_aspect_ratio">Rapporto d\'aspetto</string> + <string name="renderer_scaling_filter">Filtro di adattamento alla finestra</string> + <string name="renderer_anti_aliasing">Metodo di anti-aliasing</string> + <string name="renderer_force_max_clock">Forza clock massimi (solo Adreno)</string> + <string name="renderer_force_max_clock_description">Forza la GPU a girare col massimo clock possibile (i vincoli alla temperatura saranno comunque applicati)</string> + <string name="renderer_asynchronous_shaders">Usa shaders asincrone</string> + <string name="renderer_asynchronous_shaders_description">Compila le shaders asincronamente, questo riduce lo shutter ma potrebbe introdurre dei glitch. </string> + <string name="renderer_debug">Abilità il debug grafico</string> + <string name="renderer_debug_description">Quando l\'opzione è selezionata, l\'API grafica entra in una modalità di debug più lenta</string> + <string name="use_disk_shader_cache">Usa cache shader su disco</string> + <string name="use_disk_shader_cache_description">Riduce lo stuttering salvando e caricando le shader generate sul disco.</string> + + <!-- Audio settings strings --> + <string name="audio_volume">Volume</string> + <string name="audio_volume_description">Specifica il volume dell\'audio in uscita.</string> + + <!-- Miscellaneous --> + <string name="slider_default">Predefinito</string> + <string name="ini_saved">Impostazioni salvate</string> + <string name="gameid_saved">Impostazioni salvate per %1$s</string> + <string name="error_saving">Errore nel salvare %1$s.ini %2$s</string> + <string name="loading">Caricamento…</string> + <string name="reset_setting_confirmation">Vuoi ripristinare queste impostazioni al loro valore originale?</string> + <string name="reset_to_default">Riportare alle impostazioni originali</string> + <string name="reset_all_settings">Resettare tutte le impostazioni?</string> + <string name="reset_all_settings_description">Tutte le Impostazioni Avanzate saranno ripristinate a quelle originali. Questa operazione non è reversibile</string> + <string name="settings_reset">Reimposta le impostazioni</string> + <string name="close">Chiudi</string> + <string name="learn_more">Per saperne di più</string> + + <!-- GPU driver installation --> + <string name="select_gpu_driver">Seleziona il driver della GPU</string> + <string name="select_gpu_driver_title">Vuoi sostituire il driver della tua GPU attuale?</string> + <string name="select_gpu_driver_install">Installa</string> + <string name="select_gpu_driver_default">Predefinito</string> + <string name="select_gpu_driver_install_success">Installato%s</string> + <string name="select_gpu_driver_use_default">Utilizza il driver predefinito della GPU.</string> + <string name="select_gpu_driver_error">Il driver selezionato è invalido, è in utilizzo quello predefinito di sistema!</string> + <string name="system_gpu_driver">Driver GPU del sistema</string> + <string name="installing_driver">Installando i driver...</string> + + <!-- Preferences Screen --> + <string name="preferences_settings">Impostazioni</string> + <string name="preferences_general">Generali</string> + <string name="preferences_system">Sistema</string> + <string name="preferences_graphics">Grafica</string> + <string name="preferences_audio">Audio</string> + <string name="preferences_theme">Tema e colori</string> + + <!-- ROM loading errors --> + <string name="loader_error_encrypted">La tua ROM è criptata</string> + <string name="loader_error_encrypted_roms_description"><![CDATA[Per favore segui la guida per eseguire il dump della <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-cartridge-games\">cartuccia di gioco</a> o i <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-installed-titles-eshop\">titoli installati</a>.]]></string> + <string name="loader_error_encrypted_keys_description"><![CDATA[Per favore assicurati che il file <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> sia installato in modo che i giochi possano essere decrittati.]]></string> + <string name="loader_error_video_core">È stato riscontrato un errore nell\'inizializzazione del core video</string> + <string name="loader_error_video_core_description">Questo è causato solitamente dal driver incompatibile di una GPU. L\'installazione di driver GPU personalizzati potrebbe risolvere questo problema.</string> + <string name="loader_error_invalid_format">Impossibile caricare la ROM</string> + <string name="loader_error_file_not_found">Il file della ROM non esiste</string> + + <!-- Emulation Menu --> + <string name="emulation_exit">Uscire dall\'emulazione</string> + <string name="emulation_done">Fatto</string> + <string name="emulation_fps_counter">Contatore degli FPS</string> + <string name="emulation_toggle_controls">Controlli a interruttore</string> + <string name="emulation_rel_stick_center">Centro relativo degli Stick</string> + <string name="emulation_dpad_slide">Slittamento del Pad Direzionale</string> + <string name="emulation_haptics">Aptico</string> + <string name="emulation_show_overlay">Mostra Overlay</string> + <string name="emulation_toggle_all">Attiva/disattiva tutto</string> + <string name="emulation_control_adjust">Aggiusta Overlay</string> + <string name="emulation_control_scale">Scala</string> + <string name="emulation_control_opacity">Opacità</string> + <string name="emulation_touch_overlay_reset">Reimposta Overlay</string> + <string name="emulation_touch_overlay_edit">Modifica Overlay</string> + <string name="emulation_pause">Metti in pausa l\'emulazione</string> + <string name="emulation_unpause">Riprendi Emulazione</string> + <string name="emulation_input_overlay">Impostazioni Overlay</string> + <string name="emulation_game_loading">Caricamento del gioco...</string> + + <string name="load_settings">Caricamento delle impostazioni...</string> + + <!-- Software keyboard --> + <string name="software_keyboard">Tastiera software</string> + + <!-- Errors and warnings --> + <string name="abort_button">Interrompi</string> + <string name="continue_button">Continua</string> + <string name="system_archive_not_found">Archivio di sistema non trovato</string> + <string name="system_archive_not_found_message">%s è mancante. Per favore esegui il dump degli archivi del tuo sistema.\nContinuare ad emulare potrebbe portare bug o causare crash.</string> + <string name="system_archive_general">Un archivio di sistema</string> + <string name="save_load_error">Errore di salvataggio/caricamento</string> + <string name="fatal_error">Errore Fatale</string> + <string name="fatal_error_message">Un errore fatale è accaduto. Controlla i log per i dettagli.\nContinuare ad emulare potrebbe portare bug o causare crash.</string> + <string name="performance_warning">Disattivare questa impostazione può ridurre significativamente le performance di emulazione! Per una migliore esperienza, è consigliato lasciare questa impostazione attivata.</string> + + <!-- Region Names --> + <string name="region_japan">Giappone</string> + <string name="region_usa">USA</string> + <string name="region_europe">Europa</string> + <string name="region_australia">Australia</string> + <string name="region_china">Cina</string> + <string name="region_korea">Corea</string> + <string name="region_taiwan">Taiwan</string> + + <!-- Language Names --> + <string name="language_japanese">Giapponese (日本語)</string> + <string name="language_english">Inglese (English)</string> + <string name="language_french">Francese (Français)</string> + <string name="langauge_german">Tedesco (Deutsch)</string> + <string name="language_italian">Italiano (Italiano)</string> + <string name="language_spanish">Spagnolo (Español)</string> + <string name="language_chinese">Cinese (简体中文)</string> + <string name="language_korean">Coreano (한국어)</string> + <string name="language_dutch">Olandese (Nederlands)</string> + <string name="language_portuguese">Portoghese (Português)</string> + <string name="language_russian">Russo (Русский)</string> + <string name="language_taiwanese">Taiwanese (台湾)</string> + <string name="language_british_english">Inglese britannico</string> + <string name="language_canadian_french">Francese Canadese (Français canadien)</string> + <string name="language_latin_american_spanish">Spagnolo Latino Americano (Español latinoamericano)</string> + <string name="language_simplified_chinese">Cinese Semplificato (简体中文)</string> + <string name="language_traditional_chinese">Cinese tradizionale (正體中文)</string> + <string name="language_brazilian_portuguese">Portoghese (Português)</string> + + <!-- Renderer APIs --> + <string name="renderer_vulkan">Vulkan</string> + <string name="renderer_none">Nessuna</string> + + <!-- Renderer Accuracy --> + <string name="renderer_accuracy_normal">Normale</string> + <string name="renderer_accuracy_high">Alta</string> + <string name="renderer_accuracy_extreme">Estrema (Lenta)</string> + + <!-- Resolutions --> + <string name="resolution_half">0.5X (360p/540p)</string> + <string name="resolution_three_quarter">0.75X (540p/810p)</string> + <string name="resolution_one">1X (720p/1080p)</string> + <string name="resolution_two">2X (1440p/2160p) (Slow)</string> + <string name="resolution_three">3X (2160p/3240p) (Slow)</string> + <string name="resolution_four">4X (2880p/4320p) (Slow)</string> + + <!-- Renderer VSync --> + <string name="renderer_vsync_immediate">Immediato (Off)</string> + <string name="renderer_vsync_mailbox">Cassella postale</string> + <string name="renderer_vsync_fifo">FIFO (On)</string> + <string name="renderer_vsync_fifo_relaxed">FIFO Rilassato</string> + + <!-- Scaling Filters --> + <string name="scaling_filter_nearest_neighbor">Nearest neighbor</string> + <string name="scaling_filter_bilinear">Bilineare</string> + <string name="scaling_filter_bicubic">Bicubico</string> + <string name="scaling_filter_gaussian">Gaussiano</string> + <string name="scaling_filter_scale_force">ScaleForce</string> + <string name="scaling_filter_fsr">AMD FidelityFX™️ Super Resolution</string> + + <!-- Anti-Aliasing --> + <string name="anti_aliasing_none">Nessuna</string> + <string name="anti_aliasing_fxaa">FXAA</string> + <string name="anti_aliasing_smaa">SMAA</string> + + <!-- Aspect Ratios --> + <string name="ratio_default">Predefinito (16:9)</string> + <string name="ratio_force_four_three">Forza 4:3</string> + <string name="ratio_force_twenty_one_nine">Forza 21:9</string> + <string name="ratio_force_sixteen_ten">Forza 16:10</string> + <string name="ratio_stretch">Allunga a finestra</string> + + <!-- CPU Accuracy --> + <string name="cpu_accuracy_accurate">Accurata</string> + <string name="cpu_accuracy_unsafe">Non sicura</string> + <string name="cpu_accuracy_paranoid">Paranoico (Lento)</string> + + <!-- Gamepad Buttons --> + <string name="gamepad_d_pad">D-Pad</string> + <string name="gamepad_left_stick">Levetta sinistra</string> + <string name="gamepad_right_stick">Levetta destra</string> + <string name="gamepad_home">Home</string> + <string name="gamepad_screenshot">Screenshot</string> + + <!-- Disk shader cache --> + <string name="preparing_shaders">Preparazione degli shaders</string> + <string name="building_shaders">Costruendo gli shaders</string> + + <!-- Theme options --> + <string name="change_app_theme">Cambia il tema dell\'app</string> + <string name="theme_default">Predefinito</string> + <string name="theme_material_you">Material You</string> + + <!-- Theme Modes --> + <string name="change_theme_mode">Cambia la modalità del tema</string> + <string name="theme_mode_follow_system">Segue il Sistema</string> + <string name="theme_mode_light">Chiaro</string> + <string name="theme_mode_dark">Scuro</string> + + <!-- Black backgrounds theme --> + <string name="use_black_backgrounds">Usa sfondi neri</string> + <string name="use_black_backgrounds_description">Quando utilizzi il tema scuro, applica sfondi neri.</string> + +</resources> diff --git a/src/android/app/src/main/res/values-ja/strings.xml b/src/android/app/src/main/res/values-ja/strings.xml new file mode 100644 index 0000000000..46eda9ef7e --- /dev/null +++ b/src/android/app/src/main/res/values-ja/strings.xml @@ -0,0 +1,335 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <string name="app_disclaimer">このソフトウェアは、Nintendo Switch用のゲームを実行します。 ゲームソフトやキーは含まれません。<br /><br />事前に、 <![CDATA[<b> prod.keys </b>]]> ファイルをデバイスのストレージに配置しておいてください。<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">詳細</a>]]></string> + <string name="emulation_notification_channel_name">エミュレーションが有効です</string> + <string name="emulation_notification_channel_description">エミュレーションの実行中に常設通知を表示します。</string> + <string name="emulation_notification_running">yuzu は実行中です</string> + <string name="notice_notification_channel_description">問題が発生したときに通知を表示します。</string> + <string name="notification_permission_not_granted">通知が許可されていません!</string> + + <!-- Setup strings --> + <string name="welcome">ようこそ!</string> + <string name="welcome_description"><b>yuzu</b> のセットアップ方法を学び、エミュレーションに飛び込みましょう。</string> + <string name="get_started">はじめる</string> + <string name="keys">キー</string> + <string name="keys_description">下のボタンから <b>prod.keys</b> ファイルを選択してください。</string> + <string name="select_keys">キーを選択</string> + <string name="games">ゲーム</string> + <string name="games_description">下のボタンから<b>ゲーム</b>があるフォルダを選択してください。</string> + <string name="done">完了</string> + <string name="done_description">準備が完了しました。\nゲームをお楽しみください!</string> + <string name="text_continue">続行</string> + <string name="next">次へ</string> + <string name="back">戻る</string> + <string name="add_games">ゲームを追加</string> + <string name="add_games_description">ゲームフォルダを選択</string> + + <!-- Home strings --> + <string name="home_games">ゲーム</string> + <string name="home_search">検索</string> + <string name="home_settings">設定</string> + <string name="empty_gamelist">ファイルが見つからないか、ゲームディレクトリがまだ選択されていません。</string> + <string name="search_and_filter_games">ゲームの検索と絞り込み</string> + <string name="select_games_folder">ゲームフォルダを選択</string> + <string name="select_games_folder_description">yuzu がゲームリストに追加できるようにします</string> + <string name="add_games_warning">ゲームフォルダの選択をスキップしますか?</string> + <string name="add_games_warning_description">フォルダを選択しない場合、ゲームはゲームリストに表示されません。</string> + <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> + <string name="home_search_games">ゲームを検索</string> + <string name="games_dir_selected">ゲームディレクトリが選択されました</string> + <string name="install_prod_keys">prod.keys をインストール</string> + <string name="install_prod_keys_description">ゲームの復号化に必要</string> + <string name="install_prod_keys_warning">キーの追加をスキップしますか?</string> + <string name="install_prod_keys_warning_description">製品版ゲームのエミュレーションには、有効なキーが必要です。続行すると自作アプリしか機能しません。</string> + <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string> + <string name="notifications">通知</string> + <string name="notifications_description">下のボタンで通知の権限を許可してください。</string> + <string name="give_permission">許可</string> + <string name="notification_warning">通知の許可をスキップしますか?</string> + <string name="notification_warning_description">yuzuは重要なお知らせを通知できません。</string> + <string name="permission_denied">権限が拒否されました</string> + <string name="permission_denied_description">この権限を複数回拒否したため、システム設定で手動で許可する必要があります。</string> + <string name="about">情報</string> + <string name="about_description">ビルドバージョン、クレジットなど</string> + <string name="warning_help">ヘルプ</string> + <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="invalid_keys_file">無効なキーファイルが選択されました</string> + <string name="install_keys_success">正常にインストールされました</string> + <string name="reading_keys_failure">暗号化キーの読み取りエラー</string> + <string name="invalid_keys_error">暗号化キーが無効です</string> + <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> + <string name="install_keys_failure_description">選択されたファイルが不正または破損しています。キーを再ダンプしてください。</string> + <string name="install_gpu_driver">GPUドライバーをインストール</string> + <string name="install_gpu_driver_description">代替ドライバーをインストールしてパフォーマンスや精度を向上させます</string> + <string name="advanced_settings">高度な設定</string> + <string name="settings_description">エミュレーターの設定を構成します</string> + <string name="search_recently_played">最近プレイした</string> + <string name="search_recently_added">最近追加された</string> + <string name="search_retail">製品版</string> + <string name="search_homebrew">自作</string> + <string name="open_user_folder">yuzu フォルダを開く</string> + <string name="open_user_folder_description">yuzu内部のファイルを管理します</string> + <string name="theme_and_color_description">アプリの見た目を変更</string> + <string name="no_file_manager">ファイルマネージャーが見つかりませんでした</string> + <string name="notification_no_directory_link">yuzuのディレクトリを開けません</string> + <string name="notification_no_directory_link_description">ファイルマネージャのサイドパネルでユーザーフォルダを手動で探してください。</string> + <string name="manage_save_data">セーブデータを管理</string> + <string name="manage_save_data_description">セーブデータが見つかりました。以下のオプションから選択してください。</string> + <string name="import_export_saves_description">セーブファイルをインポート/エクスポート</string> + <string name="import_export_saves_no_profile">セーブデータがありません。ゲームを起動してから再度お試しください。</string> + <string name="save_file_imported_success">インポートが完了しました</string> + <string name="save_file_invalid_zip_structure">セーブデータのディレクトリ構造が無効です</string> + <string name="save_file_invalid_zip_structure_description">最初のサブフォルダ名は、ゲームのタイトルIDである必要があります。</string> + <string name="import_saves">インポート</string> + <string name="export_saves">エクスポート</string> + + <!-- About screen strings --> + <string name="gaia_is_not_real">ガイアは実在しない</string> + <string name="copied_to_clipboard">クリップボードにコピーしました</string> + <string name="about_app_description">オープンソースのSwitchエミュレータ</string> + <string name="contributors">貢献者</string> + <string name="contributors_description">yuzuチームの\u2764で作られた</string> + <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> + <string name="build">ビルド</string> + <string name="support_link">https://discord.gg/u77vRWY</string> + <string name="website_link">https://yuzu-emu.org/</string> + <string name="github_link">https://github.com/yuzu-emu</string> + + <!-- Early access upgrade strings --> + <string name="early_access">早期アクセス</string> + <string name="get_early_access">早期アクセスを手に入れる</string> + <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string> + <string name="get_early_access_description">最先端の機能、アップデートの早期アクセスなど</string> + <string name="early_access_benefits">早期アクセスのメリット</string> + <string name="cutting_edge_features">最先端の機能</string> + <string name="early_access_updates">アップデートの早期アクセス</string> + <string name="no_manual_installation">手動インストールが不要</string> + <string name="prioritized_support">優先的なサポート</string> + <string name="helping_game_preservation">ゲームの保存に貢献</string> + <string name="our_eternal_gratitude">私たちの永遠の感謝</string> + <string name="are_you_interested">興味がありますか?</string> + + <!-- General settings strings --> + <string name="frame_limit_enable">速度制限を有効化</string> + <string name="frame_limit_enable_description">有効にすると、エミュレーション速度が任意の割合に制限されます。</string> + <string name="frame_limit_slider">エミュレーション速度の制限</string> + <string name="frame_limit_slider_description">エミュレーション速度を制限する割合を指定します。デフォルトの100%では、エミュレーションは通常の速度に制限されます。値が高いまたは低いほど、速度制限が増加または減少します。</string> + <string name="cpu_accuracy">CPU精度</string> + + <!-- System settings strings --> + <string name="use_docked_mode">TVモード</string> + <string name="use_docked_mode_description">TVモードでエミュレートします。パフォーマンスが犠牲になりますが、解像度が向上します。</string> + <string name="emulated_region">地域</string> + <string name="emulated_language">言語</string> + <string name="select_rtc_date">RTCの日付を選択</string> + <string name="select_rtc_time">RTCの時刻を選択</string> + <string name="use_custom_rtc">カスタムRTC</string> + <string name="use_custom_rtc_description">現在のシステム時間とは別にカスタムのリアルタイムクロックを設定できます。</string> + <string name="set_custom_rtc">カスタムRTCを設定</string> + + <!-- Graphics settings strings --> + <string name="renderer_api">API</string> + <string name="renderer_accuracy">精度</string> + <string name="renderer_resolution">解像度</string> + <string name="renderer_vsync">垂直同期モード</string> + <string name="renderer_aspect_ratio">アスペクト比</string> + <string name="renderer_scaling_filter">ウィンドウ適応フィルター</string> + <string name="renderer_anti_aliasing">アンチエイリアス方式</string> + <string name="renderer_force_max_clock">最大クロックを強制 (Adrenoのみ)</string> + <string name="renderer_force_max_clock_description">GPUを可能な限り最大クロックで動作させます (過熱制限は引き続き適用されます)。</string> + <string name="renderer_asynchronous_shaders">非同期シェーダー</string> + <string name="renderer_asynchronous_shaders_description">シェーダーを非同期でコンパイルします。コマ落ちが軽減されますが、不具合が発生する可能性があります。</string> + <string name="renderer_debug">グラフィックデバッグ</string> + <string name="renderer_debug_description">オンにすると、グラフィックAPI は低速のデバッグモードに入ります。</string> + <string name="use_disk_shader_cache">シェーダーキャッシュを使用</string> + <string name="use_disk_shader_cache_description">生成したシェーダーをディスクに保存して読み込むことで、コマ落ちを軽減します。</string> + + <!-- Audio settings strings --> + <string name="audio_volume">音量</string> + <string name="audio_volume_description">オーディオ出力の音量を指定します</string> + + <!-- Miscellaneous --> + <string name="slider_default">デフォルト</string> + <string name="ini_saved">設定を保存しました</string> + <string name="gameid_saved">%1$sの設定を保存しました</string> + <string name="error_saving">%1$s.ini の保存エラー: %2$s</string> + <string name="loading">読み込み中…</string> + <string name="reset_setting_confirmation">この設定を初期値にリセットしますか?</string> + <string name="reset_to_default">初期設定に戻す</string> + <string name="reset_all_settings">すべての設定をリセットしますか?</string> + <string name="reset_all_settings_description">すべての詳細設定が初期設定に戻されます。この操作は元に戻せません。</string> + <string name="settings_reset">設定をリセットしました</string> + <string name="close">閉じる</string> + <string name="learn_more">詳細情報</string> + + <!-- GPU driver installation --> + <string name="select_gpu_driver">GPUドライバを選択</string> + <string name="select_gpu_driver_title">現在のGPUドライバーを置き換えますか?</string> + <string name="select_gpu_driver_install">インストール</string> + <string name="select_gpu_driver_default">デフォルト</string> + <string name="select_gpu_driver_install_success">%s をインストールしました</string> + <string name="select_gpu_driver_use_default">デフォルトのGPUドライバーを使用します</string> + <string name="select_gpu_driver_error">選択されたドライバが無効なため、システムのデフォルトを使用します!</string> + <string name="system_gpu_driver">システムのGPUドライバ</string> + <string name="installing_driver">インストール中…</string> + + <!-- Preferences Screen --> + <string name="preferences_settings">設定</string> + <string name="preferences_general">全般</string> + <string name="preferences_system">システム</string> + <string name="preferences_graphics">グラフィック</string> + <string name="preferences_audio">サウンド</string> + <string name="preferences_theme">テーマと色</string> + + <!-- ROM loading errors --> + <string name="loader_error_encrypted">ROMが暗号化されています</string> + <string name="loader_error_encrypted_roms_description"><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart/#dumping-cartridge-games\">ゲームカートリッジ</a>や<a href=\"https://yuzu-emu.org/help/quickstart/#dumping-installed-titles-eshop\">インストール済みのタイトル</a>を再度ダンプするためのガイドに従ってください。]]></string> + <string name="loader_error_encrypted_keys_description"><![CDATA[ゲームを復号化するために <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> ファイルがインストールされていることを確認してください。]]></string> + <string name="loader_error_video_core">ビデオコアの初期化中にエラーが発生しました</string> + <string name="loader_error_video_core_description">これは通常、互換性のないGPUドライバーが原因で発生します。 カスタムGPUドライバーをインストールすると、問題が解決する可能性があります。</string> + <string name="loader_error_invalid_format">ROMの読み込みに失敗しました</string> + <string name="loader_error_file_not_found">ROMファイルが存在しません</string> + + <!-- Emulation Menu --> + <string name="emulation_exit">エミュレーションを終了</string> + <string name="emulation_done">完了</string> + <string name="emulation_fps_counter">FPSカウンター</string> + <string name="emulation_toggle_controls">コントロールを切り替え</string> + <string name="emulation_dpad_slide">十字キーのスライド操作</string> + <string name="emulation_haptics">振動</string> + <string name="emulation_show_overlay">オーバーレイを表示</string> + <string name="emulation_toggle_all">すべて選択</string> + <string name="emulation_control_adjust">オーバーレイを調整</string> + <string name="emulation_control_scale">大きさ</string> + <string name="emulation_control_opacity">不透明度</string> + <string name="emulation_touch_overlay_reset">リセット</string> + <string name="emulation_touch_overlay_edit">オーバーレイを編集</string> + <string name="emulation_pause">エミュレーションを一時停止</string> + <string name="emulation_unpause">エミュレーションを再開</string> + <string name="emulation_input_overlay">オーバーレイオプション</string> + <string name="emulation_game_loading">ロード中…</string> + + <string name="load_settings">設定をロード中…</string> + + <!-- Software keyboard --> + <string name="software_keyboard">ソフトウェアキーボード</string> + + <!-- Errors and warnings --> + <string name="abort_button">中断</string> + <string name="continue_button">続行</string> + <string name="system_archive_not_found">システムアーカイブが見つかりません</string> + <string name="system_archive_not_found_message">%s が見つかりません。システムアーカイブをダンプしてください。\nエミュレーションを続行すると、クラッシュやバグが発生する可能性があります。</string> + <string name="system_archive_general">システムアーカイブ</string> + <string name="save_load_error">セーブ/ロード エラー</string> + <string name="fatal_error">致命的なエラー</string> + <string name="fatal_error_message">致命的なエラーが発生しました。詳細はログを確認してください。\nエミュレーションを続行するとクラッシュやバグが発生する可能性があります。</string> + <string name="performance_warning">この設定をオフにすると、エミュレーションのパフォーマンスが著しく低下します!最高の体験を得るためには、この設定を有効にしておくことをお勧めします。</string> + + <!-- Region Names --> + <string name="region_japan">日本</string> + <string name="region_usa">アメリカ</string> + <string name="region_europe">ヨーロッパ</string> + <string name="region_australia">オーストラリア</string> + <string name="region_china">中国</string> + <string name="region_korea">韓国</string> + <string name="region_taiwan">台湾</string> + + <!-- Language Names --> + <string name="language_japanese">日本語</string> + <string name="language_english">英語</string> + <string name="language_french">フランス語 (Français)</string> + <string name="langauge_german">ドイツ語 (Deutsch)</string> + <string name="language_italian">イタリア語 (Italiano)</string> + <string name="language_spanish">スペイン語 (Español)</string> + <string name="language_chinese">中国語 (简体中文)</string> + <string name="language_korean">韓国語 (한국어)</string> + <string name="language_dutch">オランダ語 (Nederlands)</string> + <string name="language_portuguese">ポルトガル語 (Português)</string> + <string name="language_russian">ロシア語 (Русский)</string> + <string name="language_taiwanese">台湾語 (台湾)</string> + <string name="language_british_english">イギリス英語</string> + <string name="language_canadian_french">フランス語(カナダ) (Français canadien)</string> + <string name="language_latin_american_spanish">スペイン語(ラテンアメリカ) (Español latinoamericano)</string> + <string name="language_simplified_chinese">中国語 (简体中文)</string> + <string name="language_traditional_chinese">繁体字中国語 (正體中文)</string> + <string name="language_brazilian_portuguese">ポルトガル語(ブラジル) (Português do Brasil)</string> + + <!-- Renderer APIs --> + <string name="renderer_vulkan">Vulkan</string> + <string name="renderer_none">なし</string> + + <!-- Renderer Accuracy --> + <string name="renderer_accuracy_normal">標準</string> + <string name="renderer_accuracy_high">高い</string> + <string name="renderer_accuracy_extreme">最高 (低速)</string> + + <!-- Resolutions --> + <string name="resolution_half">0.5X (360p/540p)</string> + <string name="resolution_three_quarter">0.75X (540p/810p)</string> + <string name="resolution_one">1X (720p/1080p)</string> + <string name="resolution_two">2X (1440p/2160p) (低速)</string> + <string name="resolution_three">3X (2160p/3240p) (低速)</string> + <string name="resolution_four">4X (2880p/4320p) (低速)</string> + + <!-- Renderer VSync --> + <string name="renderer_vsync_immediate">Immediate (オフ)</string> + <string name="renderer_vsync_mailbox">Mailbox</string> + <string name="renderer_vsync_fifo">FIFO (オン)</string> + <string name="renderer_vsync_fifo_relaxed">FIFO Relaxed</string> + + <!-- Scaling Filters --> + <string name="scaling_filter_nearest_neighbor">Nearest Neighbor</string> + <string name="scaling_filter_bilinear">Bilinear</string> + <string name="scaling_filter_bicubic">Bicubic</string> + <string name="scaling_filter_gaussian">Gaussian</string> + <string name="scaling_filter_scale_force">ScaleForce</string> + <string name="scaling_filter_fsr">AMD FidelityFX™ Super Resolution</string> + + <!-- Anti-Aliasing --> + <string name="anti_aliasing_none">なし</string> + <string name="anti_aliasing_fxaa">FXAA</string> + <string name="anti_aliasing_smaa">SMAA</string> + + <!-- Aspect Ratios --> + <string name="ratio_default">デフォルト (16:9)</string> + <string name="ratio_force_four_three">強制 4:3</string> + <string name="ratio_force_twenty_one_nine">強制 21:9</string> + <string name="ratio_force_sixteen_ten">強制 16:10</string> + <string name="ratio_stretch">ウィンドウに合わせる</string> + + <!-- CPU Accuracy --> + <string name="cpu_accuracy_accurate">正確</string> + <string name="cpu_accuracy_unsafe">不安定</string> + <string name="cpu_accuracy_paranoid">パラノイド (低速)</string> + + <!-- Gamepad Buttons --> + <string name="gamepad_d_pad">方向ボタン</string> + <string name="gamepad_left_stick">Lスティック</string> + <string name="gamepad_right_stick">Rスティック</string> + <string name="gamepad_home">HOMEボタン</string> + <string name="gamepad_screenshot">スクリーンショット</string> + + <!-- Disk shader cache --> + <string name="preparing_shaders">シェーダーを準備しています</string> + <string name="building_shaders">シェーダーを構築しています</string> + + <!-- Theme options --> + <string name="change_app_theme">アプリのテーマ</string> + <string name="theme_default">デフォルト</string> + <string name="theme_material_you">Material You</string> + + <!-- Theme Modes --> + <string name="change_theme_mode">テーマモード</string> + <string name="theme_mode_follow_system">システムに従う</string> + <string name="theme_mode_light">ライト</string> + <string name="theme_mode_dark">ダーク</string> + + <!-- Black backgrounds theme --> + <string name="use_black_backgrounds">黒色の背景を使用</string> + <string name="use_black_backgrounds_description">ダークテーマの使用時は、黒色の背景を有効にしてください。</string> + +</resources> diff --git a/src/android/app/src/main/res/values-ko/strings.xml b/src/android/app/src/main/res/values-ko/strings.xml new file mode 100644 index 0000000000..5da80ab4be --- /dev/null +++ b/src/android/app/src/main/res/values-ko/strings.xml @@ -0,0 +1,337 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <string name="app_disclaimer">이 소프트웨어는 닌텐도 스위치 게임 콘솔용 게임을 실행합니다. 게임 타이틀이나 keys는 포함되어 있지 않습니다.<br /><br />시작하기 전에 장치 저장소에서 <![CDATA[<b> prod.keys </b>]]> 파일을 찾아주세요.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">자세히 알아보기</a>]]></string> + <string name="emulation_notification_channel_name">에뮬레이션이 활성화됨</string> + <string name="emulation_notification_channel_description">에뮬레이션이 실행 중일 때 영구 알림을 표시합니다.</string> + <string name="emulation_notification_running">yuzu가 실행 중입니다.</string> + <string name="notice_notification_channel_name">알림 및 오류</string> + <string name="notice_notification_channel_description">문제가 발생하면 알림을 표시합니다.</string> + <string name="notification_permission_not_granted">알림 권한이 부여되지 않았습니다!</string> + + <!-- Setup strings --> + <string name="welcome">환영합니다!</string> + <string name="welcome_description"><b>yuzu</b> 를 설정하고 에뮬레이션으로 이동하는 방법을 알아보세요.</string> + <string name="get_started">시작하기</string> + <string name="keys">Keys</string> + <string name="keys_description">아래 버튼을 사용하여 <b>prod.keys</b> 파일을 선택합니다.</string> + <string name="select_keys">keys 선택</string> + <string name="games">게임</string> + <string name="games_description">아래 버튼으로 <b>게임</b> 폴더를 선택합니다.</string> + <string name="done">완료</string> + <string name="done_description">모든 준비가 완료되었습니다.\n게임을 즐기세요!</string> + <string name="text_continue">계속</string> + <string name="next">다음</string> + <string name="back">뒤로</string> + <string name="add_games">게임 추가</string> + <string name="add_games_description">게임 폴더 선택</string> + + <!-- Home strings --> + <string name="home_games">게임</string> + <string name="home_search">검색</string> + <string name="home_settings">설정</string> + <string name="empty_gamelist">파일을 찾을 수 없거나 아직 게임 디렉토리를 선택하지 않았습니다.</string> + <string name="search_and_filter_games">게임 검색 및 필터링</string> + <string name="select_games_folder">게임 폴더 선택</string> + <string name="select_games_folder_description">yuzu가 게임 목록을 채울 수 있도록 허용</string> + <string name="add_games_warning">게임 폴더 선택을 건너뛰겠습니까?</string> + <string name="add_games_warning_description">폴더를 선택하지 않으면 게임 목록에 게임이 표시되지 않습니다.</string> + <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> + <string name="home_search_games">게임 검색</string> + <string name="games_dir_selected">게임 디렉터리 선택</string> + <string name="install_prod_keys">prod.keys 설치</string> + <string name="install_prod_keys_description">판매용 게임 암호 해독에 요구</string> + <string name="install_prod_keys_warning">keys 추가를 건너뛰겠습니까?</string> + <string name="install_prod_keys_warning_description">정품 게임을 에뮬레이트하려면 유효한 keys가 필요합니다. 계속하면 자체 제작 앱만 작동합니다.</string> + <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string> + <string name="notifications">알림</string> + <string name="notifications_description">아래 버튼으로 알림 권한을 부여합니다.</string> + <string name="give_permission">권한 부여</string> + <string name="notification_warning">알림 권한 부여를 건너뛰겠습니까?</string> + <string name="notification_warning_description">yuzu는 중요한 정보를 알려드리지 않습니다.</string> + <string name="permission_denied">권한 거부됨</string> + <string name="permission_denied_description">이 권한을 너무 많이 거부했으므로 이제 시스템 설정에서 수동으로 권한을 부여해야 합니다.</string> + <string name="about">정보</string> + <string name="about_description">빌드 버전, 크레딧 등</string> + <string name="warning_help">도움말</string> + <string name="warning_skip">건너뛰기</string> + <string name="warning_cancel">취소</string> + <string name="install_amiibo_keys">Amiibo keys 설치</string> + <string name="install_amiibo_keys_description">게임에서 아미보 사용 시 필요</string> + <string name="invalid_keys_file">잘못된 keys 파일 선택</string> + <string name="install_keys_success">keys가 성공적으로 설치됨</string> + <string name="reading_keys_failure">암호화 keys 읽기 오류</string> + <string name="invalid_keys_error">잘못된 암호화 keys</string> + <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> + <string name="install_keys_failure_description">선택한 파일이 잘못되었거나 손상되었습니다. keys를 다시 덤프하세요.</string> + <string name="install_gpu_driver">GPU 드라이버 설치</string> + <string name="install_gpu_driver_description">잠재적으로 더 나은 성능 또는 정확성을 위해 대체 드라이버를 설치하세요.</string> + <string name="advanced_settings">고급 설정</string> + <string name="settings_description">에뮬레이터 설정 구성</string> + <string name="search_recently_played">최근 플레이한 게임</string> + <string name="search_recently_added">최근 추가한 게임</string> + <string name="search_retail">판매용</string> + <string name="search_homebrew">홈브류</string> + <string name="open_user_folder">yuzu 폴더 열기</string> + <string name="open_user_folder_description">yuzu의 내부 파일 관리</string> + <string name="theme_and_color_description">앱 모양 수정</string> + <string name="no_file_manager">파일 관리자를 찾을 수 없음</string> + <string name="notification_no_directory_link">yuzu 디렉토리를 열 수 없음</string> + <string name="notification_no_directory_link_description">파일 관리자의 사이드 패널에서 사용자 폴더를 수동으로 찾아주세요.</string> + <string name="manage_save_data">저장 데이터 관리</string> + <string name="manage_save_data_description">데이터를 저장했습니다. 아래에서 옵션을 선택하세요.</string> + <string name="import_export_saves_description">저장 파일 가져오기 또는 내보내기</string> + <string name="import_export_saves_no_profile">저장 데이터를 찾을 수 없습니다. 게임을 실행한 후 다시 시도하세요.</string> + <string name="save_file_imported_success">가져오기 성공</string> + <string name="save_file_invalid_zip_structure">저장 디렉터리 구조가 잘못됨</string> + <string name="save_file_invalid_zip_structure_description">첫 번째 하위 폴더 이름은 게임의 타이틀 ID여야 합니다.</string> + <string name="import_saves">가져오기</string> + <string name="export_saves">내보내기</string> + + <!-- About screen strings --> + <string name="gaia_is_not_real">가이아는 진짜가 아님</string> + <string name="copied_to_clipboard">클립보드에 복사</string> + <string name="about_app_description">오픈 소스 스위치 에뮬레이터</string> + <string name="contributors">기여자</string> + <string name="contributors_description">yuzu 팀의 \u2764로 제작</string> + <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> + <string name="build">빌드</string> + <string name="support_link">https://discord.gg/u77vRWY</string> + <string name="website_link">https://yuzu-emu.org/</string> + <string name="github_link">https://github.com/yuzu-emu</string> + + <!-- Early access upgrade strings --> + <string name="early_access">미리 체험하기</string> + <string name="get_early_access">미리 체험하기 신청</string> + <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string> + <string name="get_early_access_description">최첨단 기능, 미리 체험하기 업데이트 등</string> + <string name="early_access_benefits">미리 체험하기 혜택</string> + <string name="cutting_edge_features">최첨단 기능</string> + <string name="early_access_updates">미리 체험하기 업데이트</string> + <string name="no_manual_installation">수동 설치 불필요</string> + <string name="prioritized_support">우선 지원</string> + <string name="helping_game_preservation">게임 보존 도움주기</string> + <string name="our_eternal_gratitude">영원한 감사의 마음을 전합니다</string> + <string name="are_you_interested">관심 있으세요?</string> + + <!-- General settings strings --> + <string name="frame_limit_enable">제한 속도 활성화</string> + <string name="frame_limit_enable_description">활성화하면 에뮬레이션 속도가 정상 속도의 지정된 비율로 제한됩니다.</string> + <string name="frame_limit_slider">속도 제한 비율</string> + <string name="frame_limit_slider_description">에뮬레이션 속도를 제한할 비율을 지정합니다. 기본값인 100%로 설정하면 에뮬레이션이 정상 속도로 제한됩니다. 값이 높거나 낮으면 속도 제한이 증가하거나 감소합니다.</string> + <string name="cpu_accuracy">CPU 정확도</string> + + <!-- System settings strings --> + <string name="use_docked_mode">도킹 모드</string> + <string name="use_docked_mode_description">도킹 모드에서 에뮬레이션하면 성능이 저하되는 대신 해상도가 향상됩니다.</string> + <string name="emulated_region">에뮬레이트된 지역</string> + <string name="emulated_language">에뮬레이트된 언어</string> + <string name="select_rtc_date">RTC 날짜 선택</string> + <string name="select_rtc_time">RTC 시간 선택</string> + <string name="use_custom_rtc">커스텀 RTC 활성화</string> + <string name="use_custom_rtc_description">이 설정을 사용하면 현재 시스템 시간과 별도로 사용자 지정 실시간 시계를 설정할 수 있음</string> + <string name="set_custom_rtc">커스텀 RTC 설정</string> + + <!-- Graphics settings strings --> + <string name="renderer_api">API</string> + <string name="renderer_accuracy">정확도 수준</string> + <string name="renderer_resolution">해상도</string> + <string name="renderer_vsync">수직동기화 모드</string> + <string name="renderer_aspect_ratio">화면비</string> + <string name="renderer_scaling_filter">창 적응 필터</string> + <string name="renderer_anti_aliasing">안티-에일리어싱 방법</string> + <string name="renderer_force_max_clock">최대 클럭 강제 설정 (아드레노만 해당)</string> + <string name="renderer_force_max_clock_description">GPU가 가능한 최대 클럭으로 실행되도록 강제합니다 (열 제약 조건은 여전히 적용됩니다).</string> + <string name="renderer_asynchronous_shaders">비동기 셰이더 사용</string> + <string name="renderer_asynchronous_shaders_description">셰이더를 비동기식으로 컴파일하므로 끊김 현상이 줄어들지만 글리치가 발생할 수 있습니다.</string> + <string name="renderer_debug">그래픽 디버깅 활성화</string> + <string name="renderer_debug_description">이 옵션을 선택하면 그래픽 API가 느린 디버깅 모드로 전환됩니다.</string> + <string name="use_disk_shader_cache">디스크 셰이더 캐시 사용</string> + <string name="use_disk_shader_cache_description">생성된 셰이더를 디스크에 저장하고 불러오기하여 끊김 현상을 줄입니다.</string> + + <!-- Audio settings strings --> + <string name="audio_volume">볼륨</string> + <string name="audio_volume_description">오디오 출력의 볼륨을 지정합니다.</string> + + <!-- Miscellaneous --> + <string name="slider_default">기본값</string> + <string name="ini_saved">저장된 설정</string> + <string name="gameid_saved">%1$s를 위해 저장된 설정</string> + <string name="error_saving">%1$s.ini 저장 중 오류: %2$s</string> + <string name="loading">불러오기 중...</string> + <string name="reset_setting_confirmation">이 설정을 기본값으로 되돌리겠습니까?</string> + <string name="reset_to_default">기본값으로 재설정</string> + <string name="reset_all_settings">모든 설정을 초기화하겠습니까?</string> + <string name="reset_all_settings_description">모든 고급 설정이 기본 구성으로 재설정됩니다. 이 설정은 되돌릴 수 없습니다.</string> + <string name="settings_reset">설정 초기화</string> + <string name="close">닫기</string> + <string name="learn_more">자세히 알아보기</string> + + <!-- GPU driver installation --> + <string name="select_gpu_driver">GPU 드라이버 선택</string> + <string name="select_gpu_driver_title">현재 사용 중인 GPU 드라이버를 교체하겠습니까?</string> + <string name="select_gpu_driver_install">설치</string> + <string name="select_gpu_driver_default">기본값</string> + <string name="select_gpu_driver_install_success">설치된 %s</string> + <string name="select_gpu_driver_use_default">기본 GPU 드라이버 사용</string> + <string name="select_gpu_driver_error">시스템 기본값을 사용하여 잘못된 드라이버를 선택했습니다!</string> + <string name="system_gpu_driver">시스템 GPU 드라이버</string> + <string name="installing_driver">드라이버 설치 중...</string> + + <!-- Preferences Screen --> + <string name="preferences_settings">설정</string> + <string name="preferences_general">일반</string> + <string name="preferences_system">시스템</string> + <string name="preferences_graphics">그래픽</string> + <string name="preferences_audio">오디오</string> + <string name="preferences_theme">테마 및 색상</string> + + <!-- ROM loading errors --> + <string name="loader_error_encrypted">롬이 암호화되었음</string> + <string name="loader_error_encrypted_roms_description"><![CDATA[가이드에 따라 <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-cartridge-games\">게임 카트리지</a> 또는 <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-installed-titles-eshop\">설치된 타이틀</a>를 다시 덤프하세요.]]></string> + <string name="loader_error_encrypted_keys_description"><![CDATA[P게임을 해독할 수 있도록 <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> 파일이 설치되어 있는지 확인하세요.]]></string> + <string name="loader_error_video_core">비디오 코어를 초기화하는 동안 오류 발생</string> + <string name="loader_error_video_core_description">이 문제는 일반적으로 호환되지 않는 GPU 드라이버로 인해 발생합니다. 사용자 지정 GPU 드라이버를 설치하면 이 문제가 해결될 수 있습니다.</string> + <string name="loader_error_invalid_format">롬을 불러올 수 없음</string> + <string name="loader_error_file_not_found">롬 파일이 존재하지 않음</string> + + <!-- Emulation Menu --> + <string name="emulation_exit">에뮬레이션 종료</string> + <string name="emulation_done">완료</string> + <string name="emulation_fps_counter">FPS 카운터</string> + <string name="emulation_toggle_controls">토글 제어</string> + <string name="emulation_rel_stick_center">상대 스틱 센터</string> + <string name="emulation_dpad_slide">십자패드 슬라이드</string> + <string name="emulation_haptics">햅틱</string> + <string name="emulation_show_overlay">오버레이 표시</string> + <string name="emulation_toggle_all">모두 토글</string> + <string name="emulation_control_adjust">오버레이 조정</string> + <string name="emulation_control_scale">스케일</string> + <string name="emulation_control_opacity">불투명도</string> + <string name="emulation_touch_overlay_reset">오버레이 재설정</string> + <string name="emulation_touch_overlay_edit">오버레이 편집</string> + <string name="emulation_pause">에뮬레이션 일시 중지</string> + <string name="emulation_unpause">에뮬레이션 일시 중지 해제</string> + <string name="emulation_input_overlay">오버레이 옵션</string> + <string name="emulation_game_loading">게임 불러오기 중...</string> + + <string name="load_settings">설정 불러오기 중...</string> + + <!-- Software keyboard --> + <string name="software_keyboard">가상 키보드</string> + + <!-- Errors and warnings --> + <string name="abort_button">정보</string> + <string name="continue_button">계속</string> + <string name="system_archive_not_found">시스템 아카이브를 찾을 수 없음</string> + <string name="system_archive_not_found_message">%s가 누락되었습니다. 시스템 아카이브를 덤프하세요.\n에뮬레이션을 계속하면 충돌 및 버그가 발생할 수 있습니다.</string> + <string name="system_archive_general">시스템 아카이브</string> + <string name="save_load_error">저장하기/불러오기 오류</string> + <string name="fatal_error">치명적인 오류</string> + <string name="fatal_error_message">치명적인 오류가 발생했습니다. 자세한 내용은 로그를 확인하십시오.\n에뮬레이션을 계속하면 충돌 및 버그가 발생할 수 있습니다.</string> + <string name="performance_warning">이 설정을 끄면 에뮬레이션 성능이 크게 저하됩니다! 최상의 환경을 위해 이 설정을 활성화된 상태로 두는 것이 좋습니다.</string> + + <!-- Region Names --> + <string name="region_japan">일본</string> + <string name="region_usa">미국</string> + <string name="region_europe">유럽</string> + <string name="region_australia">호주</string> + <string name="region_china">중국</string> + <string name="region_korea">대한민국</string> + <string name="region_taiwan">타이완</string> + + <!-- Language Names --> + <string name="language_japanese">일본어 (日本語)</string> + <string name="language_english">영어 (English)</string> + <string name="language_french">프랑스어 (Français)</string> + <string name="langauge_german">독일어(Deutsch)</string> + <string name="language_italian">이탈리아어 (Italiano)</string> + <string name="language_spanish">스페인어 (Español)</string> + <string name="language_chinese">중국어 (简体中文)</string> + <string name="language_korean">한국어 (Korean)</string> + <string name="language_dutch">네덜란드어 (Nederlands)</string> + <string name="language_portuguese">포르투갈어 (Português)</string> + <string name="language_russian">러시아어 (Русский)</string> + <string name="language_taiwanese">대만어 (台湾)</string> + <string name="language_british_english">영어 (British English)</string> + <string name="language_canadian_french">캐나다 프랑스어 (Français canadien)</string> + <string name="language_latin_american_spanish">라틴 아메리카 스페인어 (Español latinoamericano)</string> + <string name="language_simplified_chinese">중국어 간체 (简体中文)</string> + <string name="language_traditional_chinese">중국어 번체 (正體中文)</string> + <string name="language_brazilian_portuguese">브라질 포르투갈어 (Português do Brasil)</string> + + <!-- Renderer APIs --> + <string name="renderer_vulkan">불칸</string> + <string name="renderer_none">없음</string> + + <!-- Renderer Accuracy --> + <string name="renderer_accuracy_normal">보통</string> + <string name="renderer_accuracy_high">높음</string> + <string name="renderer_accuracy_extreme">극한 (느림)</string> + + <!-- Resolutions --> + <string name="resolution_half">0.5X (360p/540p)</string> + <string name="resolution_three_quarter">0.75X (540p/810p)</string> + <string name="resolution_one">1X (720p/1080p)</string> + <string name="resolution_two">2X (1440p/2160p) (느림)</string> + <string name="resolution_three">3X (2160p/3240p) (느림)</string> + <string name="resolution_four">4X (2880p/4320p) (느림)</string> + + <!-- Renderer VSync --> + <string name="renderer_vsync_immediate">즉시 (끔)</string> + <string name="renderer_vsync_mailbox">메일박스</string> + <string name="renderer_vsync_fifo">FIFO (켬)</string> + <string name="renderer_vsync_fifo_relaxed">FIFO 릴랙스</string> + + <!-- Scaling Filters --> + <string name="scaling_filter_nearest_neighbor">가장 가까운 이웃</string> + <string name="scaling_filter_bilinear">이중선형</string> + <string name="scaling_filter_bicubic">고등차수보간</string> + <string name="scaling_filter_gaussian">가우시안</string> + <string name="scaling_filter_scale_force">스케일포스</string> + <string name="scaling_filter_fsr">AMD FidelityFX™ 초고해상도</string> + + <!-- Anti-Aliasing --> + <string name="anti_aliasing_none">없음</string> + <string name="anti_aliasing_fxaa">FXAA</string> + <string name="anti_aliasing_smaa">SMAA</string> + + <!-- Aspect Ratios --> + <string name="ratio_default">기본 (16:9)</string> + <string name="ratio_force_four_three">강제 4:3</string> + <string name="ratio_force_twenty_one_nine">강제 21:9</string> + <string name="ratio_force_sixteen_ten">강제 16:10</string> + <string name="ratio_stretch">창에 맞게 늘림</string> + + <!-- CPU Accuracy --> + <string name="cpu_accuracy_accurate">정확함</string> + <string name="cpu_accuracy_unsafe">안전하지 않음</string> + <string name="cpu_accuracy_paranoid">편집증 (느림)</string> + + <!-- Gamepad Buttons --> + <string name="gamepad_d_pad">십자패드</string> + <string name="gamepad_left_stick">L 스틱</string> + <string name="gamepad_right_stick">R 스틱</string> + <string name="gamepad_home">홈</string> + <string name="gamepad_screenshot">스크린샷</string> + + <!-- Disk shader cache --> + <string name="preparing_shaders">셰이더 준비하기</string> + <string name="building_shaders">셰이더 빌드 중</string> + + <!-- Theme options --> + <string name="change_app_theme">앱 테마 변경</string> + <string name="theme_default">기본값</string> + <string name="theme_material_you">Material You</string> + + <!-- Theme Modes --> + <string name="change_theme_mode">테마 모드 변경</string> + <string name="theme_mode_follow_system">팔로우 시스템</string> + <string name="theme_mode_light">밝음</string> + <string name="theme_mode_dark">어두움</string> + + <!-- Black backgrounds theme --> + <string name="use_black_backgrounds">검은색 배경 사용</string> + <string name="use_black_backgrounds_description">어두운 테마를 사용할 때는 검은색 배경을 적용합니다.</string> + +</resources> diff --git a/src/android/app/src/main/res/values-nb/strings.xml b/src/android/app/src/main/res/values-nb/strings.xml new file mode 100644 index 0000000000..3e1f9bce53 --- /dev/null +++ b/src/android/app/src/main/res/values-nb/strings.xml @@ -0,0 +1,337 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <string name="app_disclaimer">Denne programvaren vil kjøre spill for Nintendo Switch-spillkonsollen. Ingen spilltitler eller nøkler er inkludert.<br /><br />Før du begynner, må du finne <![CDATA[<b> prod.keys </b>]]> filen din på enhetslagringen.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Lær mer</a>]]></string> + <string name="emulation_notification_channel_name">Emulering er aktiv</string> + <string name="emulation_notification_channel_description">Viser et vedvarende varsel når emuleringen kjører.</string> + <string name="emulation_notification_running">Yuzu kjører</string> + <string name="notice_notification_channel_name">Merknader og feil</string> + <string name="notice_notification_channel_description">Viser varsler når noe går galt.</string> + <string name="notification_permission_not_granted">Varslingstillatelse ikke gitt!</string> + + <!-- Setup strings --> + <string name="welcome">Velkommen!</string> + <string name="welcome_description">Lær å sette opp <b>yuzu</b> og hopp inn i emulering.</string> + <string name="get_started">Kom i gang</string> + <string name="keys">Nøkler</string> + <string name="keys_description">Velg din <b>prod.keys</b> fil ved å bruke knappen under.</string> + <string name="select_keys">Velg nøkler</string> + <string name="games">Spill</string> + <string name="games_description">Velg din <b>Spill</b> mappe ved å bruke knappen under.</string> + <string name="done">Ferdig</string> + <string name="done_description">Nå er du klar.\nGled deg til å spille!</string> + <string name="text_continue">Fortsett</string> + <string name="next">Neste</string> + <string name="back">Tilbake</string> + <string name="add_games">Legg til spill</string> + <string name="add_games_description">Velg din spillmappe</string> + + <!-- Home strings --> + <string name="home_games">Spill</string> + <string name="home_search">Søk</string> + <string name="home_settings">Innstillinger</string> + <string name="empty_gamelist">Ingen filer ble funnet eller ingen spillkatalog er valgt ennå.</string> + <string name="search_and_filter_games">Søk og filtrer spill</string> + <string name="select_games_folder">Velg spillmappe</string> + <string name="select_games_folder_description">Gjør det mulig for yuzu å fylle ut spillelisten.</string> + <string name="add_games_warning">Hoppe over valg av spillmappe?</string> + <string name="add_games_warning_description">Spill vises ikke i Spill-listen hvis en mappe ikke er valgt.</string> + <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> + <string name="home_search_games">Søk i spill</string> + <string name="games_dir_selected">Spillkatalogen er valgt</string> + <string name="install_prod_keys">Installer prod.keys</string> + <string name="install_prod_keys_description">Nødvendig for å dekryptere spill</string> + <string name="install_prod_keys_warning">Hoppe over å legge til nøkler?</string> + <string name="install_prod_keys_warning_description">Gyldige nøkler er påkrevd for å emulere spill. Bare hjemmebryggede apper vil fungere hvis du fortsetter.</string> + <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string> + <string name="notifications">Varsler</string> + <string name="notifications_description">Gi varslingstillatelse med knappen nedenfor.</string> + <string name="give_permission">Gi tillatelse</string> + <string name="notification_warning">Hoppe over å gi tillatelse til varsling?</string> + <string name="notification_warning_description">yuzu vil ikke kunne varsle deg om viktig informasjon.</string> + <string name="permission_denied">Tillatelse avslått</string> + <string name="permission_denied_description">Du har nektet denne tillatelsen for mange ganger, og nå må du gi den manuelt i systeminnstillingene.</string> + <string name="about">Om</string> + <string name="about_description">Byggeversjon, kildehenvisninger og mer</string> + <string name="warning_help">Hjelp</string> + <string name="warning_skip">Hopp over</string> + <string name="warning_cancel">Avbryt</string> + <string name="install_amiibo_keys">Installer Amiibo-nøkler</string> + <string name="install_amiibo_keys_description">Kreves for å bruke Amiibo i spillet</string> + <string name="invalid_keys_file">Ugyldig nøkkelfil valgt</string> + <string name="install_keys_success">Nøkler vellykket installert</string> + <string name="reading_keys_failure">Feil ved lesing av krypteringsnøkler</string> + <string name="invalid_keys_error">Ugyldige krypteringsnøkler</string> + <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> + <string name="install_keys_failure_description">Den valgte filen er feil eller ødelagt. Vennligst dump nøklene på nytt.</string> + <string name="install_gpu_driver">Installer GPU-driver</string> + <string name="install_gpu_driver_description">Installer alternative drivere for potensielt bedre ytelse eller nøyaktighet.</string> + <string name="advanced_settings">Avanserte innstillinger</string> + <string name="settings_description">Konfigurere emulatorinnstillinger</string> + <string name="search_recently_played">Nylig spilt</string> + <string name="search_recently_added">Nylig lagt til</string> + <string name="search_retail">Butikkhandel</string> + <string name="search_homebrew">Homebrew</string> + <string name="open_user_folder">Åpne yuzu-mappen</string> + <string name="open_user_folder_description">Administrere yuzus interne filer</string> + <string name="theme_and_color_description">Endre appens utseende</string> + <string name="no_file_manager">Ingen filbehandler funnet</string> + <string name="notification_no_directory_link">Kunne ikke åpne yuzu-katalogen</string> + <string name="notification_no_directory_link_description">Finn brukermappen manuelt med filbehandlingens sidepanel.</string> + <string name="manage_save_data">Administrere lagringsdata</string> + <string name="manage_save_data_description">Lagringsdata funnet. Velg et alternativ nedenfor.</string> + <string name="import_export_saves_description">Importer eller eksporter lagringsfiler</string> + <string name="import_export_saves_no_profile">Ingen lagringsdata funnet. Start et nytt spill og prøv på nytt.</string> + <string name="save_file_imported_success">Vellykket import</string> + <string name="save_file_invalid_zip_structure">Ugyldig struktur for lagringskatalog</string> + <string name="save_file_invalid_zip_structure_description">Det første undermappenavnet må være spillets tittel-ID.</string> + <string name="import_saves">Importer</string> + <string name="export_saves">Eksporter</string> + + <!-- About screen strings --> + <string name="gaia_is_not_real">Gaia er ikke ekte</string> + <string name="copied_to_clipboard">Kopiert til utklippstavlen</string> + <string name="about_app_description">En Switch-emulator med åpen kildekode</string> + <string name="contributors">Bidragsytere</string> + <string name="contributors_description">Laget med \u2764 fra yuzu-teamet</string> + <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> + <string name="build">Bygg</string> + <string name="support_link">https://discord.gg/u77vRWY</string> + <string name="website_link">https://yuzu-emu.org/</string> + <string name="github_link">https://github.com/yuzu-emu</string> + + <!-- Early access upgrade strings --> + <string name="early_access">Tidlig tilgang</string> + <string name="get_early_access">Få tidlig tilgang</string> + <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string> + <string name="get_early_access_description">Banebrytende funksjoner, tidlig tilgang til oppdateringer og mye mer.</string> + <string name="early_access_benefits">Fordeler ved tidlig tilgang</string> + <string name="cutting_edge_features">Avanserte funksjoner</string> + <string name="early_access_updates">Tidlig tilgang til oppdateringer</string> + <string name="no_manual_installation">Ingen manuell installasjon</string> + <string name="prioritized_support">Prioritert støtte</string> + <string name="helping_game_preservation">Bidra til bevaring av spill</string> + <string name="our_eternal_gratitude">Vår evige takknemlighet</string> + <string name="are_you_interested">Er du interessert?</string> + + <!-- General settings strings --> + <string name="frame_limit_enable">Aktiver hastighetsbegrensning</string> + <string name="frame_limit_enable_description">Når aktivert, begrenses emuleringshastigheten til en angitt prosentandel av normal hastighet.</string> + <string name="frame_limit_slider">Hastighetsbegrensning i prosent</string> + <string name="frame_limit_slider_description">Angir prosentandelen som skal begrense emuleringshastigheten. Med standardverdien 100 % vil emuleringen være begrenset til normal hastighet. Høyere eller lavere verdier vil øke eller redusere hastighetsbegrensningen.</string> + <string name="cpu_accuracy">CPU-nøyaktighet</string> + + <!-- System settings strings --> + <string name="use_docked_mode">Dokket modus</string> + <string name="use_docked_mode_description">Emulerer i dokket modus, noe som øker oppløsningen på bekostning av ytelsen.</string> + <string name="emulated_region">Emulert region</string> + <string name="emulated_language">Emulert språk</string> + <string name="select_rtc_date">Velg RTC-dato</string> + <string name="select_rtc_time">Velg RTC-tid</string> + <string name="use_custom_rtc">Aktiver egendefinert RTC</string> + <string name="use_custom_rtc_description">Med denne innstillingen kan du stille inn en egendefinert sanntidsklokke som er atskilt fra gjeldende systemtid.</string> + <string name="set_custom_rtc">Angi egendefinert RTC</string> + + <!-- Graphics settings strings --> + <string name="renderer_api">API</string> + <string name="renderer_accuracy">Nøyaktighetsnivå</string> + <string name="renderer_resolution">Oppløsning</string> + <string name="renderer_vsync">VSync-modus</string> + <string name="renderer_aspect_ratio">Størrelsesforhold</string> + <string name="renderer_scaling_filter">Filter for vindustilpasning</string> + <string name="renderer_anti_aliasing">Anti-Aliasing-metode</string> + <string name="renderer_force_max_clock">Tving fram maksimal klokkefrekvens (kun Adreno)</string> + <string name="renderer_force_max_clock_description">Tvinger GPU-en til å kjøre med maksimal klokkefrekvens (termiske begrensninger vil fortsatt gjelde).</string> + <string name="renderer_asynchronous_shaders">Bruk asynkrone shaders</string> + <string name="renderer_asynchronous_shaders_description">Kompilerer shaders asynkront, noe som reduserer hakkingen, men kan føre til feil.</string> + <string name="renderer_debug">Aktiver feilsøking av grafikk</string> + <string name="renderer_debug_description">Når dette er merket av, går grafikk-API-et inn i en langsommere feilsøkingsmodus.</string> + <string name="use_disk_shader_cache">Bruk disk shader-cache</string> + <string name="use_disk_shader_cache_description">Reduser hakking ved å lagre og laste inn genererte shaders på disken.</string> + + <!-- Audio settings strings --> + <string name="audio_volume">Volum</string> + <string name="audio_volume_description">Angir volumet på lydutgangen.</string> + + <!-- Miscellaneous --> + <string name="slider_default">Standard</string> + <string name="ini_saved">Lagrede innstillinger</string> + <string name="gameid_saved">Lagrede innstillinger for %1$s</string> + <string name="error_saving">Feil ved lagring av %1$s.ini: %2$s</string> + <string name="loading">Lastes inn...</string> + <string name="reset_setting_confirmation">Vil du tilbakestille denne innstillingen til standardverdien?</string> + <string name="reset_to_default">Tilbakestill til standardinnstillingene</string> + <string name="reset_all_settings">Tilbakestille alle innstillinger?</string> + <string name="reset_all_settings_description">Alle avanserte innstillinger tilbakestilles til standardkonfigurasjonen. Dette kan ikke angres.</string> + <string name="settings_reset">Tilbakestilling av innstillinger</string> + <string name="close">Lukk</string> + <string name="learn_more">Lær Mer</string> + + <!-- GPU driver installation --> + <string name="select_gpu_driver">Velg GPU-driver</string> + <string name="select_gpu_driver_title">Ønsker du å bytte ut din nåværende GPU-driver?</string> + <string name="select_gpu_driver_install">Installer</string> + <string name="select_gpu_driver_default">Standard</string> + <string name="select_gpu_driver_install_success">Installert %s</string> + <string name="select_gpu_driver_use_default">Bruk av standard GPU-driver</string> + <string name="select_gpu_driver_error">Ugyldig driver valgt, bruker systemstandard!</string> + <string name="system_gpu_driver">Systemets GPU-driver</string> + <string name="installing_driver">Installerer driver...</string> + + <!-- Preferences Screen --> + <string name="preferences_settings">Innstillinger</string> + <string name="preferences_general">Generelt</string> + <string name="preferences_system">System</string> + <string name="preferences_graphics">Grafikk</string> + <string name="preferences_audio">Lyd</string> + <string name="preferences_theme">Tema og farge</string> + + <!-- ROM loading errors --> + <string name="loader_error_encrypted">ROM-en din er kryptert</string> + <string name="loader_error_encrypted_roms_description"><![CDATA[Følg veiledningene for å redumpe dine <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-cartridge-games\">spillkassetter</a> eller <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-installed-titles-eshop\">installerte titler</a>.]]></string> + <string name="loader_error_encrypted_keys_description"><![CDATA[Vennligst sørg for at <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> filen er installert slik at spillene kan dekrypteres.]]></string> + <string name="loader_error_video_core">Det oppstod en feil ved initialisering av videokjernen</string> + <string name="loader_error_video_core_description">Dette skyldes vanligvis en inkompatibel GPU-driver. Installering av en tilpasset GPU-driver kan løse problemet.</string> + <string name="loader_error_invalid_format">Kunne ikke laste inn ROM</string> + <string name="loader_error_file_not_found">ROM-filen finnes ikke</string> + + <!-- Emulation Menu --> + <string name="emulation_exit">Avslutt emulering</string> + <string name="emulation_done">Ferdig</string> + <string name="emulation_fps_counter">FPS-teller</string> + <string name="emulation_toggle_controls">Veksle kontroller</string> + <string name="emulation_rel_stick_center">Relativt senter for stikken</string> + <string name="emulation_dpad_slide">DPad-skyveplate</string> + <string name="emulation_haptics">Haptikk</string> + <string name="emulation_show_overlay">Vis overlegg</string> + <string name="emulation_toggle_all">Slå av alt</string> + <string name="emulation_control_adjust">Juster overlegg</string> + <string name="emulation_control_scale">Skaler</string> + <string name="emulation_control_opacity">Gjennomsiktighet</string> + <string name="emulation_touch_overlay_reset">Tilbakestill overlegg</string> + <string name="emulation_touch_overlay_edit">Rediger overlegg</string> + <string name="emulation_pause">Pause Emulering</string> + <string name="emulation_unpause">Opphev pausing av emulering</string> + <string name="emulation_input_overlay">Alternativer for overlegg</string> + <string name="emulation_game_loading">Spillet lastes inn...</string> + + <string name="load_settings">Laster inn innstillinger...</string> + + <!-- Software keyboard --> + <string name="software_keyboard">Programvare Tastatur</string> + + <!-- Errors and warnings --> + <string name="abort_button">Avbryt</string> + <string name="continue_button">Fortsett</string> + <string name="system_archive_not_found">System Arkiv Ikke Funnet</string> + <string name="system_archive_not_found_message">%s mangler. Dump systemarkivene dine.\nFortsatt emulering kan føre til krasj og feil.</string> + <string name="system_archive_general">Et systemarkiv</string> + <string name="save_load_error">Feil ved lagring/innlasting</string> + <string name="fatal_error">Fatal Feil</string> + <string name="fatal_error_message">Det oppstod en fatal feil. Sjekk loggen for mer informasjon.\nFortsatt emulering kan føre til krasj og feil.</string> + <string name="performance_warning">Hvis du slår av denne innstillingen, reduseres emuleringsytelsen betydelig! Vi anbefaler at du lar denne innstillingen være aktivert for å få den beste opplevelsen.</string> + + <!-- Region Names --> + <string name="region_japan">Japan</string> + <string name="region_usa">USA</string> + <string name="region_europe">Europa</string> + <string name="region_australia">Australia</string> + <string name="region_china">Kina</string> + <string name="region_korea">Korea</string> + <string name="region_taiwan">Taiwan</string> + + <!-- Language Names --> + <string name="language_japanese">Japansk (日本語)</string> + <string name="language_english">Engelsk</string> + <string name="language_french">Fransk (Français)</string> + <string name="langauge_german">Tysk (Deutsch)</string> + <string name="language_italian">Italiensk (Italiano)</string> + <string name="language_spanish">Spansk (Español)</string> + <string name="language_chinese">Kinesisk (简体中文)</string> + <string name="language_korean">Koreansk (한국어)</string> + <string name="language_dutch">Nederlandsk (Nederlands)</string> + <string name="language_portuguese">Portugisisk (Português)</string> + <string name="language_russian">Russisk (Русский)</string> + <string name="language_taiwanese">Taiwansk (台湾)</string> + <string name="language_british_english">Britisk Engelsk</string> + <string name="language_canadian_french">Kanadisk fransk (Français canadien)</string> + <string name="language_latin_american_spanish">Latinamerikansk spansk (Español latinoamericano)</string> + <string name="language_simplified_chinese">Forenklet kinesisk (简体中文)</string> + <string name="language_traditional_chinese">Tradisjonell Kinesisk (正體中文)</string> + <string name="language_brazilian_portuguese">Brasiliansk portugisisk (Português do Brasil)</string> + + <!-- Renderer APIs --> + <string name="renderer_vulkan">Vulkan</string> + <string name="renderer_none">Ingen</string> + + <!-- Renderer Accuracy --> + <string name="renderer_accuracy_normal">Normal</string> + <string name="renderer_accuracy_high">Høy</string> + <string name="renderer_accuracy_extreme">Ekstrem (Treg)</string> + + <!-- Resolutions --> + <string name="resolution_half">0.5X (360p/540p)</string> + <string name="resolution_three_quarter">0.75X (540p/810p)</string> + <string name="resolution_one">1X (720p/1080p)</string> + <string name="resolution_two">2X (1440p/2160p) (Slow)</string> + <string name="resolution_three">3X (2160p/3240p) (Slow)</string> + <string name="resolution_four">4X (2880p/4320p) (Slow)</string> + + <!-- Renderer VSync --> + <string name="renderer_vsync_immediate">Umiddelbar (av)</string> + <string name="renderer_vsync_mailbox">Postkasse</string> + <string name="renderer_vsync_fifo">FIFO (På)</string> + <string name="renderer_vsync_fifo_relaxed">FIFO avslappet</string> + + <!-- Scaling Filters --> + <string name="scaling_filter_nearest_neighbor">Nærmeste nabo</string> + <string name="scaling_filter_bilinear">Bilineær</string> + <string name="scaling_filter_bicubic">Bikubisk</string> + <string name="scaling_filter_gaussian">Gaussisk</string> + <string name="scaling_filter_scale_force">ScaleForce</string> + <string name="scaling_filter_fsr">AMD FidelityFX™ Super Resolution</string> + + <!-- Anti-Aliasing --> + <string name="anti_aliasing_none">Ingen</string> + <string name="anti_aliasing_fxaa">FXAA</string> + <string name="anti_aliasing_smaa">SMAA</string> + + <!-- Aspect Ratios --> + <string name="ratio_default">Standard (16:9)</string> + <string name="ratio_force_four_three">Tving 4:3</string> + <string name="ratio_force_twenty_one_nine">Tving 21:9</string> + <string name="ratio_force_sixteen_ten">Tving 16:10</string> + <string name="ratio_stretch">Strekk til Vindu</string> + + <!-- CPU Accuracy --> + <string name="cpu_accuracy_accurate">Nøyaktig</string> + <string name="cpu_accuracy_unsafe">Utrygt</string> + <string name="cpu_accuracy_paranoid">Paranoid (Langsom)</string> + + <!-- Gamepad Buttons --> + <string name="gamepad_d_pad">D-Pad</string> + <string name="gamepad_left_stick">Venstre Pinne</string> + <string name="gamepad_right_stick">Høyre Pinne</string> + <string name="gamepad_home">Hjem</string> + <string name="gamepad_screenshot">Skjermbilde</string> + + <!-- Disk shader cache --> + <string name="preparing_shaders">Forberedelse av shaders</string> + <string name="building_shaders">Bygging av shaders</string> + + <!-- Theme options --> + <string name="change_app_theme">Endre appens tema</string> + <string name="theme_default">Standard</string> + <string name="theme_material_you">Material You</string> + + <!-- Theme Modes --> + <string name="change_theme_mode">Endre temamodus</string> + <string name="theme_mode_follow_system">Følg systemet</string> + <string name="theme_mode_light">Lys</string> + <string name="theme_mode_dark">Mørk</string> + + <!-- Black backgrounds theme --> + <string name="use_black_backgrounds">Bruk svart bakgrunn</string> + <string name="use_black_backgrounds_description">Bruk svart bakgrunn når du bruker det mørke temaet.</string> + +</resources> diff --git a/src/android/app/src/main/res/values-pl/strings.xml b/src/android/app/src/main/res/values-pl/strings.xml new file mode 100644 index 0000000000..1cd1a8f879 --- /dev/null +++ b/src/android/app/src/main/res/values-pl/strings.xml @@ -0,0 +1,337 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <string name="app_disclaimer">To oprogramowanie umożliwia uruchomienie gier z konsoli Nintendo Switch. Nie zawiera gier ani wymaganych kluczy.<br /><br />Zanim zaczniesz, wybierz plik kluczy <![CDATA[<b> prod.keys </b>]]> z katalogu w pamięci masowej.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Dowiedz się więcej</a>]]></string> + <string name="emulation_notification_channel_name">Emulacja jest uruchomiona</string> + <string name="emulation_notification_channel_description">Pokaż trwałe powiadomienie gdy emulacja jest uruchomiona.</string> + <string name="emulation_notification_running">yuzu jest uruchomiony</string> + <string name="notice_notification_channel_name">Powiadomienia błędy</string> + <string name="notice_notification_channel_description">Pokaż powiadomienie gdy coś pójdzie źle</string> + <string name="notification_permission_not_granted">Nie zezwolono na powiadomienia!</string> + + <!-- Setup strings --> + <string name="welcome">Witaj!</string> + <string name="welcome_description">Zobacz jak skonfigurować <b>yuzu</b> i wskocz w świat emulacji.</string> + <string name="get_started">Zaczynamy</string> + <string name="keys">Klucze</string> + <string name="keys_description">Wybierz swoje klucze <b>prod.keys</b> za pomocą przycisku poniżej.</string> + <string name="select_keys">Wybierz klucze</string> + <string name="games">Gry</string> + <string name="games_description">Wybierz katalog z grami <b>Games</b> za pomocą przycisku poniżej.</string> + <string name="done">Gotowe</string> + <string name="done_description">Wszystko skonfigurowane.\n Miłego grania!</string> + <string name="text_continue">Kontynuuj</string> + <string name="next">Dalej</string> + <string name="back">Wstecz</string> + <string name="add_games">Dodaj gry</string> + <string name="add_games_description">Wybierz folder zawierający Twoje gry</string> + + <!-- Home strings --> + <string name="home_games">Gry</string> + <string name="home_search">Szukaj</string> + <string name="home_settings">Ustawienia</string> + <string name="empty_gamelist">Nie znaleziono plików, lub nie wybrano jeszcze katalogu zawierającego gry.</string> + <string name="search_and_filter_games">Szukaj i filtruj gry</string> + <string name="select_games_folder">Wybierz folder z grami</string> + <string name="select_games_folder_description">Pozwala yuzu wygenerować listę gier</string> + <string name="add_games_warning">Pominąć wybór folderu z grami?</string> + <string name="add_games_warning_description">Aby pokazać listę gier wybierz katalog zawierający gry.</string> + <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> + <string name="home_search_games">Szukaj gier</string> + <string name="games_dir_selected">Wybrano katalog gier</string> + <string name="install_prod_keys">Instaluj klucze prod.keys</string> + <string name="install_prod_keys_description">Wymagane aby poprawnie odczytać sklepowe gry</string> + <string name="install_prod_keys_warning">Pominąć dodawanie kluczy?</string> + <string name="install_prod_keys_warning_description">Poprawne klucze są wymagane aby emulować sklepowe gry. Jeśli przejdziesz dalej, jedynie homebrew będą działać.</string> + <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string> + <string name="notifications">Powiadomienia</string> + <string name="notifications_description">Nadaj uprawnienia dostępu do powiadomień. </string> + <string name="give_permission">Nadaj uprawnienia</string> + <string name="notification_warning">Pominąć nadanie uprawnień powiadomień?</string> + <string name="notification_warning_description">yuzu nie będzie mógł powiadamiać Cię o ważnych informacjach.</string> + <string name="permission_denied">Odmowa dostępu</string> + <string name="permission_denied_description">Odmówiłeś dostępu do powiadomień zbyt wiele razy, teraz musisz przyznać je w ustawieniach systemowych Androida.</string> + <string name="about">O aplikacji</string> + <string name="about_description">Wersja, podziękowania i więcej</string> + <string name="warning_help">Pomoc</string> + <string name="warning_skip">Pomiń</string> + <string name="warning_cancel">Anuluj</string> + <string name="install_amiibo_keys">Zainstaluj klucze Amiibo</string> + <string name="install_amiibo_keys_description">Wymagane aby korzystać z Amiibo w grze</string> + <string name="invalid_keys_file">Wybrano niepoprawne klucze</string> + <string name="install_keys_success">Klucze zainstalowane pomyślnie</string> + <string name="reading_keys_failure">Błąd podczas odczytu kluczy</string> + <string name="invalid_keys_error">Niepoprawne klucze</string> + <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> + <string name="install_keys_failure_description">Wybrany plik jest niepoprawny lub uszkodzony. Zrzuć ponownie swoje klucze.</string> + <string name="install_gpu_driver">Zainstaluj sterownik GPU</string> + <string name="install_gpu_driver_description">Użyj alternatywnych sterowników aby potencjalnie zwiększyć wydajność i naprawić błędy</string> + <string name="advanced_settings">Ustawienia zaawansowane</string> + <string name="settings_description">Skonfiguruj ustawienia emulatora</string> + <string name="search_recently_played">Ostatnio grane</string> + <string name="search_recently_added">Ostatnio dodane</string> + <string name="search_retail">Sklepowe</string> + <string name="search_homebrew">Homebrew</string> + <string name="open_user_folder">Otwórz folder yuzu</string> + <string name="open_user_folder_description">Zarządzaj plikami emulatora</string> + <string name="theme_and_color_description">Personalizuj wygląd aplikacji</string> + <string name="no_file_manager">Nie znaleziono menedżera plików</string> + <string name="notification_no_directory_link">Nie można otworzyć folderu emulatora</string> + <string name="notification_no_directory_link_description">Proszę wybrać ręcznie folder z pomocą panelu bocznego menedżera plików.</string> + <string name="manage_save_data">Zarządzaj plikami zapisów gier</string> + <string name="manage_save_data_description">Znaleziono pliki zapisów gier. Wybierz opcję poniżej.</string> + <string name="import_export_saves_description">Importuj lub wyeksportuj pliki zapisów</string> + <string name="import_export_saves_no_profile">Nie znaleziono plików zapisów. Uruchom grę i spróbuj ponownie.</string> + <string name="save_file_imported_success">Zaimportowano pomyślnie</string> + <string name="save_file_invalid_zip_structure">Niepoprawna struktura folderów</string> + <string name="save_file_invalid_zip_structure_description">Pierwszy podkatalog musi zawierać w nazwie numer ID tytułu gry.</string> + <string name="import_saves">Importuj</string> + <string name="export_saves">Eksportuj</string> + + <!-- About screen strings --> + <string name="gaia_is_not_real">Gaia isn\'t real</string> + <string name="copied_to_clipboard">Skopiowano do schowka</string> + <string name="about_app_description">Otwarto-źródłowy emulator konsoli Switch</string> + <string name="contributors">Współtwórcy</string> + <string name="contributors_description">Stworzone z \u2764 przez zespół yuzu</string> + <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> + <string name="build">Wersja</string> + <string name="support_link">https://discord.gg/u77vRWY</string> + <string name="website_link">https://yuzu-emu.org/</string> + <string name="github_link">https://github.com/yuzu-emu</string> + + <!-- Early access upgrade strings --> + <string name="early_access">Wczesny dostęp</string> + <string name="get_early_access">Uzyskaj wczesny dostęp</string> + <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string> + <string name="get_early_access_description">Nowe funkcje, szybszy dostęp do aktualizacji i nie tylko</string> + <string name="early_access_benefits">Korzyści z wcześniejszego dostępu</string> + <string name="cutting_edge_features">Nowatorskie funkcje</string> + <string name="early_access_updates">Częste aktualizacje</string> + <string name="no_manual_installation">Automatyczne aktualizacje</string> + <string name="prioritized_support">Priorytetowe wsparcie</string> + <string name="helping_game_preservation">Pomoc w problemach z grami</string> + <string name="our_eternal_gratitude">Nasza wdzięczność</string> + <string name="are_you_interested">Jesteś zainteresowany?</string> + + <!-- General settings strings --> + <string name="frame_limit_enable">Włącz limit szybkości emulacji</string> + <string name="frame_limit_enable_description">Włącz, aby ustawić procentowy limit szybkości emulacji</string> + <string name="frame_limit_slider">Procentowy limit szybkości emulacji</string> + <string name="frame_limit_slider_description">Określa limit szybkości emulacji gier. Domyślna wartość 100% oznacza normalną szybkość z jaką działa gra. Wartości niższe lub wyższe zmniejszą lub zwiększą limit szybkości.</string> + <string name="cpu_accuracy">Dokładność procesora CPU</string> + + <!-- System settings strings --> + <string name="use_docked_mode">Tryb zadokowany</string> + <string name="use_docked_mode_description">Emulacja w trybie stacji dokującej, zwiększa rozdzielczość kosztem wydajności.</string> + <string name="emulated_region">Region emulacji</string> + <string name="emulated_language">Język emulacji</string> + <string name="select_rtc_date">Ustaw datę RTC</string> + <string name="select_rtc_time">Ustaw czas RTC</string> + <string name="use_custom_rtc">Włącz niestandardowy zegar RTC</string> + <string name="use_custom_rtc_description">Ta opcja pozwala na wybranie własnych ustawień czasu używanych w czasie emulacji, innych niż czas systemu Android.</string> + <string name="set_custom_rtc">Ustaw niestandardowy czas RTC</string> + + <!-- Graphics settings strings --> + <string name="renderer_api">Interfejs graficzny</string> + <string name="renderer_accuracy">Poziom precyzji emulacji</string> + <string name="renderer_resolution">Rozdzielczość</string> + <string name="renderer_vsync">Synchronizacja pionowa VSync</string> + <string name="renderer_aspect_ratio">Proporcje ekranu</string> + <string name="renderer_scaling_filter">Filtr adaptacji rozdzielczości</string> + <string name="renderer_anti_aliasing">Metoda wygładzania krawędzi</string> + <string name="renderer_force_max_clock">Maksymalne taktowanie GPU (układy Adreno)</string> + <string name="renderer_force_max_clock_description">Wymusza uruchomienie maksymalnego taktowania układu graficznego (zabezpieczenia termiczne będą dalej aktywne).</string> + <string name="renderer_asynchronous_shaders">Wyłącz synchronizację shaderów</string> + <string name="renderer_asynchronous_shaders_description">Kompiluj oświetlenie bez synchronizacji, poprawi wydajność ale może powodować błędy.</string> + <string name="renderer_debug">Włącz debugowanie grafiki</string> + <string name="renderer_debug_description">Kiedy włączone, interfejs graficzny korzysta z wolnego trybu debugowania błędów.</string> + <string name="use_disk_shader_cache">Użyj pamięci podręcznej shaderów na dysku</string> + <string name="use_disk_shader_cache_description">Zmniejsza przycięcia przez przechowywanie gotowych wygenerowanych plików oświetlenia w pamięci urządzenia.</string> + + <!-- Audio settings strings --> + <string name="audio_volume">Głośność</string> + <string name="audio_volume_description">Ustala poziom głośności wyjścia dźwięku.</string> + + <!-- Miscellaneous --> + <string name="slider_default">Domyślne</string> + <string name="ini_saved">Ustawienia zapisane</string> + <string name="gameid_saved">Ustawienia zapisane w %1$s</string> + <string name="error_saving">Błąd zapisu %1$s.ini: %2$s</string> + <string name="loading">Wczytywanie...</string> + <string name="reset_setting_confirmation">Przywrócić wartość tego ustawienia do wartości domyślnej?</string> + <string name="reset_to_default">Przywróć ustawienia domyślne</string> + <string name="reset_all_settings">Przywrócić WSZYSTKIE ustawienia?</string> + <string name="reset_all_settings_description">Wszystkie zaawansowane opcje zostaną przywrócone do wartości domyślnych. Czynności nie będzie można cofnąć.</string> + <string name="settings_reset">Reset ustawień</string> + <string name="close">Zamknij</string> + <string name="learn_more">Dowiedz się więcej</string> + + <!-- GPU driver installation --> + <string name="select_gpu_driver">Wybierz sterownik GPU </string> + <string name="select_gpu_driver_title">Chcesz zastąpić obecny sterownik układu graficznego?</string> + <string name="select_gpu_driver_install">Zainstaluj</string> + <string name="select_gpu_driver_default">Domyślne</string> + <string name="select_gpu_driver_install_success">Zainstalowano %s</string> + <string name="select_gpu_driver_use_default">Aktywny domyślny sterownik GPU</string> + <string name="select_gpu_driver_error">Wybrano błędny sterownik, powrót do domyślnego. </string> + <string name="system_gpu_driver">Systemowy sterownik GPU</string> + <string name="installing_driver">Instalowanie sterownika...</string> + + <!-- Preferences Screen --> + <string name="preferences_settings">Ustawienia</string> + <string name="preferences_general">Ogólne</string> + <string name="preferences_system">System</string> + <string name="preferences_graphics">Grafika</string> + <string name="preferences_audio">Dźwięk</string> + <string name="preferences_theme">Motyw i kolor</string> + + <!-- ROM loading errors --> + <string name="loader_error_encrypted">Twój ROM jest zakodowany</string> + <string name="loader_error_encrypted_roms_description"><![CDATA[Użyj przewodnika aby wykonać zrzuty <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-cartridge-games\">kardridży</a> lub <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-installed-titles-eshop\">zainstalowanych gier</a>.]]></string> + <string name="loader_error_encrypted_keys_description"><![CDATA[Upewnij się że plik kluczy <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> jest zainstalowany aby gry mogły zostać odczytane.]]></string> + <string name="loader_error_video_core">Błąd inicjacji podsystemu graficznego</string> + <string name="loader_error_video_core_description">Zazwyczaj spowodowane niekompatybilnym sterownikiem GPU, instalacja niestandardowego sterownika może rozwiązać ten problem.</string> + <string name="loader_error_invalid_format">Nie można wczytać pliku ROM</string> + <string name="loader_error_file_not_found">Plik ROM nie istnieje</string> + + <!-- Emulation Menu --> + <string name="emulation_exit">Zakończ emulację</string> + <string name="emulation_done">Gotowe</string> + <string name="emulation_fps_counter">Licznik FPS</string> + <string name="emulation_toggle_controls">Wybierz przyciski</string> + <string name="emulation_rel_stick_center">Wycentruj gałki</string> + <string name="emulation_dpad_slide">Ruchomy DPad</string> + <string name="emulation_haptics">Wibracje haptyczne</string> + <string name="emulation_show_overlay">Pokaż przyciski</string> + <string name="emulation_toggle_all">Zaznacz wszystkie</string> + <string name="emulation_control_adjust">Dostosuj nakładkę</string> + <string name="emulation_control_scale">Skala</string> + <string name="emulation_control_opacity">Przeźroczystość</string> + <string name="emulation_touch_overlay_reset">Resetuj</string> + <string name="emulation_touch_overlay_edit">Edytuj nakładkę</string> + <string name="emulation_pause">Wstrzymaj emulację</string> + <string name="emulation_unpause">Wznów emulację</string> + <string name="emulation_input_overlay">Opcje nakładki</string> + <string name="emulation_game_loading">Wczytywanie gry...</string> + + <string name="load_settings">Wczytywanie ustawień...</string> + + <!-- Software keyboard --> + <string name="software_keyboard">Klawiatura systemowa</string> + + <!-- Errors and warnings --> + <string name="abort_button">Przerwij</string> + <string name="continue_button">Kontynuuj</string> + <string name="system_archive_not_found">Archiwum systemu nie znalezione.</string> + <string name="system_archive_not_found_message">%s nieznaleziony. Proszę wykonać zrzut archiwum systemu.\nKontynuowanie może powodować błędy lub przerwanie emulacji.</string> + <string name="system_archive_general">Archiwum systemu</string> + <string name="save_load_error">Błąd odczytu/zapisu</string> + <string name="fatal_error">Błąd krytyczny</string> + <string name="fatal_error_message">Wystąpił błąd krytyczny. Szczegóły znajdziesz w pliku log.\nKontynuowanie może spowodować błędy lub przerwanie emulacji. </string> + <string name="performance_warning">Wyłączenie tej opcji znacząco ograniczy wydajność! Dla najlepszego doświadczenia, zaleca się zostawienie tej opcji włączonej.</string> + + <!-- Region Names --> + <string name="region_japan">Japonia</string> + <string name="region_usa">USA</string> + <string name="region_europe">Europa</string> + <string name="region_australia">Australia</string> + <string name="region_china">Chiny</string> + <string name="region_korea">Korea</string> + <string name="region_taiwan">Tajwan</string> + + <!-- Language Names --> + <string name="language_japanese">Japoński (日本語)</string> + <string name="language_english">Angielski</string> + <string name="language_french">Francuski (Francja)</string> + <string name="langauge_german">Niemiecki (Niemcy)</string> + <string name="language_italian">Włoski (Włochy)</string> + <string name="language_spanish">Hiszpański (Hiszpania)</string> + <string name="language_chinese">Chiński (简体中文)</string> + <string name="language_korean">Koreański (한국어)</string> + <string name="language_dutch">Duński (Holandia)</string> + <string name="language_portuguese">Portugalski (Portugalia)</string> + <string name="language_russian">Rosyjski (Русский)</string> + <string name="language_taiwanese">Tajwański (台湾)</string> + <string name="language_british_english">Angielski Brytyjski</string> + <string name="language_canadian_french">Francuski (Kanada)</string> + <string name="language_latin_american_spanish">Hiszpański (Ameryka Latynoska)</string> + <string name="language_simplified_chinese">Chiński uproszczony (简体中文)</string> + <string name="language_traditional_chinese">Chiński tradycyjny (正體中文)</string> + <string name="language_brazilian_portuguese">Portugalski (Brazylia)</string> + + <!-- Renderer APIs --> + <string name="renderer_vulkan">Vulkan</string> + <string name="renderer_none">Żadny</string> + + <!-- Renderer Accuracy --> + <string name="renderer_accuracy_normal">Normalny</string> + <string name="renderer_accuracy_high">Wysoki</string> + <string name="renderer_accuracy_extreme">Ekstremalny (Wolny)</string> + + <!-- Resolutions --> + <string name="resolution_half">0.5X (360p/540p)</string> + <string name="resolution_three_quarter">0.75X (540p/810p)</string> + <string name="resolution_one">1X (720p/1080p)</string> + <string name="resolution_two">2X (1440p/2160p) (Wolno)</string> + <string name="resolution_three">3X (2160p/3240p) (Wolno)</string> + <string name="resolution_four">4X (2880p/4320p) (Wolno)</string> + + <!-- Renderer VSync --> + <string name="renderer_vsync_immediate">Natychmiastowa (Wyłączona)</string> + <string name="renderer_vsync_mailbox">Skrzynka pocztowa</string> + <string name="renderer_vsync_fifo">FIFO (Włączona)</string> + <string name="renderer_vsync_fifo_relaxed">FIFO Relaks</string> + + <!-- Scaling Filters --> + <string name="scaling_filter_nearest_neighbor">Najbliższy sąsiadujący</string> + <string name="scaling_filter_bilinear">Bilinearny</string> + <string name="scaling_filter_bicubic">Bikubiczny</string> + <string name="scaling_filter_gaussian">Kulisty</string> + <string name="scaling_filter_scale_force">ScaleForce</string> + <string name="scaling_filter_fsr">AMD FidelityFX™ Super Resolution</string> + + <!-- Anti-Aliasing --> + <string name="anti_aliasing_none">Żadna (wyłączony)</string> + <string name="anti_aliasing_fxaa">FXAA</string> + <string name="anti_aliasing_smaa">SMAA</string> + + <!-- Aspect Ratios --> + <string name="ratio_default">Domyślne (16:9)</string> + <string name="ratio_force_four_three">Wymuś 4:3</string> + <string name="ratio_force_twenty_one_nine">Wymuś 21:9</string> + <string name="ratio_force_sixteen_ten">Wymuś 16:10</string> + <string name="ratio_stretch">Rozciągnij do Okna</string> + + <!-- CPU Accuracy --> + <string name="cpu_accuracy_accurate">Dokładny</string> + <string name="cpu_accuracy_unsafe">Niebezpieczny</string> + <string name="cpu_accuracy_paranoid">Paranoid (Wolny)</string> + + <!-- Gamepad Buttons --> + <string name="gamepad_d_pad">D-Pad</string> + <string name="gamepad_left_stick">Lewa gałka</string> + <string name="gamepad_right_stick">Prawa gałka</string> + <string name="gamepad_home">Home</string> + <string name="gamepad_screenshot">Zrzut ekranu</string> + + <!-- Disk shader cache --> + <string name="preparing_shaders">Przygotowanie shaderów</string> + <string name="building_shaders">Budowanie shaderów</string> + + <!-- Theme options --> + <string name="change_app_theme">Zmień motyw aplikacji</string> + <string name="theme_default">Domyślny</string> + <string name="theme_material_you">Material You</string> + + <!-- Theme Modes --> + <string name="change_theme_mode">Zmiana trybu motywu</string> + <string name="theme_mode_follow_system">Podążaj za systemowym</string> + <string name="theme_mode_light">Jasny</string> + <string name="theme_mode_dark">Ciemny</string> + + <!-- Black backgrounds theme --> + <string name="use_black_backgrounds">Używaj czarnego tła</string> + <string name="use_black_backgrounds_description">Kiedy używany ciemny motyw, tła zostają zastąpione czernią.</string> + +</resources> diff --git a/src/android/app/src/main/res/values-pt-rBR/strings.xml b/src/android/app/src/main/res/values-pt-rBR/strings.xml new file mode 100644 index 0000000000..35197c2809 --- /dev/null +++ b/src/android/app/src/main/res/values-pt-rBR/strings.xml @@ -0,0 +1,337 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <string name="app_disclaimer">Este software corre jogos para a consola Nintendo Switch. Não estão incluídas nem jogos ou chaves. <br /><br />Antes de começares, por favor localiza o ficheiro <![CDATA[1 prod.keys 1]]> no armazenamento do teu dispositivo.<br /><br /><![CDATA[2Learn more2]]></string> + <string name="emulation_notification_channel_name">Emulação está Ativa</string> + <string name="emulation_notification_channel_description">Mostra uma notificação permanente enquanto a emulação está a correr.</string> + <string name="emulation_notification_running">Yuzu está em execução </string> + <string name="notice_notification_channel_name">Notificações e erros</string> + <string name="notice_notification_channel_description">Mostra notificações quendo algo corre mal.</string> + <string name="notification_permission_not_granted">Permissões de notificação não permitidas </string> + + <!-- Setup strings --> + <string name="welcome">Bemvindo! </string> + <string name="welcome_description">Aprende como configurar <b>yuzu</b> e arranca a emulação.</string> + <string name="get_started">Começa</string> + <string name="keys">Chaves</string> + <string name="keys_description">Seleciona o teu ficheiro <b>prod.keys</b> com o botão abaixo.</string> + <string name="select_keys">Seleciona as Chaves</string> + <string name="games">Jogos</string> + <string name="games_description">Seleciona a tua pasta <b>Games</b> com o botão abaixo.</string> + <string name="done">Feito</string> + <string name="done_description">Tudo pronto.\nDisfruta dos teus jogos!</string> + <string name="text_continue">Continuar</string> + <string name="next">Próximo</string> + <string name="back">Voltar</string> + <string name="add_games">Adiciona Jogos</string> + <string name="add_games_description">Seleciona a tua pasta de Jogos</string> + + <!-- Home strings --> + <string name="home_games">Jogos</string> + <string name="home_search">Pesquisar</string> + <string name="home_settings">Configurações</string> + <string name="empty_gamelist">Não foram encontrados jogos ou a pasta de Jogos ainda não foi definida. </string> + <string name="search_and_filter_games">Procura e filtra jogos.</string> + <string name="select_games_folder">Seleciona a pasta de jogos.</string> + <string name="select_games_folder_description">Permite que o Yuzu preencha a lista de jogos</string> + <string name="add_games_warning">Ignorar a seleção da pasta de jogos?</string> + <string name="add_games_warning_description">Os jogos não serão exibidos na lista de jogos se uma pasta não estiver selecionada.</string> + <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> + <string name="home_search_games">Procurar Jogos</string> + <string name="games_dir_selected">Pasta de Jogos selecionada</string> + <string name="install_prod_keys">Instala prod.keys</string> + <string name="install_prod_keys_description">Necessário para desencriptar jogos comerciais</string> + <string name="install_prod_keys_warning">Ignorar a adição de chaves?</string> + <string name="install_prod_keys_warning_description">São necessárias chaves válidas para emular jogos comerciais. Somente aplicativos homebrew funcionarão se você continuar.</string> + <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#Guia de introdução</string> + <string name="notifications">Notificações</string> + <string name="notifications_description">Conceda a permissão de notificação com o botão abaixo.</string> + <string name="give_permission">Conceda permissão</string> + <string name="notification_warning">Saltar a concessão da permissão de notificação?</string> + <string name="notification_warning_description">Yuzu não conseguirá te notificar de informações importantes. </string> + <string name="permission_denied">Permissão negada</string> + <string name="permission_denied_description">Você negou essa permissão muitas vezes e agora precisa concedê-la manualmente nas configurações do sistema.</string> + <string name="about">Sobre</string> + <string name="about_description">Versão de compilação, créditos e mais</string> + <string name="warning_help">Ajuda</string> + <string name="warning_skip">Saltar</string> + <string name="warning_cancel">Cancelar</string> + <string name="install_amiibo_keys">Instala chaves Amiibo</string> + <string name="install_amiibo_keys_description">Necessário para usares Amiibo no jogo</string> + <string name="invalid_keys_file">Ficheiro de chaves inválido</string> + <string name="install_keys_success">Chaves instaladas com sucesso</string> + <string name="reading_keys_failure">Erro ao ler chaves de encriptação</string> + <string name="invalid_keys_error">Chaves de encriptação inválidas</string> + <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> + <string name="install_keys_failure_description">O ficheiro selecionado está corrompido. Por favor recarrega as tuas chaves.</string> + <string name="install_gpu_driver">Instala driver para GPU</string> + <string name="install_gpu_driver_description">Instala drivers alternativos para desempenho ou precisão potencialmente melhores</string> + <string name="advanced_settings">Definições avançadas</string> + <string name="settings_description">Configura definições do emulador</string> + <string name="search_recently_played">Jogos recentes</string> + <string name="search_recently_added">Adicionados recentemente</string> + <string name="search_retail">Jogos comerciais</string> + <string name="search_homebrew">Homebrew</string> + <string name="open_user_folder">Abre a pasta Yuzu</string> + <string name="open_user_folder_description">Gere os ficheiro internos do Yuzu</string> + <string name="theme_and_color_description">Modifica a aparência da App</string> + <string name="no_file_manager">Nenhum gestor de ficheiros encontrado</string> + <string name="notification_no_directory_link">Impossível abrir pasta Yuzu</string> + <string name="notification_no_directory_link_description">Localiza a pasta de utilizador manualmente com o painel lateral do gestor de ficheiros.</string> + <string name="manage_save_data">Gerir dados guardados</string> + <string name="manage_save_data_description">Dados não encontrados. Por favor seleciona uma opção abaixo.</string> + <string name="import_export_saves_description">Importa ou exporta dados guardados</string> + <string name="import_export_saves_no_profile">Dados não encontrados. Por favor lança o jogo e tenta novamente.</string> + <string name="save_file_imported_success">Importado com sucesso</string> + <string name="save_file_invalid_zip_structure">Estrutura de diretório de dados invalida</string> + <string name="save_file_invalid_zip_structure_description">O nome da primeira sub pasta tem de ser a ID do jogo.</string> + <string name="import_saves">Importar</string> + <string name="export_saves">Exportar</string> + + <!-- About screen strings --> + <string name="gaia_is_not_real">Gaia não é real</string> + <string name="copied_to_clipboard">Copiado para a área de transferência</string> + <string name="about_app_description">Um emulador Switch de código aberto</string> + <string name="contributors">Contribuidores</string> + <string name="contributors_description">Feito com \u2764 da equipa do Yuzu</string> + <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> + <string name="build">Versão</string> + <string name="support_link">https://discord.gg/u77vRWY</string> + <string name="website_link">https://yuzu-emu.org/</string> + <string name="github_link">https://github.com/yuzu-emu</string> + + <!-- Early access upgrade strings --> + <string name="early_access">Acesso antecipado</string> + <string name="get_early_access">Obtém Acesso Antecipado</string> + <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string> + <string name="get_early_access_description">Recursos de ponta, acesso antecipado a atualizações e muito mais</string> + <string name="early_access_benefits">Benefícios do Acesso Antecipado</string> + <string name="cutting_edge_features">Recursos de ponta</string> + <string name="early_access_updates">Acesso antecipado a atualizações</string> + <string name="no_manual_installation">Sem instalação manual</string> + <string name="prioritized_support">Suporte prioritário</string> + <string name="helping_game_preservation">Ajuda na preservação dos jogos</string> + <string name="our_eternal_gratitude">A nossa eterna gratidão</string> + <string name="are_you_interested">Estás interessado?</string> + + <!-- General settings strings --> + <string name="frame_limit_enable">Ativar limite de velocidade</string> + <string name="frame_limit_enable_description">Quando ativada, a velocidade da emulação será limitada à percentagem definida da velocidade normal.</string> + <string name="frame_limit_slider">Percentagem do limite de velocidade</string> + <string name="frame_limit_slider_description">Especifica o limite da percentagem da velocidade da emulação. Com a velocidade por defeito a 100% a emulação será limitada à velocidade normal. Valores maiores ou menores aumentarão ou diminuirão o limite de velocidade.</string> + <string name="cpu_accuracy">Precisão do CPU</string> + + <!-- System settings strings --> + <string name="use_docked_mode">Modo ancorado</string> + <string name="use_docked_mode_description">Emula em modo ancorado, que aumenta a resolução ás custas da performance.</string> + <string name="emulated_region">Região da emulação</string> + <string name="emulated_language">Idioma da emulação</string> + <string name="select_rtc_date">Seleciona a data RTC</string> + <string name="select_rtc_time">Seleciona a hora RTC</string> + <string name="use_custom_rtc">Ativa RTC personalizado</string> + <string name="use_custom_rtc_description">Esta configuração permite definir um RTC personalizado diferente da hora atual do sistema</string> + <string name="set_custom_rtc">Define RTC personalizado</string> + + <!-- Graphics settings strings --> + <string name="renderer_api">API</string> + <string name="renderer_accuracy">Nível de precisão</string> + <string name="renderer_resolution">Resolução</string> + <string name="renderer_vsync">Modo VSync</string> + <string name="renderer_aspect_ratio">Proporção do ecrã</string> + <string name="renderer_scaling_filter">Filtro de Adaptação da Janela</string> + <string name="renderer_anti_aliasing">Método de Anti-Aliasing </string> + <string name="renderer_force_max_clock">Força velocidade máxima (Adreno only)</string> + <string name="renderer_force_max_clock_description">Força o GPU a correr à velocidade máxima (restrições térmicas serão aplicadas)</string> + <string name="renderer_asynchronous_shaders">Usa shaders assíncronos </string> + <string name="renderer_asynchronous_shaders_description">Compila shaders assincronamente, que aumentará a fluidez, mas poderá causar falhas.</string> + <string name="renderer_debug">Ativar depuração de gráficos</string> + <string name="renderer_debug_description">Quando selecionado, a API gráfica entra num modo de depuração mais lento.</string> + <string name="use_disk_shader_cache">Usar cache de shaders em disco</string> + <string name="use_disk_shader_cache_description">Aumenta a fluidez ao guardar e carregar shaders gerados para o armazenamento.</string> + + <!-- Audio settings strings --> + <string name="audio_volume">Volume</string> + <string name="audio_volume_description">Especifica o volume de saída.</string> + + <!-- Miscellaneous --> + <string name="slider_default">Padrão</string> + <string name="ini_saved">Definições guardadas</string> + <string name="gameid_saved">Definições guardadas para %1$s</string> + <string name="error_saving">Erro ao guardar %1$s.ini: %2$s</string> + <string name="loading">A carregar...</string> + <string name="reset_setting_confirmation">Queres reverter esta definição para os valores padrão?</string> + <string name="reset_to_default">Reverter para padrão</string> + <string name="reset_all_settings">Redefinir todas as definições?</string> + <string name="reset_all_settings_description">Todas as definições avançadas serão redefinidas para as definições padrão. Isto não pode ser revertido.</string> + <string name="settings_reset">Redefinir definições</string> + <string name="close">Fechar</string> + <string name="learn_more">Saiba mais</string> + + <!-- GPU driver installation --> + <string name="select_gpu_driver">Seleciona a driver para o GPU</string> + <string name="select_gpu_driver_title">Queres substituir o driver do GPU atual? </string> + <string name="select_gpu_driver_install">Instalar</string> + <string name="select_gpu_driver_default">Padrão</string> + <string name="select_gpu_driver_install_success">Instalado%s</string> + <string name="select_gpu_driver_use_default">Usar o driver padrão do GPU</string> + <string name="select_gpu_driver_error">Driver selecionado inválido, a usar o padrão do sistema!</string> + <string name="system_gpu_driver">Driver do GPU padrão</string> + <string name="installing_driver">A instalar o Driver...</string> + + <!-- Preferences Screen --> + <string name="preferences_settings">Configurações</string> + <string name="preferences_general">Geral</string> + <string name="preferences_system">Sistema</string> + <string name="preferences_graphics">Gráficos</string> + <string name="preferences_audio">Áudio</string> + <string name="preferences_theme">Cor e tema.</string> + + <!-- ROM loading errors --> + <string name="loader_error_encrypted">A tua ROM está encriptada</string> + <string name="loader_error_encrypted_roms_description"><![CDATA[Por favor segue os guias para fazer redump das tuas<a href=\"https://yuzu-emu.org/help/quickstart/#dumping-cartridge-games\">Cartidges de Jogo</a> or <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-installed-titles-eshop\">Jogos Instalados</a>.]]></string> + <string name="loader_error_encrypted_keys_description"><![CDATA[Por favor confirma que o teu ficheiro <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> está instalado para que os jogos possam ser desencriptados.]]></string> + <string name="loader_error_video_core">Ocorreu um erro ao iniciar o núcleo de vídeo.</string> + <string name="loader_error_video_core_description">Isto é normalmente causado por um driver de GPU incompatível. Instalar um driver GPU pode resolver este problema.</string> + <string name="loader_error_invalid_format">Impossível carregar a tua ROM</string> + <string name="loader_error_file_not_found">O ficheiro da ROM não existe</string> + + <!-- Emulation Menu --> + <string name="emulation_exit">Sair da emulação</string> + <string name="emulation_done">Feito</string> + <string name="emulation_fps_counter">Contador de FPS</string> + <string name="emulation_toggle_controls">Alterar Controlos</string> + <string name="emulation_rel_stick_center">Centro do Analógico Relativo</string> + <string name="emulation_dpad_slide">Deslizar do DPad</string> + <string name="emulation_haptics">Hápticos </string> + <string name="emulation_show_overlay">Mostrar sobreposição </string> + <string name="emulation_toggle_all">Alterar todos</string> + <string name="emulation_control_adjust">Ajustar a sobreposição </string> + <string name="emulation_control_scale">Escala</string> + <string name="emulation_control_opacity">Opacidade</string> + <string name="emulation_touch_overlay_reset">Redefinir Sobreposição </string> + <string name="emulation_touch_overlay_edit">Editar sobreposição </string> + <string name="emulation_pause">Pausa emulação</string> + <string name="emulation_unpause">Retomar emulação</string> + <string name="emulation_input_overlay">Opções de sobreposição </string> + <string name="emulation_game_loading">Jogo a carregar...</string> + + <string name="load_settings">Configurações a carregar...</string> + + <!-- Software keyboard --> + <string name="software_keyboard">Teclado de software</string> + + <!-- Errors and warnings --> + <string name="abort_button">Abortar</string> + <string name="continue_button">Continuar</string> + <string name="system_archive_not_found">Arquivo do sistema não encontrado</string> + <string name="system_archive_not_found_message">%s está em falta. Por favor apaga os teus ficheiros de sistema.\nContinuar a emulação pode causar erros.</string> + <string name="system_archive_general">Um arquivo do sistema</string> + <string name="save_load_error">Erro Guardar/Carregar</string> + <string name="fatal_error">Erro fatal</string> + <string name="fatal_error_message">Ocorreu um erro fatal. Verifica o teu registro para detalhes. \nContinuar a emulação pode causar erros.</string> + <string name="performance_warning">Desligar esta configuração irá reduzir a performance da emulação significantemente! Para a melhor experiência é recomendado que deixes esta configuração ativada.</string> + + <!-- Region Names --> + <string name="region_japan">Japão</string> + <string name="region_usa">EUA</string> + <string name="region_europe">Europa</string> + <string name="region_australia">Austrália</string> + <string name="region_china">China</string> + <string name="region_korea">Coréia</string> + <string name="region_taiwan">Taiwan</string> + + <!-- Language Names --> + <string name="language_japanese">Japônes (日本語)</string> + <string name="language_english">Português do Brasil</string> + <string name="language_french">Francês (Français)</string> + <string name="langauge_german">Alemão (Deutsch)</string> + <string name="language_italian">Italiano (Italiano)</string> + <string name="language_spanish">Espanhol (Español)</string> + <string name="language_chinese">Mandarim (简体中文)</string> + <string name="language_korean">Coreano (한국어)</string> + <string name="language_dutch">Holandês (Nederlands)</string> + <string name="language_portuguese">Português (Português)</string> + <string name="language_russian">Russo (Русский)</string> + <string name="language_taiwanese">Taiwanês (台湾)</string> + <string name="language_british_english">Inglês britânico (British English)</string> + <string name="language_canadian_french">Fracês Canadiano (Français canadien)</string> + <string name="language_latin_american_spanish">Espanhol da América Latina (Español latino-americano)</string> + <string name="language_simplified_chinese">Chinês Simplificado (简体中文)</string> + <string name="language_traditional_chinese">Chinês tradicional (正體中文)</string> + <string name="language_brazilian_portuguese">Português do Brasil (Português do Brasil)</string> + + <!-- Renderer APIs --> + <string name="renderer_vulkan">Vulcano</string> + <string name="renderer_none">Nenhum</string> + + <!-- Renderer Accuracy --> + <string name="renderer_accuracy_normal">Normal</string> + <string name="renderer_accuracy_high">Alto</string> + <string name="renderer_accuracy_extreme">Estremo (Lento)</string> + + <!-- Resolutions --> + <string name="resolution_half">0.5X (360p/540p)</string> + <string name="resolution_three_quarter">0.75X (540p/810p)</string> + <string name="resolution_one">1X (720p/1080p)</string> + <string name="resolution_two">2X (1440p/2160p) (Slow)</string> + <string name="resolution_three">3X (2160p/3240p) (Lento)</string> + <string name="resolution_four">4X (2880p/4320p) (Lento)</string> + + <!-- Renderer VSync --> + <string name="renderer_vsync_immediate">Imediato (Desligado)</string> + <string name="renderer_vsync_mailbox">Caixa de entrada</string> + <string name="renderer_vsync_fifo">FIFO (Ligado)</string> + <string name="renderer_vsync_fifo_relaxed">FIFO Relaxado </string> + + <!-- Scaling Filters --> + <string name="scaling_filter_nearest_neighbor">Vizinho mais próximo</string> + <string name="scaling_filter_bilinear">Bilinear</string> + <string name="scaling_filter_bicubic">Bicúbico</string> + <string name="scaling_filter_gaussian">Gaussiano</string> + <string name="scaling_filter_scale_force">ScaleForce</string> + <string name="scaling_filter_fsr">AMD FidelityFX™ Super Resolution</string> + + <!-- Anti-Aliasing --> + <string name="anti_aliasing_none">Nenhum</string> + <string name="anti_aliasing_fxaa">FXAA</string> + <string name="anti_aliasing_smaa">SMAA</string> + + <!-- Aspect Ratios --> + <string name="ratio_default">Padrão (16:9)</string> + <string name="ratio_force_four_three">Forçar 4:3</string> + <string name="ratio_force_twenty_one_nine">Forçar 21:9</string> + <string name="ratio_force_sixteen_ten">Forçar 16:10</string> + <string name="ratio_stretch">Esticar para a janela</string> + + <!-- CPU Accuracy --> + <string name="cpu_accuracy_accurate">Preciso</string> + <string name="cpu_accuracy_unsafe">Não seguro</string> + <string name="cpu_accuracy_paranoid">Paranoid (Lento)</string> + + <!-- Gamepad Buttons --> + <string name="gamepad_d_pad">D-pad</string> + <string name="gamepad_left_stick">Analógico esquerdo</string> + <string name="gamepad_right_stick">Analógico direito</string> + <string name="gamepad_home">Botão Home</string> + <string name="gamepad_screenshot">Captura de ecrã</string> + + <!-- Disk shader cache --> + <string name="preparing_shaders">A preparar shaders</string> + <string name="building_shaders">A criar shaders</string> + + <!-- Theme options --> + <string name="change_app_theme">Muda o Tema da App</string> + <string name="theme_default">Padrão</string> + <string name="theme_material_you">Material You</string> + + <!-- Theme Modes --> + <string name="change_theme_mode">Altera o Modo do Tema</string> + <string name="theme_mode_follow_system">Igual ao Sistema</string> + <string name="theme_mode_light">Claro</string> + <string name="theme_mode_dark">Escuro</string> + + <!-- Black backgrounds theme --> + <string name="use_black_backgrounds">Usa Fundos Negros</string> + <string name="use_black_backgrounds_description">Quando usar tema escuro, aplicar fundos escuros</string> + +</resources> diff --git a/src/android/app/src/main/res/values-pt-rPT/strings.xml b/src/android/app/src/main/res/values-pt-rPT/strings.xml new file mode 100644 index 0000000000..8761e23749 --- /dev/null +++ b/src/android/app/src/main/res/values-pt-rPT/strings.xml @@ -0,0 +1,337 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <string name="app_disclaimer">Este software corre jogos para a consola Nintendo Switch. Não estão incluídas nem jogos ou chaves. <br /><br />Antes de começares, por favor localiza o ficheiro <![CDATA[1 prod.keys 1]]> no armazenamento do teu dispositivo.<br /><br /><![CDATA[2Learn more2]]></string> + <string name="emulation_notification_channel_name">Emulação está Ativa</string> + <string name="emulation_notification_channel_description">Mostra uma notificação permanente enquanto a emulação está a correr.</string> + <string name="emulation_notification_running">Yuzu está em execução </string> + <string name="notice_notification_channel_name">Notificações e erros</string> + <string name="notice_notification_channel_description">Mostra notificações quendo algo corre mal.</string> + <string name="notification_permission_not_granted">Permissões de notificação não permitidas </string> + + <!-- Setup strings --> + <string name="welcome">Benvindo! </string> + <string name="welcome_description">Aprende como configurar <b>yuzu</b> e arranca a emulação.</string> + <string name="get_started">Começa</string> + <string name="keys">Chaves</string> + <string name="keys_description">Seleciona o teu ficheiro <b>prod.keys</b> com o botão abaixo.</string> + <string name="select_keys">Seleciona as Chaves</string> + <string name="games">Jogos</string> + <string name="games_description">Seleciona a tua pasta <b>Games</b> com o botão abaixo.</string> + <string name="done">Feito</string> + <string name="done_description">Tudo pronto.\nDisfruta dos teus jogos!</string> + <string name="text_continue">Continuar</string> + <string name="next">Próximo</string> + <string name="back">Voltar</string> + <string name="add_games">Adiciona Jogos</string> + <string name="add_games_description">Seleciona a tua pasta de Jogos</string> + + <!-- Home strings --> + <string name="home_games">Jogos</string> + <string name="home_search">Pesquisar</string> + <string name="home_settings">Configurações</string> + <string name="empty_gamelist">Não foram encontrados jogos ou a pasta de Jogos ainda não foi definida. </string> + <string name="search_and_filter_games">Procura e filtra jogos.</string> + <string name="select_games_folder">Seleciona a pasta de jogos.</string> + <string name="select_games_folder_description">Permite que o Yuzu preencha a lista de jogos</string> + <string name="add_games_warning">Ignorar a seleção da pasta de jogos?</string> + <string name="add_games_warning_description">Os jogos não serão exibidos na lista de jogos se uma pasta não estiver selecionada.</string> + <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> + <string name="home_search_games">Procurar Jogos</string> + <string name="games_dir_selected">Pasta de Jogos selecionada</string> + <string name="install_prod_keys">Instala prod.keys</string> + <string name="install_prod_keys_description">Necessário para desencriptar jogos comerciais</string> + <string name="install_prod_keys_warning">Ignorar a adição de chaves?</string> + <string name="install_prod_keys_warning_description">São necessárias chaves válidas para emular jogos comerciais. Somente aplicativos homebrew funcionarão se você continuar.</string> + <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string> + <string name="notifications">Notificações</string> + <string name="notifications_description">Conceda a permissão de notificação com o botão abaixo.</string> + <string name="give_permission">Conceda permissão</string> + <string name="notification_warning">Saltar a concessão da permissão de notificação?</string> + <string name="notification_warning_description">Yuzu não conseguirá te notificar de informações importantes. </string> + <string name="permission_denied">Permissão negada</string> + <string name="permission_denied_description">Você negou essa permissão muitas vezes e agora precisa concedê-la manualmente nas configurações do sistema.</string> + <string name="about">Sobre</string> + <string name="about_description">Versão de compilação, créditos e mais</string> + <string name="warning_help">Ajuda</string> + <string name="warning_skip">Saltar</string> + <string name="warning_cancel">Cancelar</string> + <string name="install_amiibo_keys">Instala chaves Amiibo</string> + <string name="install_amiibo_keys_description">Necessário para usares Amiibo no jogo</string> + <string name="invalid_keys_file">Ficheiro de chaves inválido</string> + <string name="install_keys_success">Chaves instaladas com sucesso</string> + <string name="reading_keys_failure">Erro ao ler chaves de encriptação</string> + <string name="invalid_keys_error">Chaves de encriptação inválidas</string> + <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> + <string name="install_keys_failure_description">O ficheiro selecionado está corrompido. Por favor recarrega as tuas chaves.</string> + <string name="install_gpu_driver">Instala driver para GPU</string> + <string name="install_gpu_driver_description">Instala drivers alternativos para desempenho ou precisão potencialmente melhores</string> + <string name="advanced_settings">Configurações avançadas</string> + <string name="settings_description">Configura configurações do emulador</string> + <string name="search_recently_played">Jogos recentes</string> + <string name="search_recently_added">Adicionados recentemente</string> + <string name="search_retail">Jogos comerciais</string> + <string name="search_homebrew">Homebrew</string> + <string name="open_user_folder">Abre a pasta Yuzu</string> + <string name="open_user_folder_description">Gere os ficheiro internos do Yuzu</string> + <string name="theme_and_color_description">Modifica a aparência da App</string> + <string name="no_file_manager">Nenhum gestor de ficheiros encontrado</string> + <string name="notification_no_directory_link">Impossível abrir pasta Yuzu</string> + <string name="notification_no_directory_link_description">Localiza a pasta de utilizador manualmente com o painel lateral do gestor de ficheiros.</string> + <string name="manage_save_data">Gerir dados guardados</string> + <string name="manage_save_data_description">Dados não encontrados. Por favor seleciona uma opção abaixo.</string> + <string name="import_export_saves_description">Importa ou exporta dados guardados</string> + <string name="import_export_saves_no_profile">Dados não encontrados. Por favor lança o jogo e tenta novamente.</string> + <string name="save_file_imported_success">Importado com sucesso</string> + <string name="save_file_invalid_zip_structure">Estrutura de diretório de dados invalida</string> + <string name="save_file_invalid_zip_structure_description">O nome da primeira sub pasta tem de ser a ID do jogo.</string> + <string name="import_saves">Importar</string> + <string name="export_saves">Exportar</string> + + <!-- About screen strings --> + <string name="gaia_is_not_real">Gaia não é real</string> + <string name="copied_to_clipboard">Copiado para a área de transferência</string> + <string name="about_app_description">Um emulador Switch de código aberto</string> + <string name="contributors">Contribuidores</string> + <string name="contributors_description">Feito com \u2764 da equipa do Yuzu</string> + <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> + <string name="build">Versão</string> + <string name="support_link">https://discord.gg/u77vRWY</string> + <string name="website_link">https://yuzu-emu.org/</string> + <string name="github_link">https://github.com/yuzu-emu</string> + + <!-- Early access upgrade strings --> + <string name="early_access">Acesso antecipado</string> + <string name="get_early_access">Obtém Acesso Antecipado</string> + <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string> + <string name="get_early_access_description">Recursos de ponta, acesso antecipado a atualizações e muito mais</string> + <string name="early_access_benefits">Benefícios do Acesso Antecipado</string> + <string name="cutting_edge_features">Recursos de ponta</string> + <string name="early_access_updates">Acesso antecipado a atualizações</string> + <string name="no_manual_installation">Sem instalação manual</string> + <string name="prioritized_support">Suporte prioritário</string> + <string name="helping_game_preservation">Ajuda na preservação dos jogos</string> + <string name="our_eternal_gratitude">A nossa eterna gratidão</string> + <string name="are_you_interested">Estás interessado?</string> + + <!-- General settings strings --> + <string name="frame_limit_enable">Ativar limite de velocidade</string> + <string name="frame_limit_enable_description">Quando ativada, a velocidade da emulação será limitada à percentagem definida da velocidade normal.</string> + <string name="frame_limit_slider">Percentagem do limite de velocidade</string> + <string name="frame_limit_slider_description">Especifica o limite da percentagem da velocidade da emulação. Com a velocidade por defeito a 100% a emulação será limitada à velocidade normal. Valores maiores ou menores aumentarão ou diminuirão o limite de velocidade.</string> + <string name="cpu_accuracy">Precisão do CPU</string> + + <!-- System settings strings --> + <string name="use_docked_mode">Modo ancorado</string> + <string name="use_docked_mode_description">Emula em modo ancorado, que aumenta a resolução ás custas da performance.</string> + <string name="emulated_region">Região da emulação</string> + <string name="emulated_language">Idioma da emulação</string> + <string name="select_rtc_date">Seleciona a data RTC</string> + <string name="select_rtc_time">Seleciona a hora RTC</string> + <string name="use_custom_rtc">Ativa RTC personalizado</string> + <string name="use_custom_rtc_description">Esta configuração permite definir um RTC personalizado diferente da hora atual do sistema</string> + <string name="set_custom_rtc">Define RTC personalizado</string> + + <!-- Graphics settings strings --> + <string name="renderer_api">API</string> + <string name="renderer_accuracy">Nível de precisão</string> + <string name="renderer_resolution">Resolução</string> + <string name="renderer_vsync">Modo VSync</string> + <string name="renderer_aspect_ratio">Proporção do ecrã</string> + <string name="renderer_scaling_filter">Filtro de Adaptação da Janela</string> + <string name="renderer_anti_aliasing">Método de Anti-Aliasing </string> + <string name="renderer_force_max_clock">Força velocidade máxima (Adreno only)</string> + <string name="renderer_force_max_clock_description">Força o GPU a correr à velocidade máxima (restrições térmicas serão aplicadas)</string> + <string name="renderer_asynchronous_shaders">Usa shaders assíncronos </string> + <string name="renderer_asynchronous_shaders_description">Compila shaders assincronamente, que aumentará a fluidez, mas poderá causar falhas.</string> + <string name="renderer_debug">Ativar depuração de gráficos</string> + <string name="renderer_debug_description">Quando selecionado, a API gráfica entra num modo de depuração mais lento.</string> + <string name="use_disk_shader_cache">Usar cache do disk shader</string> + <string name="use_disk_shader_cache_description">Aumenta a fluidez ao guardar e carregar shaders gerados para o armazenamento.</string> + + <!-- Audio settings strings --> + <string name="audio_volume">Volume</string> + <string name="audio_volume_description">Especifica o volume de saída.</string> + + <!-- Miscellaneous --> + <string name="slider_default">Padrão</string> + <string name="ini_saved">Configurações guardadas</string> + <string name="gameid_saved">Configurações guardadas para %1$s</string> + <string name="error_saving">Erro ao guardar %1$s.ini: %2$s</string> + <string name="loading">A carregar...</string> + <string name="reset_setting_confirmation">Queres reverter esta definição para os valores padrão?</string> + <string name="reset_to_default">Reverter para padrão</string> + <string name="reset_all_settings">Redefinir todas as configurações?</string> + <string name="reset_all_settings_description">Todas as configurações avançadas serão redefinidas para as definições padrão. Isto não pode ser revertido.</string> + <string name="settings_reset">Redefinir configurações </string> + <string name="close">Fechar</string> + <string name="learn_more">Saber Mais</string> + + <!-- GPU driver installation --> + <string name="select_gpu_driver">Seleciona a driver para o GPU</string> + <string name="select_gpu_driver_title">Queres substituir o driver do GPU atual? </string> + <string name="select_gpu_driver_install">Instalar</string> + <string name="select_gpu_driver_default">Padrão</string> + <string name="select_gpu_driver_install_success">Instalado%s</string> + <string name="select_gpu_driver_use_default">Usar o driver padrão do GPU</string> + <string name="select_gpu_driver_error">Driver selecionado inválido, a usar o padrão do sistema!</string> + <string name="system_gpu_driver">Driver do GPU padrão</string> + <string name="installing_driver">A instalar o Driver...</string> + + <!-- Preferences Screen --> + <string name="preferences_settings">Configurações</string> + <string name="preferences_general">Geral</string> + <string name="preferences_system">Sistema</string> + <string name="preferences_graphics">Gráficos</string> + <string name="preferences_audio">Audio</string> + <string name="preferences_theme">Cor e tema.</string> + + <!-- ROM loading errors --> + <string name="loader_error_encrypted">A tua ROM está encriptada</string> + <string name="loader_error_encrypted_roms_description"><![CDATA[Por favor segue os guias para fazer redump das tuas<a href=\"https://yuzu-emu.org/help/quickstart/#dumping-cartridge-games\">Cartidges de Jogo</a> or <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-installed-titles-eshop\">Jogos Instalados</a>.]]></string> + <string name="loader_error_encrypted_keys_description"><![CDATA[Por favor confirma que o teu ficheiro <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> está instalado para que os jogos possam ser desencriptados.]]></string> + <string name="loader_error_video_core">Ocorreu um erro ao iniciar o núcleo de vídeo.</string> + <string name="loader_error_video_core_description">Isto é normalmente causado por um driver de GPU incompatível. Instalar um driver GPU pode resolver este problema.</string> + <string name="loader_error_invalid_format">Impossível carregar a tua ROM</string> + <string name="loader_error_file_not_found">O ficheiro da ROM não existe</string> + + <!-- Emulation Menu --> + <string name="emulation_exit">Sair da emulação</string> + <string name="emulation_done">Feito</string> + <string name="emulation_fps_counter">Contador de FPS</string> + <string name="emulation_toggle_controls">Alterar Controlos</string> + <string name="emulation_rel_stick_center">Centro do Analógico Relativo</string> + <string name="emulation_dpad_slide">Deslizar do DPad</string> + <string name="emulation_haptics">Hápticos </string> + <string name="emulation_show_overlay">Mostrar sobreposição </string> + <string name="emulation_toggle_all">Alterar todos</string> + <string name="emulation_control_adjust">Ajustar a sobreposição </string> + <string name="emulation_control_scale">Escala</string> + <string name="emulation_control_opacity">Opacidade</string> + <string name="emulation_touch_overlay_reset">Redefinir Sobreposição </string> + <string name="emulation_touch_overlay_edit">Editar sobreposição </string> + <string name="emulation_pause">Pausa emulação</string> + <string name="emulation_unpause">Retomar emulação</string> + <string name="emulation_input_overlay">Opções de sobreposição </string> + <string name="emulation_game_loading">Jogo a carregar...</string> + + <string name="load_settings">Configurações a carregar...</string> + + <!-- Software keyboard --> + <string name="software_keyboard">Teclado de Software</string> + + <!-- Errors and warnings --> + <string name="abort_button">Abortar</string> + <string name="continue_button">Continuar</string> + <string name="system_archive_not_found">Arquivo do Sistema Não Encontrado</string> + <string name="system_archive_not_found_message">%s está em falta. Por favor apaga os teus ficheiros de sistema.\nContinuar a emulação pode causar erros.</string> + <string name="system_archive_general">Um arquivo do sistema</string> + <string name="save_load_error">Erro Guardar/Carregar</string> + <string name="fatal_error">Erro fatal</string> + <string name="fatal_error_message">Ocorreu um erro fatal. Verifica o teu registro para detalhes. \nContinuar a emulação pode causar erros.</string> + <string name="performance_warning">Desligar esta configuração irá reduzir a performance da emulação significantemente! Para a melhor experiência é recomendado que deixes esta configuração ativada.</string> + + <!-- Region Names --> + <string name="region_japan">Japão</string> + <string name="region_usa">EUA</string> + <string name="region_europe">Europa</string> + <string name="region_australia">Austrália</string> + <string name="region_china">China</string> + <string name="region_korea">Coreia</string> + <string name="region_taiwan">Taiwan</string> + + <!-- Language Names --> + <string name="language_japanese">Japonês (日本語)</string> + <string name="language_english">Inglês</string> + <string name="language_french">Francês (Français)</string> + <string name="langauge_german">Alemão (Deutsch)</string> + <string name="language_italian">Italiano (Italiano)</string> + <string name="language_spanish">Espanhol (Español)</string> + <string name="language_chinese">Chinês simplificado (简体中文)</string> + <string name="language_korean">Coreano (한국어)</string> + <string name="language_dutch">Holandês (Nederlands)</string> + <string name="language_portuguese">Português (Português)</string> + <string name="language_russian">Russo (Русский)</string> + <string name="language_taiwanese">Taiwanês (台湾)</string> + <string name="language_british_english">Inglês Britânico</string> + <string name="language_canadian_french">Fracês Canadiano (Français canadien)</string> + <string name="language_latin_american_spanish">Espanhol da América Latina (Español latino-americano)</string> + <string name="language_simplified_chinese">Chinês Simplificado (简体中文)</string> + <string name="language_traditional_chinese">Chinês Tradicional (正 體 中文)</string> + <string name="language_brazilian_portuguese">Português do Brasil (Português do Brasil)</string> + + <!-- Renderer APIs --> + <string name="renderer_vulkan">Vulcano</string> + <string name="renderer_none">Nenhum</string> + + <!-- Renderer Accuracy --> + <string name="renderer_accuracy_normal">Normal</string> + <string name="renderer_accuracy_high">Alto</string> + <string name="renderer_accuracy_extreme">Estremo (Lento)</string> + + <!-- Resolutions --> + <string name="resolution_half">0.5X (360p/540p)</string> + <string name="resolution_three_quarter">0.75X (540p/810p)</string> + <string name="resolution_one">1X (720p/1080p)</string> + <string name="resolution_two">2X (1440p/2160p) (Lento)</string> + <string name="resolution_three">3X (2160p/3240p) (Lento)</string> + <string name="resolution_four">4X (2880p/4320p) (Lento)</string> + + <!-- Renderer VSync --> + <string name="renderer_vsync_immediate">Imediato (Desligado)</string> + <string name="renderer_vsync_mailbox">Caixa de entrada</string> + <string name="renderer_vsync_fifo">FIFO (Ligado)</string> + <string name="renderer_vsync_fifo_relaxed">FIFO Relaxado </string> + + <!-- Scaling Filters --> + <string name="scaling_filter_nearest_neighbor">Vizinho mais próximo</string> + <string name="scaling_filter_bilinear">Bilinear</string> + <string name="scaling_filter_bicubic">Bicúbico</string> + <string name="scaling_filter_gaussian">Gaussiano</string> + <string name="scaling_filter_scale_force">ScaleForce</string> + <string name="scaling_filter_fsr">AMD FidelityFX™ Super Resolution</string> + + <!-- Anti-Aliasing --> + <string name="anti_aliasing_none">Nenhum</string> + <string name="anti_aliasing_fxaa">FXAA</string> + <string name="anti_aliasing_smaa">SMAA</string> + + <!-- Aspect Ratios --> + <string name="ratio_default">Padrão (16:9)</string> + <string name="ratio_force_four_three">Forçar 4:3</string> + <string name="ratio_force_twenty_one_nine">Forçar 21:9</string> + <string name="ratio_force_sixteen_ten">Forçar 16:10</string> + <string name="ratio_stretch">Esticar à Janela</string> + + <!-- CPU Accuracy --> + <string name="cpu_accuracy_accurate">Preciso</string> + <string name="cpu_accuracy_unsafe">Inseguro</string> + <string name="cpu_accuracy_paranoid">Paranoid (Lento)</string> + + <!-- Gamepad Buttons --> + <string name="gamepad_d_pad">D-Pad</string> + <string name="gamepad_left_stick">Analógico Esquerdo</string> + <string name="gamepad_right_stick">Analógico Direito</string> + <string name="gamepad_home">Home</string> + <string name="gamepad_screenshot">Captura de ecrã</string> + + <!-- Disk shader cache --> + <string name="preparing_shaders">A preparar shaders</string> + <string name="building_shaders">A criar shaders</string> + + <!-- Theme options --> + <string name="change_app_theme">Muda o Tema da App</string> + <string name="theme_default">Padrão</string> + <string name="theme_material_you">Material You</string> + + <!-- Theme Modes --> + <string name="change_theme_mode">Altera o Modo do Tema</string> + <string name="theme_mode_follow_system">Igual ao Sistema</string> + <string name="theme_mode_light">Claro</string> + <string name="theme_mode_dark">Escuro</string> + + <!-- Black backgrounds theme --> + <string name="use_black_backgrounds">Usa Fundos Escuros</string> + <string name="use_black_backgrounds_description">Quando usar tema escuro, aplicar fundos escuros</string> + +</resources> diff --git a/src/android/app/src/main/res/values-ru/strings.xml b/src/android/app/src/main/res/values-ru/strings.xml new file mode 100644 index 0000000000..0fb4908f7c --- /dev/null +++ b/src/android/app/src/main/res/values-ru/strings.xml @@ -0,0 +1,337 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <string name="app_disclaimer">Это программное обеспечение позволяет запускать игры для игровой консоли Nintendo Switch. Мы не предоставляем сами игры или ключи.<br /><br />Перед началом работы найдите файл <![CDATA[<b> prod.keys </b>]]> в хранилище устройства..<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Узнать больше</a>]]></string> + <string name="emulation_notification_channel_name">Эмуляция активна</string> + <string name="emulation_notification_channel_description">Показывает постоянное уведомление, когда запущена эмуляция.</string> + <string name="emulation_notification_running">yuzu запущен</string> + <string name="notice_notification_channel_name">Уведомления и ошибки</string> + <string name="notice_notification_channel_description">Показывать уведомления, когда что-то пошло не так</string> + <string name="notification_permission_not_granted">Вы не предоставили разрешение уведомлений!</string> + + <!-- Setup strings --> + <string name="welcome">Добро пожаловать!</string> + <string name="welcome_description">Узнайте, как настроить <b>yuzu</b> и перейти прямиком к эмуляции.</string> + <string name="get_started">Начать</string> + <string name="keys">Ключи</string> + <string name="keys_description">Выберите ваш файл <b>prod.keys</b> с помощью кнопки ниже.</string> + <string name="select_keys">Выбрать ключи</string> + <string name="games">Игры</string> + <string name="games_description">Выберите вашу папку с <b>играми</b> с помощью кнопки ниже.</string> + <string name="done">Готово</string> + <string name="done_description">Все готово.\nМожно играть!</string> + <string name="text_continue">Продолжить</string> + <string name="next">Далее</string> + <string name="back">Назад</string> + <string name="add_games">Добавить игры</string> + <string name="add_games_description">Выберите папку с играми</string> + + <!-- Home strings --> + <string name="home_games">Игры</string> + <string name="home_search">Поиск</string> + <string name="home_settings">Настройки</string> + <string name="empty_gamelist">Не найдены файлы или еще не выбрана папка с играми.</string> + <string name="search_and_filter_games">Поиск и фильтрация игр</string> + <string name="select_games_folder">Выберите папку с играми</string> + <string name="select_games_folder_description">Позволяет yuzu заполнить список игр</string> + <string name="add_games_warning">Пропустить выбор папки с играми?</string> + <string name="add_games_warning_description">Игры не будут отображаться в списке Игры, если папка не выбрана.</string> + <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> + <string name="home_search_games">Найти игры</string> + <string name="games_dir_selected">Выбрана папка с играми</string> + <string name="install_prod_keys">Установить prod.keys</string> + <string name="install_prod_keys_description">Требуется для расшифровки розничных игр</string> + <string name="install_prod_keys_warning">Пропустить добавление ключей?</string> + <string name="install_prod_keys_warning_description">Для эмуляции розничных игр требуются действительные ключи. Если вы продолжите, будут работать только homebrew приложения.</string> + <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string> + <string name="notifications">Уведомления</string> + <string name="notifications_description">Предоставьте разрешение уведомлений с помощью кнопки ниже.</string> + <string name="give_permission">Предоставить разрешение</string> + <string name="notification_warning">Пропустить предоставление разрешения уведомлений?</string> + <string name="notification_warning_description">yuzu не сможет уведомлять вас о важной информации.</string> + <string name="permission_denied">Разрешение отказано</string> + <string name="permission_denied_description">Вы слишком часто отклоняли это разрешение, и теперь вам нужно будет вручную предоставить его в настройках системы.</string> + <string name="about">О нас</string> + <string name="about_description">Версия сборки, титры и другое</string> + <string name="warning_help">Помощь</string> + <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="invalid_keys_file">Выбран неверный файл ключей</string> + <string name="install_keys_success">Ключи успешно установлены</string> + <string name="reading_keys_failure">Ошибка при чтении ключей шифрования</string> + <string name="invalid_keys_error">Неверные ключи шифрования</string> + <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> + <string name="install_keys_failure_description">Выбранный файл неверен или поврежден. Пожалуйста, пере-дампите ваши ключи.</string> + <string name="install_gpu_driver">Установить драйвер ГП</string> + <string name="install_gpu_driver_description">Установите альтернативные драйверы для потенциально лучшей производительности и/или точности</string> + <string name="advanced_settings">Расширенные настройки</string> + <string name="settings_description">Настройка параметров эмулятора</string> + <string name="search_recently_played">Недавно сыграно</string> + <string name="search_recently_added">Недавно добавлено</string> + <string name="search_retail">Розничные</string> + <string name="search_homebrew">Homebrew</string> + <string name="open_user_folder">Открыть папку yuzu</string> + <string name="open_user_folder_description">Управление внутренними файлами yuzu</string> + <string name="theme_and_color_description">Изменение внешнего вида приложения</string> + <string name="no_file_manager">Не найден файловый менеджер</string> + <string name="notification_no_directory_link">Не удалось открыть папку yuzu</string> + <string name="notification_no_directory_link_description">Пожалуйста, найдите папку пользователя с помощью боковой панели файлового менеджера вручную.</string> + <string name="manage_save_data">Управление данными сохранений</string> + <string name="manage_save_data_description">Найдено данные сохранений. Пожалуйста, выберите вариант ниже.</string> + <string name="import_export_saves_description">Импорт или экспорт файлов сохранения</string> + <string name="import_export_saves_no_profile">Данные сохранений не найдены. Пожалуйста, запустите игру и повторите попытку.</string> + <string name="save_file_imported_success">Успешно импортировано</string> + <string name="save_file_invalid_zip_structure">Недопустимая структура папки сохранения</string> + <string name="save_file_invalid_zip_structure_description">Название первой вложенной папки должно быть идентификатором игры.</string> + <string name="import_saves">Импорт</string> + <string name="export_saves">Экспорт</string> + + <!-- About screen strings --> + <string name="gaia_is_not_real">Gaia не существует</string> + <string name="copied_to_clipboard">Скопировано в буфер обмена</string> + <string name="about_app_description">Эмулятор Switch с открытым исходным кодом</string> + <string name="contributors">Контрибьюторы</string> + <string name="contributors_description">Сделано с \u2764 от команды yuzu</string> + <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> + <string name="build">Сборка</string> + <string name="support_link">https://discord.gg/u77vRWY</string> + <string name="website_link">https://yuzu-emu.org/</string> + <string name="github_link">https://github.com/yuzu-emu</string> + + <!-- Early access upgrade strings --> + <string name="early_access">Ранний доступ</string> + <string name="get_early_access">Получить ранний доступ</string> + <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string> + <string name="get_early_access_description">Новейшие возможности, ранний доступ к обновлениям и другое</string> + <string name="early_access_benefits">Преимущества раннего доступа</string> + <string name="cutting_edge_features">Новейшие возможности</string> + <string name="early_access_updates">Ранний доступ к обновлениям</string> + <string name="no_manual_installation">Без ручной установки</string> + <string name="prioritized_support">Приоритетная поддержка</string> + <string name="helping_game_preservation">Помощь в презервации игр</string> + <string name="our_eternal_gratitude">Наша бесконечная благодарность</string> + <string name="are_you_interested">Вы заинтересованы?</string> + + <!-- General settings strings --> + <string name="frame_limit_enable">Включить ограничение скорости</string> + <string name="frame_limit_enable_description">Если эта функция включена, скорость эмуляции будет ограничена указанным процентом от нормальной скорости.</string> + <string name="frame_limit_slider">Ограничение процента cкорости</string> + <string name="frame_limit_slider_description">Указывает процент для ограничения скорости эмуляции. При значении по умолчанию 100% эмуляция будет ограничена нормальной скоростью. Значения выше или ниже будут увеличивать или уменьшать ограничение скорости.</string> + <string name="cpu_accuracy">Точность ЦП</string> + + <!-- System settings strings --> + <string name="use_docked_mode">Режим док-станции</string> + <string name="use_docked_mode_description">Эмуляция режима док-станции, что увеличивает разрешение за счет снижения производительности.</string> + <string name="emulated_region">Эмулируемый регион</string> + <string name="emulated_language">Эмулируемый язык</string> + <string name="select_rtc_date">Выберите дату RTC</string> + <string name="select_rtc_time">Выберите время RTC</string> + <string name="use_custom_rtc">Включить пользовательский RTC</string> + <string name="use_custom_rtc_description">Этот параметр позволяет установить пользовательские часы реального времени отдельно от текущего системного времени</string> + <string name="set_custom_rtc">Установить пользовательский RTC</string> + + <!-- Graphics settings strings --> + <string name="renderer_api">API</string> + <string name="renderer_accuracy">Уровень точности</string> + <string name="renderer_resolution">Разрешение</string> + <string name="renderer_vsync">Режим верт. синхронизации</string> + <string name="renderer_aspect_ratio">Соотношение сторон</string> + <string name="renderer_scaling_filter">Фильтр адаптации окна</string> + <string name="renderer_anti_aliasing">Метод сглаживания</string> + <string name="renderer_force_max_clock">Принудительно заставить максимальную тактовую частоту (только для Adreno)</string> + <string name="renderer_force_max_clock_description">Заставляет ГП работать на максимально возможных тактовых частотах (тепловые ограничения все равно будут применяться).</string> + <string name="renderer_asynchronous_shaders">Использовать асинхронные шейдеры</string> + <string name="renderer_asynchronous_shaders_description">Компилирует шейдеры асинхронно, что уменьшает зависания, но может взамен предоставить визуальные баги.</string> + <string name="renderer_debug">Включить отладку графики</string> + <string name="renderer_debug_description">Если включено, графический API переходит в более медленный режим отладки</string> + <string name="use_disk_shader_cache">Использовать кэш шейдеров на диске</string> + <string name="use_disk_shader_cache_description">Уменьшение зависаний за счет хранения и загрузки сгенерированных шейдеров на хранилище.</string> + + <!-- Audio settings strings --> + <string name="audio_volume">Громкость</string> + <string name="audio_volume_description">Задает громкость аудиовыхода.</string> + + <!-- Miscellaneous --> + <string name="slider_default">По умолчанию</string> + <string name="ini_saved">Сохраненные настройки</string> + <string name="gameid_saved">Настройки сохранены для %1$s</string> + <string name="error_saving">Ошибка сохранения %1$s.ini: %2$s</string> + <string name="loading">Загрузка...</string> + <string name="reset_setting_confirmation">Хотите ли вы вернуть этот параметр к значению по умолчанию?</string> + <string name="reset_to_default">Сброс к настройкам по умолчанию</string> + <string name="reset_all_settings">Сбросить все настройки?</string> + <string name="reset_all_settings_description">Все дополнительные настройки будут сброшены к настройке по умолчанию. Это невозможно отменить.</string> + <string name="settings_reset">Настройки сброшены</string> + <string name="close">Закрыть</string> + <string name="learn_more">Узнать больше</string> + + <!-- GPU driver installation --> + <string name="select_gpu_driver">Выбрать драйвер ГП</string> + <string name="select_gpu_driver_title">Хотите заменить текущий драйвер ГП?</string> + <string name="select_gpu_driver_install">Установить</string> + <string name="select_gpu_driver_default">По умолчанию</string> + <string name="select_gpu_driver_install_success">Установлено %s</string> + <string name="select_gpu_driver_use_default">Используется стандартный драйвер ГП </string> + <string name="select_gpu_driver_error">Выбран неверный драйвер, используется стандартный системный!</string> + <string name="system_gpu_driver">Системный драйвер ГП</string> + <string name="installing_driver">Установка драйвера...</string> + + <!-- Preferences Screen --> + <string name="preferences_settings">Настройки</string> + <string name="preferences_general">Общие</string> + <string name="preferences_system">Система</string> + <string name="preferences_graphics">Графика</string> + <string name="preferences_audio">Аудио</string> + <string name="preferences_theme">Тема и цвет</string> + + <!-- ROM loading errors --> + <string name="loader_error_encrypted">Ваш ROM зашифрованный</string> + <string name="loader_error_encrypted_roms_description"><![CDATA[Пожалуйста, следуйте инструкциям, чтобы пере-дампить ваши <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-cartridge-games\">игровые картриджи</a> или <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-installed-titles-eshop\">установленные игры</a>.]]></string> + <string name="loader_error_encrypted_keys_description"><![CDATA[Пожалуйста, убедитесь, что ваш файл <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> установлен, чтобы игры можно было расшифровать.]]></string> + <string name="loader_error_video_core">Произошла ошибка при инициализации видеоядра.</string> + <string name="loader_error_video_core_description">Обычно это вызвано несовместимым драйвером ГП. Установка пользовательского драйвера ГП может решить эту проблему.</string> + <string name="loader_error_invalid_format">Не удалось запустить ROM</string> + <string name="loader_error_file_not_found">Файл ROM не существует</string> + + <!-- Emulation Menu --> + <string name="emulation_exit">Выход из эмуляции</string> + <string name="emulation_done">Готово</string> + <string name="emulation_fps_counter">Счётчик FPS</string> + <string name="emulation_toggle_controls">Переключение управления</string> + <string name="emulation_rel_stick_center">Относительный центр стика</string> + <string name="emulation_dpad_slide">Слайд крестовиной</string> + <string name="emulation_haptics">Тактильная обратная связь</string> + <string name="emulation_show_overlay">Показать оверлей</string> + <string name="emulation_toggle_all">Переключить всё</string> + <string name="emulation_control_adjust">Настроить оверлей</string> + <string name="emulation_control_scale">Масштаб</string> + <string name="emulation_control_opacity">Непрозрачность</string> + <string name="emulation_touch_overlay_reset">Сбросить оверлей</string> + <string name="emulation_touch_overlay_edit">Изменить оверлей</string> + <string name="emulation_pause">Пауза эмуляции</string> + <string name="emulation_unpause">Возобновление эмуляции</string> + <string name="emulation_input_overlay">Настройки оверлея</string> + <string name="emulation_game_loading">Загрузка игры...</string> + + <string name="load_settings">Загрузка настроек...</string> + + <!-- Software keyboard --> + <string name="software_keyboard">Виртуальная клавиатура</string> + + <!-- Errors and warnings --> + <string name="abort_button">Прервать</string> + <string name="continue_button">Продолжить</string> + <string name="system_archive_not_found">Системный архив не найден</string> + <string name="system_archive_not_found_message">%s отсутствует. Пожалуйста, сдампите ваши системные архивы.\nПродолжение эмуляции может привести к сбоям и ошибкам.</string> + <string name="system_archive_general">Системный архив</string> + <string name="save_load_error">Ошибка сохранения/загрузки</string> + <string name="fatal_error">Фатальная ошибка</string> + <string name="fatal_error_message">Произошла фатальная ошибка. Проверьте журнал для получения подробной информации.\nПродолжение эмуляции может привести к сбоям и ошибкам.</string> + <string name="performance_warning">Отключение этой настройки значительно снизит производительность эмуляции! Для достижения наилучших результатов рекомендуется оставить эту настройку включенной.</string> + + <!-- Region Names --> + <string name="region_japan">Япония</string> + <string name="region_usa">США</string> + <string name="region_europe">Европа</string> + <string name="region_australia">Австралия</string> + <string name="region_china">Китай</string> + <string name="region_korea">Корея</string> + <string name="region_taiwan">Тайвань</string> + + <!-- Language Names --> + <string name="language_japanese">Японский (日本語)</string> + <string name="language_english">Английский (English)</string> + <string name="language_french">Французский (Français)</string> + <string name="langauge_german">Немецкий (Deutsch)</string> + <string name="language_italian">Итальянский (Italiano)</string> + <string name="language_spanish">Испанский (Español)</string> + <string name="language_chinese">Китайский (简体中文)</string> + <string name="language_korean">Корейский (한국어)</string> + <string name="language_dutch">Голландский (Nederlands)</string> + <string name="language_portuguese">Португальский (Português)</string> + <string name="language_russian">Русский</string> + <string name="language_taiwanese">Тайваньский (台湾)</string> + <string name="language_british_english">Британский английский</string> + <string name="language_canadian_french">Канадский французский (Français canadien)</string> + <string name="language_latin_american_spanish">Латиноамериканский испанский (Español latinoamericano)</string> + <string name="language_simplified_chinese">Упрощенный китайский (简体中文)</string> + <string name="language_traditional_chinese">Традиционный китайский (正體中文)</string> + <string name="language_brazilian_portuguese">Бразильский португальский (Português do Brasil)</string> + + <!-- Renderer APIs --> + <string name="renderer_vulkan">Vulkan</string> + <string name="renderer_none">Никакой</string> + + <!-- Renderer Accuracy --> + <string name="renderer_accuracy_normal">Нормальная</string> + <string name="renderer_accuracy_high">Высокая</string> + <string name="renderer_accuracy_extreme">Экстрим (медленный)</string> + + <!-- Resolutions --> + <string name="resolution_half">0.5X (360p/540p)</string> + <string name="resolution_three_quarter">0.75X (540p/810p)</string> + <string name="resolution_one">1X (720p/1080p)</string> + <string name="resolution_two">2X (1440p/2160p) (Медленно)</string> + <string name="resolution_three">3X (2160p/3240p) (Медленно)</string> + <string name="resolution_four">4X (2880p/4320p) (Медленно)</string> + + <!-- Renderer VSync --> + <string name="renderer_vsync_immediate">Моментальная (выключена) </string> + <string name="renderer_vsync_mailbox">Mailbox</string> + <string name="renderer_vsync_fifo">FIFO (Включена)</string> + <string name="renderer_vsync_fifo_relaxed">FIFO Relaxed</string> + + <!-- Scaling Filters --> + <string name="scaling_filter_nearest_neighbor">Ближайший сосед</string> + <string name="scaling_filter_bilinear">Билинейный</string> + <string name="scaling_filter_bicubic">Бикубический</string> + <string name="scaling_filter_gaussian">Гаусс</string> + <string name="scaling_filter_scale_force">ScaleForce</string> + <string name="scaling_filter_fsr">AMD FidelityFX™️ Super Resolution</string> + + <!-- Anti-Aliasing --> + <string name="anti_aliasing_none">Выкл.</string> + <string name="anti_aliasing_fxaa">FXAA</string> + <string name="anti_aliasing_smaa">SMAA</string> + + <!-- Aspect Ratios --> + <string name="ratio_default">Стандартное (16:9)</string> + <string name="ratio_force_four_three">Заставить 4:3</string> + <string name="ratio_force_twenty_one_nine">Заставить 21:9</string> + <string name="ratio_force_sixteen_ten">Заставить 16:10</string> + <string name="ratio_stretch">Растянуть до окна</string> + + <!-- CPU Accuracy --> + <string name="cpu_accuracy_accurate">Точно</string> + <string name="cpu_accuracy_unsafe">Небезопасно</string> + <string name="cpu_accuracy_paranoid">Параноик (медленно)</string> + + <!-- Gamepad Buttons --> + <string name="gamepad_d_pad">Крестовина</string> + <string name="gamepad_left_stick">Левый мини-джойстик</string> + <string name="gamepad_right_stick">Правый мини-джойстик</string> + <string name="gamepad_home">Home</string> + <string name="gamepad_screenshot">Скриншот</string> + + <!-- Disk shader cache --> + <string name="preparing_shaders">Подготовка шейдеров</string> + <string name="building_shaders">Постройка шейдеров</string> + + <!-- Theme options --> + <string name="change_app_theme">Изменить тему приложения</string> + <string name="theme_default">По умолчанию</string> + <string name="theme_material_you">Material You</string> + + <!-- Theme Modes --> + <string name="change_theme_mode">Изменить режим темы</string> + <string name="theme_mode_follow_system">Системная</string> + <string name="theme_mode_light">Светлая</string> + <string name="theme_mode_dark">Темная</string> + + <!-- Black backgrounds theme --> + <string name="use_black_backgrounds">Использовать черный фон</string> + <string name="use_black_backgrounds_description">При использовании темной темы применяйте черный фон.</string> + +</resources> diff --git a/src/android/app/src/main/res/values-uk/strings.xml b/src/android/app/src/main/res/values-uk/strings.xml new file mode 100644 index 0000000000..0d11eb2d22 --- /dev/null +++ b/src/android/app/src/main/res/values-uk/strings.xml @@ -0,0 +1,337 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <string name="app_disclaimer">Це програмне забезпечення дозволяє запускати ігри для ігрової консолі Nintendo Switch. Ми не надаємо самі ігри або ключі.<br /><br />Перед початком роботи знайдіть ваш файл <![CDATA[<b> prod.keys </b>]]> у сховищі пристрою.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Дізнатися більше</a>]]></string> + <string name="emulation_notification_channel_name">Емуляція активна</string> + <string name="emulation_notification_channel_description">Показує постійне сповіщення, коли запущено емуляцію.</string> + <string name="emulation_notification_running">yuzu запущено</string> + <string name="notice_notification_channel_name">Сповіщення та помилки</string> + <string name="notice_notification_channel_description">Показувати сповіщення, коли щось пішло не так</string> + <string name="notification_permission_not_granted">Ви не надали дозвіл сповіщень!</string> + + <!-- Setup strings --> + <string name="welcome">Вітаємо!</string> + <string name="welcome_description">Дізнайтеся, як налаштувати <b>yuzu</b> та перейти до емуляції.</string> + <string name="get_started">Розпочати</string> + <string name="keys">Ключі</string> + <string name="keys_description">Виберіть ваш файл <b>prod.keys</b> за допомогою кнопки нижче.</string> + <string name="select_keys">Вибрати ключі</string> + <string name="games">Ігри</string> + <string name="games_description">Виберіть вашу папку з <b>іграми</b> за допомогою кнопки нижче.</string> + <string name="done">Готово</string> + <string name="done_description">Все готово.\nМожна грати!</string> + <string name="text_continue">Продовжити</string> + <string name="next">Далі</string> + <string name="back">Назад</string> + <string name="add_games">Додати ігри</string> + <string name="add_games_description">Виберіть папку з іграми</string> + + <!-- Home strings --> + <string name="home_games">Ігри</string> + <string name="home_search">Пошук</string> + <string name="home_settings">Налаштування</string> + <string name="empty_gamelist">Не знайдено файлів або ще не вибрано папку з іграми.</string> + <string name="search_and_filter_games">Пошук та фільтрація ігор</string> + <string name="select_games_folder">Виберіть папку з іграми</string> + <string name="select_games_folder_description">Дозволяє yuzu заповнити список ігор</string> + <string name="add_games_warning">Пропустити вибір папки з іграми?</string> + <string name="add_games_warning_description">Ігри не відображатимуться у списку Ігри, якщо папку не вибрано.</string> + <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> + <string name="home_search_games">Знайти ігри</string> + <string name="games_dir_selected">Вибрано папку з іграми</string> + <string name="install_prod_keys">Встановити prod.keys</string> + <string name="install_prod_keys_description">Потрібно для розшифровки роздрібних ігор</string> + <string name="install_prod_keys_warning">Пропустити додавання ключів?</string> + <string name="install_prod_keys_warning_description">Для емуляції роздрібних ігор потрібні дійсні ключі. Якщо ви продовжите, працюватимуть тільки homebrew додатки.</string> + <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string> + <string name="notifications">Сповіщення</string> + <string name="notifications_description">Надайте дозвіл сповіщень за допомогою кнопки нижче.</string> + <string name="give_permission">Надати дозвіл</string> + <string name="notification_warning">Пропустити надання дозволу сповіщень?</string> + <string name="notification_warning_description">yuzu не зможе повідомляти вас про важливу інформацію.</string> + <string name="permission_denied">У дозволі відмовлено</string> + <string name="permission_denied_description">Ви занадто часто відхиляли цей дозвіл, тож тепер вам потрібно буде вручну надати його в системних налаштуваннях.</string> + <string name="about">Про нас</string> + <string name="about_description">Версія збірки, титри та інше</string> + <string name="warning_help">Допомога</string> + <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="invalid_keys_file">Вибрано неправильний файл ключів</string> + <string name="install_keys_success">Ключі успішно встановлено</string> + <string name="reading_keys_failure">Помилка під час зчитування ключів шифрування</string> + <string name="invalid_keys_error">Невірні ключі шифрування</string> + <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> + <string name="install_keys_failure_description">Обраний файл невірний або пошкоджений. Будь ласка, пере-дампіть ваші ключі.</string> + <string name="install_gpu_driver">Встановити драйвер ГП</string> + <string name="install_gpu_driver_description">Встановіть альтернативні драйвери для потенційно кращої продуктивності та/або точності</string> + <string name="advanced_settings">Розширені налаштування</string> + <string name="settings_description">Налаштування параметрів емулятора</string> + <string name="search_recently_played">Нещодавно зіграно</string> + <string name="search_recently_added">Нещодавно додано</string> + <string name="search_retail">Роздрібні</string> + <string name="search_homebrew">Homebrew</string> + <string name="open_user_folder">Відкрити папку yuzu</string> + <string name="open_user_folder_description">Керування внутрішніми файлами yuzu</string> + <string name="theme_and_color_description">Змінити зовнішній вигляд застосунку</string> + <string name="no_file_manager">Не знайдено файлового менеджера</string> + <string name="notification_no_directory_link">Не вдалося відкрити папку yuzu</string> + <string name="notification_no_directory_link_description">Будь ласка, знайдіть папку користувача за допомогою бічної панелі файлового менеджера вручну.</string> + <string name="manage_save_data">Керування даними збережень</string> + <string name="manage_save_data_description">Знайдено дані збережень. Будь ласка, виберіть варіант нижче.</string> + <string name="import_export_saves_description">Імпорт або експорт файлів збереження</string> + <string name="import_export_saves_no_profile">Дані збережень не знайдено. Будь ласка, запустіть гру та повторіть спробу.</string> + <string name="save_file_imported_success">Успішно імпортовано</string> + <string name="save_file_invalid_zip_structure">Неприпустима структура папки збереження</string> + <string name="save_file_invalid_zip_structure_description">Назва першої вкладеної папки має бути ідентифікатором гри.</string> + <string name="import_saves">Імпорт</string> + <string name="export_saves">Експорт</string> + + <!-- About screen strings --> + <string name="gaia_is_not_real">Gaia не існує</string> + <string name="copied_to_clipboard">Скопійовано в буфер обміну</string> + <string name="about_app_description">Емулятор Switch із відкритим першокодом</string> + <string name="contributors">Вкладники</string> + <string name="contributors_description">Зроблено з \u2764 від команди yuzu</string> + <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> + <string name="build">Збірка</string> + <string name="support_link">https://discord.gg/u77vRWY</string> + <string name="website_link">https://yuzu-emu.org/</string> + <string name="github_link">https://github.com/yuzu-emu</string> + + <!-- Early access upgrade strings --> + <string name="early_access">Ранній доступ</string> + <string name="get_early_access">Отримати ранній доступ</string> + <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string> + <string name="get_early_access_description">Новітні можливості, ранній доступ до оновлень та інше</string> + <string name="early_access_benefits">Переваги раннього доступу</string> + <string name="cutting_edge_features">Новітні можливості</string> + <string name="early_access_updates">Ранній доступ до оновлень</string> + <string name="no_manual_installation">Без ручного встановлення</string> + <string name="prioritized_support">Пріоритетна підтримка</string> + <string name="helping_game_preservation">Допомога в презервації ігор</string> + <string name="our_eternal_gratitude">Наша нескінченна вдячність</string> + <string name="are_you_interested">Ви зацікавлені?</string> + + <!-- General settings strings --> + <string name="frame_limit_enable">Увімкнути обмеження швидкості</string> + <string name="frame_limit_enable_description">Якщо цю функцію ввімкнено, швидкість емуляції буде обмежена зазначеним відсотком від нормальної швидкості.</string> + <string name="frame_limit_slider">Обмеження відсотка швидкості</string> + <string name="frame_limit_slider_description">Вказує відсоток для обмеження швидкості емуляції. При значенні за замовчуванням 100% емуляція буде обмежена нормальною швидкістю. Значення вище або нижче збільшуватимуть або зменшуватимуть обмеження швидкості.</string> + <string name="cpu_accuracy">Точність ЦП</string> + + <!-- System settings strings --> + <string name="use_docked_mode">Режим док-станції</string> + <string name="use_docked_mode_description">Емуляція режиму док-станції, що збільшує роздільну здатність за рахунок зниження продуктивності.</string> + <string name="emulated_region">Емульований регіон</string> + <string name="emulated_language">Емульована мова</string> + <string name="select_rtc_date">Оберіть дату RTC</string> + <string name="select_rtc_time">Оберіть час RTC</string> + <string name="use_custom_rtc">Увімкнути користувацький RTC</string> + <string name="use_custom_rtc_description">Цей параметр дає змогу встановити користувацький годинник реального часу окремо від поточного системного часу</string> + <string name="set_custom_rtc">Встановити користувацький RTC</string> + + <!-- Graphics settings strings --> + <string name="renderer_api">API</string> + <string name="renderer_accuracy">Рівень точності</string> + <string name="renderer_resolution">Роздільна здатність</string> + <string name="renderer_vsync">Режим верт. синхронізації</string> + <string name="renderer_aspect_ratio">Співвідношення сторін</string> + <string name="renderer_scaling_filter">Фільтр адаптації вікна</string> + <string name="renderer_anti_aliasing">Метод згладжування</string> + <string name="renderer_force_max_clock">Примусово змусити максимальну тактову частоту (тільки для Adreno)</string> + <string name="renderer_force_max_clock_description">Змушує ГП працювати на максимально можливих тактових частотах (теплові обмеження все одно будуть застосовуватися).</string> + <string name="renderer_asynchronous_shaders">Використовувати асинхронні шейдери</string> + <string name="renderer_asynchronous_shaders_description">Компілює шейдери асинхронно, що зменшує зависання, але може натомість надати візуальні баги.</string> + <string name="renderer_debug">Увімкнути налагодження графіки</string> + <string name="renderer_debug_description">Якщо увімкнено, графічний API переходить у повільніший режим налагодження</string> + <string name="use_disk_shader_cache">Використовувати кеш шейдерів на диску</string> + <string name="use_disk_shader_cache_description">Зменшення зависань завдяки зберіганню та завантаженню згенерованих шейдерів на сховище.</string> + + <!-- Audio settings strings --> + <string name="audio_volume">Гучність</string> + <string name="audio_volume_description">Вказує гучність аудіовиходу.</string> + + <!-- Miscellaneous --> + <string name="slider_default">За замовчуванням</string> + <string name="ini_saved">Збережені налаштування</string> + <string name="gameid_saved">Налаштування збережені для %1$s</string> + <string name="error_saving">Помилка збереження %1$s.ini: %2$s</string> + <string name="loading">Завантаження...</string> + <string name="reset_setting_confirmation">Чи хочете ви повернути цей параметр до значення за замовчуванням?</string> + <string name="reset_to_default">Скидання до налаштувань за замовчуванням</string> + <string name="reset_all_settings">Скинути всі налаштування</string> + <string name="reset_all_settings_description">Усі додаткові налаштування буде скинуто до налаштування за замовчуванням. Це неможливо скасувати.</string> + <string name="settings_reset">Налаштування скинуто</string> + <string name="close">Закрити</string> + <string name="learn_more">Дізнатися більше</string> + + <!-- GPU driver installation --> + <string name="select_gpu_driver">Вибрати драйвер ГП</string> + <string name="select_gpu_driver_title">Хочете замінити поточний драйвер ГП?</string> + <string name="select_gpu_driver_install">Встановити</string> + <string name="select_gpu_driver_default">За замовчуванням</string> + <string name="select_gpu_driver_install_success">Встановлено %s</string> + <string name="select_gpu_driver_use_default">Використовується стандартний драйвер ГП</string> + <string name="select_gpu_driver_error">Обрано неправильний драйвер, використовується стандартний системний!</string> + <string name="system_gpu_driver">Системний драйвер ГП</string> + <string name="installing_driver">Встановлення драйвера...</string> + + <!-- Preferences Screen --> + <string name="preferences_settings">Налаштування</string> + <string name="preferences_general">Загальні</string> + <string name="preferences_system">Система</string> + <string name="preferences_graphics">Графіка</string> + <string name="preferences_audio">Аудіо</string> + <string name="preferences_theme">Тема і колір</string> + + <!-- ROM loading errors --> + <string name="loader_error_encrypted">Ваш ROM зашифрований</string> + <string name="loader_error_encrypted_roms_description"><![CDATA[Будь ласка, дотримуйтесь інструкцій, щоб пере-дампити ваші <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-cartridge-games\">ігрові картриджі</a> або <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-installed-titles-eshop\">встановлені ігри</a>.]]></string> + <string name="loader_error_encrypted_keys_description"><![CDATA[Будь ласка, переконайтеся, що ваш файл <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> встановлено, щоб ігри можна було розшифрувати.]]></string> + <string name="loader_error_video_core">Сталася помилка під час ініціалізації відеоядра.</string> + <string name="loader_error_video_core_description">Зазвичай це спричинено несумісним драйвером ГП. Встановлення користувацького драйвера ГП може вирішити цю проблему.</string> + <string name="loader_error_invalid_format">Не вдалося запустити ROM</string> + <string name="loader_error_file_not_found">Файл ROM не існує</string> + + <!-- Emulation Menu --> + <string name="emulation_exit">Вихід з емуляції</string> + <string name="emulation_done">Готово</string> + <string name="emulation_fps_counter">Лічильник FPS</string> + <string name="emulation_toggle_controls">Перемикання керування</string> + <string name="emulation_rel_stick_center">Відносний центр стіка</string> + <string name="emulation_dpad_slide">Слайд хрестовиною</string> + <string name="emulation_haptics">Тактильний зворотний зв\'язок</string> + <string name="emulation_show_overlay">Показати оверлей</string> + <string name="emulation_toggle_all">Перемкнути все</string> + <string name="emulation_control_adjust">Налаштувати оверлей</string> + <string name="emulation_control_scale">Масштаб</string> + <string name="emulation_control_opacity">Непрозорість</string> + <string name="emulation_touch_overlay_reset">Скинути оверлей</string> + <string name="emulation_touch_overlay_edit">Змінити оверлей</string> + <string name="emulation_pause">Пауза емуляції</string> + <string name="emulation_unpause">Відновлення емуляції</string> + <string name="emulation_input_overlay">Налаштування оверлея</string> + <string name="emulation_game_loading">Завантаження гри...</string> + + <string name="load_settings">Завантаження налаштувань...</string> + + <!-- Software keyboard --> + <string name="software_keyboard">Віртуальна клавіатура</string> + + <!-- Errors and warnings --> + <string name="abort_button">Перервати</string> + <string name="continue_button">Продовжити</string> + <string name="system_archive_not_found">Системний архів не знайдено</string> + <string name="system_archive_not_found_message">%s відсутній. Будь ласка, здампіть ваші системні архіви.\nПродовження емуляції може призвести до збоїв і помилок.</string> + <string name="system_archive_general">Системний архів</string> + <string name="save_load_error">Помилка збереження/завантаження</string> + <string name="fatal_error">Фатальна помилка</string> + <string name="fatal_error_message">Сталася фатальна помилка. Перевірте журнал для отримання докладної інформації.\nПродовження емуляції може призвести до збоїв і помилок.</string> + <string name="performance_warning">Вимкнення цього налаштування значно знизить продуктивність емуляції! Для досягнення найкращих результатів рекомендується залишити це налаштування увімкненим.</string> + + <!-- Region Names --> + <string name="region_japan">Японія</string> + <string name="region_usa">США</string> + <string name="region_europe">Європа</string> + <string name="region_australia">Австралія</string> + <string name="region_china">Китай</string> + <string name="region_korea">Корея</string> + <string name="region_taiwan">Тайвань</string> + + <!-- Language Names --> + <string name="language_japanese">Японська (日本語)</string> + <string name="language_english">Англійська (English)</string> + <string name="language_french">Французька (Français)</string> + <string name="langauge_german">Німецька (Deutsch)</string> + <string name="language_italian">Італійська (Italiano)</string> + <string name="language_spanish">Іспанська (Español)</string> + <string name="language_chinese">Китайскька (简体中文)</string> + <string name="language_korean">Корейська (한국어)</string> + <string name="language_dutch">Голландська (Nederlands)</string> + <string name="language_portuguese">Португальська (Português)</string> + <string name="language_russian">Російська (Русский)</string> + <string name="language_taiwanese">Тайванська (台湾)</string> + <string name="language_british_english">Британська англійська</string> + <string name="language_canadian_french">Канадська французька (Français canadien)</string> + <string name="language_latin_american_spanish">Латиноамериканська іспанська (Español latinoamericano)</string> + <string name="language_simplified_chinese">Спрощена китайська (简体中文)</string> + <string name="language_traditional_chinese">Традиційна китайська (正體中文)</string> + <string name="language_brazilian_portuguese">Бразильська португальська (Português do Brasil)</string> + + <!-- Renderer APIs --> + <string name="renderer_vulkan">Vulkan</string> + <string name="renderer_none">Вимкнено</string> + + <!-- Renderer Accuracy --> + <string name="renderer_accuracy_normal">Нормальна</string> + <string name="renderer_accuracy_high">Висока</string> + <string name="renderer_accuracy_extreme">Екстрим (повільно)</string> + + <!-- Resolutions --> + <string name="resolution_half">0.5X (360p/540p)</string> + <string name="resolution_three_quarter">0.75X (540p/810p)</string> + <string name="resolution_one">1X (720p/1080p)</string> + <string name="resolution_two">2X (1440p/2160p) (Повільно)</string> + <string name="resolution_three">3X (2160p/3240p) (Повільно)</string> + <string name="resolution_four">4X (2880p/4320p) (Повільно)</string> + + <!-- Renderer VSync --> + <string name="renderer_vsync_immediate">Моментальна (вимкнена)</string> + <string name="renderer_vsync_mailbox">Mailbox</string> + <string name="renderer_vsync_fifo">FIFO (ввімкнута)</string> + <string name="renderer_vsync_fifo_relaxed">FIFO Relaxed</string> + + <!-- Scaling Filters --> + <string name="scaling_filter_nearest_neighbor">Найближчий сусід</string> + <string name="scaling_filter_bilinear">Білінійне</string> + <string name="scaling_filter_bicubic">Бікубічне</string> + <string name="scaling_filter_gaussian">Гауса</string> + <string name="scaling_filter_scale_force">ScaleForce</string> + <string name="scaling_filter_fsr">AMD FidelityFX™ Super Resolution</string> + + <!-- Anti-Aliasing --> + <string name="anti_aliasing_none">Вимкнено</string> + <string name="anti_aliasing_fxaa">FXAA</string> + <string name="anti_aliasing_smaa">SMAA</string> + + <!-- Aspect Ratios --> + <string name="ratio_default">За замовчуванням (16:9)</string> + <string name="ratio_force_four_three">Змусити 4:3</string> + <string name="ratio_force_twenty_one_nine">Змусити 21:9</string> + <string name="ratio_force_sixteen_ten">Змусити 16:10</string> + <string name="ratio_stretch">Розтягнути до вікна</string> + + <!-- CPU Accuracy --> + <string name="cpu_accuracy_accurate">Точно</string> + <string name="cpu_accuracy_unsafe">Небезпечно</string> + <string name="cpu_accuracy_paranoid">Параноїк (повільно)</string> + + <!-- Gamepad Buttons --> + <string name="gamepad_d_pad">Кнопки напрямків</string> + <string name="gamepad_left_stick">Лівий міні-джойстик</string> + <string name="gamepad_right_stick">Правий міні-джойстик</string> + <string name="gamepad_home">Home</string> + <string name="gamepad_screenshot">Знімок екрану</string> + + <!-- Disk shader cache --> + <string name="preparing_shaders">Підготовка шейдерів</string> + <string name="building_shaders">Побудова шейдерів</string> + + <!-- Theme options --> + <string name="change_app_theme">Змінити тему застосунку</string> + <string name="theme_default">За замовчуванням</string> + <string name="theme_material_you">Material You</string> + + <!-- Theme Modes --> + <string name="change_theme_mode">Змінити режим теми</string> + <string name="theme_mode_follow_system">Системна</string> + <string name="theme_mode_light">Світла</string> + <string name="theme_mode_dark">Темна</string> + + <!-- Black backgrounds theme --> + <string name="use_black_backgrounds">Використовувати чорне тло</string> + <string name="use_black_backgrounds_description">У разі використання темної теми застосовуйте чорне тло.</string> + +</resources> diff --git a/src/android/app/src/main/res/values-zh-rCN/strings.xml b/src/android/app/src/main/res/values-zh-rCN/strings.xml new file mode 100644 index 0000000000..e00bbaa2e7 --- /dev/null +++ b/src/android/app/src/main/res/values-zh-rCN/strings.xml @@ -0,0 +1,337 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <string name="app_disclaimer">此软件可以运行 Nintendo Switch 游戏,但不包含任何游戏和密钥文件。<br /><br />在开始前,请找到放置于设备存储中的 <![CDATA[<b> prod.keys </b>]]> 文件。<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">了解更多</a>]]></string> + <string name="emulation_notification_channel_name">正在进行模拟</string> + <string name="emulation_notification_channel_description">在模拟运行时显示持久通知。</string> + <string name="emulation_notification_running">yuzu 正在运行</string> + <string name="notice_notification_channel_name">通知及错误提醒</string> + <string name="notice_notification_channel_description">当发生错误时显示通知。</string> + <string name="notification_permission_not_granted">未授予通知权限!</string> + + <!-- Setup strings --> + <string name="welcome">欢迎!</string> + <string name="welcome_description">了解如何设置 <b>yuzu</b> 并进行模拟。</string> + <string name="get_started">开始</string> + <string name="keys">密钥文件</string> + <string name="keys_description">使用下方的按钮来选择你的 <b>prod.keys</b> 文件。</string> + <string name="select_keys">选择密钥文件</string> + <string name="games">游戏</string> + <string name="games_description">使用下方的按钮选择你的 <b>游戏</b> 文件夹。</string> + <string name="done">完成</string> + <string name="done_description">你完成了全部设置。\n玩的开心!</string> + <string name="text_continue">继续</string> + <string name="next">下一步</string> + <string name="back">上一步</string> + <string name="add_games">添加游戏</string> + <string name="add_games_description">选择你的游戏文件夹</string> + + <!-- Home strings --> + <string name="home_games">游戏</string> + <string name="home_search">搜索</string> + <string name="home_settings">设置</string> + <string name="empty_gamelist">找不到游戏,或者尚未选择游戏文件夹。</string> + <string name="search_and_filter_games">搜索游戏</string> + <string name="select_games_folder">选择游戏文件夹</string> + <string name="select_games_folder_description">允许 yuzu 填充游戏列表</string> + <string name="add_games_warning">跳过选择游戏文件夹?</string> + <string name="add_games_warning_description">如果未选择游戏文件夹,游戏将不会显示在游戏列表中。</string> + <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> + <string name="home_search_games">搜索游戏</string> + <string name="games_dir_selected">已选择游戏文件夹</string> + <string name="install_prod_keys">安装 prod.keys 文件</string> + <string name="install_prod_keys_description">需要密钥文件来解密游戏</string> + <string name="install_prod_keys_warning">跳过添加密钥文件?</string> + <string name="install_prod_keys_warning_description">对于商业游戏,需要有效的密钥文件才能运行。如果没有密钥文件,将只能运行自制软件。</string> + <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string> + <string name="notifications">通知</string> + <string name="notifications_description">使用下方的按钮授予通知权限。</string> + <string name="give_permission">授予权限</string> + <string name="notification_warning">跳过授予通知权限?</string> + <string name="notification_warning_description">yuzu 将无法通知您重要信息。</string> + <string name="permission_denied">授权遭拒</string> + <string name="permission_denied_description">您曾多次拒绝权限请求,现在您需要在系统设置中手动授予权限。</string> + <string name="about">关于</string> + <string name="about_description">开发版本、贡献者、以及更多</string> + <string name="warning_help">帮助</string> + <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="invalid_keys_file">选择的密钥文件无效</string> + <string name="install_keys_success">密钥文件已成功安装</string> + <string name="reading_keys_failure">读取加密密钥时出错</string> + <string name="invalid_keys_error">无效的加密密钥</string> + <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> + <string name="install_keys_failure_description">选择的密钥文件不正确或已损坏。请重新转储密钥文件。</string> + <string name="install_gpu_driver">安装 GPU 驱动</string> + <string name="install_gpu_driver_description">安装替代的驱动程序以获得更好的性能和精度</string> + <string name="advanced_settings">高级选项</string> + <string name="settings_description">更改模拟器设置</string> + <string name="search_recently_played">最近游玩</string> + <string name="search_recently_added">最近添加</string> + <string name="search_retail">商业游戏</string> + <string name="search_homebrew">自制游戏</string> + <string name="open_user_folder">打开 yuzu 文件夹</string> + <string name="open_user_folder_description">管理 yuzu 内部文件</string> + <string name="theme_and_color_description">更改外观</string> + <string name="no_file_manager">找不到可用的文件管理器</string> + <string name="notification_no_directory_link">无法打开 yuzu 文件夹</string> + <string name="notification_no_directory_link_description">请使用文件管理器的侧部面板手动定位用户文件夹。</string> + <string name="manage_save_data">管理存档数据</string> + <string name="manage_save_data_description">已找到存档数据,请选择下方的选项。</string> + <string name="import_export_saves_description">导入或导出存档</string> + <string name="import_export_saves_no_profile">找不到存档数据,请启动游戏并重试。</string> + <string name="save_file_imported_success">已成功导入存档</string> + <string name="save_file_invalid_zip_structure">无效的存档目录</string> + <string name="save_file_invalid_zip_structure_description">第一个子文件夹名称必须为当前游戏的 ID。</string> + <string name="import_saves">导入</string> + <string name="export_saves">导出</string> + + <!-- About screen strings --> + <string name="gaia_is_not_real">Gaia 不真实</string> + <string name="copied_to_clipboard">已复制到剪贴板</string> + <string name="about_app_description">一款开放源代码的 Switch 模拟器</string> + <string name="contributors">贡献者</string> + <string name="contributors_description">使用来自 yuzu 团队的 \u2764 制作</string> + <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> + <string name="build">构建版本</string> + <string name="support_link">https://discord.gg/u77vRWY</string> + <string name="website_link">https://yuzu-emu.org/</string> + <string name="github_link">https://github.com/yuzu-emu</string> + + <!-- Early access upgrade strings --> + <string name="early_access">抢先体验</string> + <string name="get_early_access">取得抢先体验</string> + <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string> + <string name="get_early_access_description">最新的功能、抢先更新、以及更多</string> + <string name="early_access_benefits">抢先体验的权益</string> + <string name="cutting_edge_features">最新功能</string> + <string name="early_access_updates">抢先更新</string> + <string name="no_manual_installation">无需手动安装</string> + <string name="prioritized_support">优先支持</string> + <string name="helping_game_preservation">帮助保留游戏</string> + <string name="our_eternal_gratitude">我们真诚的感激</string> + <string name="are_you_interested">您对此感兴趣吗?</string> + + <!-- General settings strings --> + <string name="frame_limit_enable">启用运行速度限制</string> + <string name="frame_limit_enable_description">启用后,模拟速度将限制在正常运行速度的指定百分比。</string> + <string name="frame_limit_slider">限制速度百分比</string> + <string name="frame_limit_slider_description">指定限制模拟速度的百分比。预设为 100%,此时模拟速度将被限制为标准速度。更高或更低的值将增加或降低速度限制上限。</string> + <string name="cpu_accuracy">CPU 精度</string> + + <!-- System settings strings --> + <string name="use_docked_mode">主机模式</string> + <string name="use_docked_mode_description">以主机模式进行模拟,牺牲性能并提高画面分辨率。</string> + <string name="emulated_region">模拟区域</string> + <string name="emulated_language">模拟语言</string> + <string name="select_rtc_date">选择日期</string> + <string name="select_rtc_time">选择时间</string> + <string name="use_custom_rtc">启用自定义系统时钟</string> + <string name="use_custom_rtc_description">此选项允许您设置与目前系统时间相独立的自定义系统时钟</string> + <string name="set_custom_rtc">设置自定义系统时钟</string> + + <!-- Graphics settings strings --> + <string name="renderer_api">API</string> + <string name="renderer_accuracy">精度等级</string> + <string name="renderer_resolution">分辨率</string> + <string name="renderer_vsync">垂直同步模式</string> + <string name="renderer_aspect_ratio">屏幕纵横比</string> + <string name="renderer_scaling_filter">窗口滤镜</string> + <string name="renderer_anti_aliasing">抗锯齿方式</string> + <string name="renderer_force_max_clock">强制最大时钟 (仅限 Adreno)</string> + <string name="renderer_force_max_clock_description">强制 GPU 以最大时钟运行 (仍被温控限制)。</string> + <string name="renderer_asynchronous_shaders">使用异步着色器</string> + <string name="renderer_asynchronous_shaders_description">异步编译着色器,减少卡顿,但可能引入故障。</string> + <string name="renderer_debug">启用图形调试</string> + <string name="renderer_debug_description">启用时,图形 API 将进入较慢的调试模式。</string> + <string name="use_disk_shader_cache">使用磁盘着色器缓存</string> + <string name="use_disk_shader_cache_description">将生成的着色器缓存于磁盘中并进行读取以减少卡顿。</string> + + <!-- Audio settings strings --> + <string name="audio_volume">音量</string> + <string name="audio_volume_description">指定输出的音量。</string> + + <!-- Miscellaneous --> + <string name="slider_default">系统默认</string> + <string name="ini_saved">已保存设置</string> + <string name="gameid_saved">已保存 %1$s 的设置</string> + <string name="error_saving">保存 %1$s.ini 时出错: %2$s</string> + <string name="loading">加载中…</string> + <string name="reset_setting_confirmation">您要将此设定重设为默认值吗?</string> + <string name="reset_to_default">恢复默认</string> + <string name="reset_all_settings">重置所有设置项?</string> + <string name="reset_all_settings_description">所有高级选项都将被重设,此动作无法还原。</string> + <string name="settings_reset">重设设置项</string> + <string name="close">关闭</string> + <string name="learn_more">了解更多</string> + + <!-- GPU driver installation --> + <string name="select_gpu_driver">选择 GPU 驱动程序</string> + <string name="select_gpu_driver_title">要取代您当前的 GPU 驱动程序吗?</string> + <string name="select_gpu_driver_install">安装</string> + <string name="select_gpu_driver_default">系统默认</string> + <string name="select_gpu_driver_install_success">已安装 %s</string> + <string name="select_gpu_driver_use_default">使用默认 GPU 驱动程序</string> + <string name="select_gpu_driver_error">选择的驱动程序无效,将使用系统默认的驱动程序!</string> + <string name="system_gpu_driver">系统 GPU 驱动程序</string> + <string name="installing_driver">正在安装驱动程序…</string> + + <!-- Preferences Screen --> + <string name="preferences_settings">设置</string> + <string name="preferences_general">通用</string> + <string name="preferences_system">系统</string> + <string name="preferences_graphics">图形</string> + <string name="preferences_audio">声音</string> + <string name="preferences_theme">主题和色彩</string> + + <!-- ROM loading errors --> + <string name="loader_error_encrypted">您的 ROM 已加密</string> + <string name="loader_error_encrypted_roms_description"><![CDATA[请参考指南重新转储你的<a href=\"https://yuzu-emu.org/help/quickstart/#dumping-cartridge-games\">游戏卡带</a>或<a href=\"https://yuzu-emu.org/help/quickstart/#dumping-installed-titles-eshop\">已安装的游戏</a>。]]></string> + <string name="loader_error_encrypted_keys_description"><![CDATA[请确保 <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> 文件已安装,使得游戏可以被解密。]]></string> + <string name="loader_error_video_core">初始化视频核心时发生错误</string> + <string name="loader_error_video_core_description">这通常由不兼容的 GPU 驱动程序造成,安装自定义 GPU 驱动程序可能解决此问题。</string> + <string name="loader_error_invalid_format">无法载入 ROM</string> + <string name="loader_error_file_not_found">ROM 文件不存在</string> + + <!-- Emulation Menu --> + <string name="emulation_exit">退出模拟</string> + <string name="emulation_done">完成</string> + <string name="emulation_fps_counter">FPS 计数器</string> + <string name="emulation_toggle_controls">按键切换</string> + <string name="emulation_rel_stick_center">相对摇杆中心</string> + <string name="emulation_dpad_slide">十字方向键滑动</string> + <string name="emulation_haptics">触觉反馈</string> + <string name="emulation_show_overlay">显示虚拟按键</string> + <string name="emulation_toggle_all">全部切换</string> + <string name="emulation_control_adjust">调整虚拟按键</string> + <string name="emulation_control_scale">缩放</string> + <string name="emulation_control_opacity">不透明度</string> + <string name="emulation_touch_overlay_reset">重设虚拟按键</string> + <string name="emulation_touch_overlay_edit">编辑虚拟按键</string> + <string name="emulation_pause">暂停模拟</string> + <string name="emulation_unpause">继续模拟</string> + <string name="emulation_input_overlay">虚拟按键选项</string> + <string name="emulation_game_loading">载入游戏中…</string> + + <string name="load_settings">正在载入设定…</string> + + <!-- Software keyboard --> + <string name="software_keyboard">软件键盘</string> + + <!-- Errors and warnings --> + <string name="abort_button">中止</string> + <string name="continue_button">继续</string> + <string name="system_archive_not_found">未找到系统档案</string> + <string name="system_archive_not_found_message">%s 丢失,请转储您的系统档案。\n继续模拟可能造成崩溃和错误。</string> + <string name="system_archive_general">系统档案</string> + <string name="save_load_error">保存/载入发生错误</string> + <string name="fatal_error">致命错误</string> + <string name="fatal_error_message">发生致命错误,请查阅日志获取详细信息。\n继续模拟可能会造成崩溃和错误。</string> + <string name="performance_warning">关闭此项会显著降低模拟性能!建议您将此项保持为启用状态。</string> + + <!-- Region Names --> + <string name="region_japan">日本</string> + <string name="region_usa">美国</string> + <string name="region_europe">欧洲</string> + <string name="region_australia">澳大利亚</string> + <string name="region_china">中国</string> + <string name="region_korea">韩国</string> + <string name="region_taiwan">中国台湾</string> + + <!-- Language Names --> + <string name="language_japanese">日语 (日本語)</string> + <string name="language_english">英语 (English)</string> + <string name="language_french">法语 (Français)</string> + <string name="langauge_german">德语 (Deutsch)</string> + <string name="language_italian">意大利语 (Italiano)</string> + <string name="language_spanish">西班牙语 (Español)</string> + <string name="language_chinese">中文 (简体中文)</string> + <string name="language_korean">韩语 (한국어)</string> + <string name="language_dutch">荷兰语 (Nederlands)</string> + <string name="language_portuguese">葡萄牙语 (Português)</string> + <string name="language_russian">俄语 (Русский)</string> + <string name="language_taiwanese">台湾中文 (台灣)</string> + <string name="language_british_english">英式英语</string> + <string name="language_canadian_french">加拿大法语 (Français canadien)</string> + <string name="language_latin_american_spanish">拉丁美洲西班牙语 (Español latinoamericano)</string> + <string name="language_simplified_chinese">简体中文 (简体中文)</string> + <string name="language_traditional_chinese">繁体中文 (正體中文)</string> + <string name="language_brazilian_portuguese">巴西葡萄牙语 (Português do Brasil)</string> + + <!-- Renderer APIs --> + <string name="renderer_vulkan">Vulkan</string> + <string name="renderer_none">无</string> + + <!-- Renderer Accuracy --> + <string name="renderer_accuracy_normal">正常</string> + <string name="renderer_accuracy_high">高</string> + <string name="renderer_accuracy_extreme">极高 (慢速)</string> + + <!-- Resolutions --> + <string name="resolution_half">0.5X (360p/540p)</string> + <string name="resolution_three_quarter">0.75X (540p/810p)</string> + <string name="resolution_one">1X (720p/1080p)</string> + <string name="resolution_two">2X (1440p/2160p) (慢速)</string> + <string name="resolution_three">3X (2160p/3240p) (慢速)</string> + <string name="resolution_four">4X (2880p/4320p) (慢速)</string> + + <!-- Renderer VSync --> + <string name="renderer_vsync_immediate">即时 (关闭)</string> + <string name="renderer_vsync_mailbox">Mailbox</string> + <string name="renderer_vsync_fifo">FIFO (开启)</string> + <string name="renderer_vsync_fifo_relaxed">FIFO Relaxed</string> + + <!-- Scaling Filters --> + <string name="scaling_filter_nearest_neighbor">近邻取样</string> + <string name="scaling_filter_bilinear">双线性过滤</string> + <string name="scaling_filter_bicubic">双三线过滤</string> + <string name="scaling_filter_gaussian">高斯模糊</string> + <string name="scaling_filter_scale_force">强制缩放</string> + <string name="scaling_filter_fsr">AMD FidelityFX™️ 超级分辨率锐画技术</string> + + <!-- Anti-Aliasing --> + <string name="anti_aliasing_none">无</string> + <string name="anti_aliasing_fxaa">快速近似抗锯齿</string> + <string name="anti_aliasing_smaa">子像素形态学抗锯齿</string> + + <!-- Aspect Ratios --> + <string name="ratio_default">默认 (16:9)</string> + <string name="ratio_force_four_three">强制 4:3</string> + <string name="ratio_force_twenty_one_nine">强制 21:9</string> + <string name="ratio_force_sixteen_ten">强制 16:10</string> + <string name="ratio_stretch">拉伸窗口</string> + + <!-- CPU Accuracy --> + <string name="cpu_accuracy_accurate">高精度</string> + <string name="cpu_accuracy_unsafe">低精度</string> + <string name="cpu_accuracy_paranoid">偏执模式 (慢速)</string> + + <!-- Gamepad Buttons --> + <string name="gamepad_d_pad">十字方向键</string> + <string name="gamepad_left_stick">左摇杆</string> + <string name="gamepad_right_stick">右摇杆</string> + <string name="gamepad_home">Home</string> + <string name="gamepad_screenshot">截图</string> + + <!-- Disk shader cache --> + <string name="preparing_shaders">正在准备着色器</string> + <string name="building_shaders">正在编译着色器</string> + + <!-- Theme options --> + <string name="change_app_theme">切换主题</string> + <string name="theme_default">系统默认</string> + <string name="theme_material_you">Material You</string> + + <!-- Theme Modes --> + <string name="change_theme_mode">主题模式</string> + <string name="theme_mode_follow_system">跟随系统</string> + <string name="theme_mode_light">浅色</string> + <string name="theme_mode_dark">深色</string> + + <!-- Black backgrounds theme --> + <string name="use_black_backgrounds">使用黑色背景</string> + <string name="use_black_backgrounds_description">使用深色主题时,套用黑色背景。</string> + +</resources> diff --git a/src/android/app/src/main/res/values-zh-rTW/strings.xml b/src/android/app/src/main/res/values-zh-rTW/strings.xml new file mode 100644 index 0000000000..a54d042480 --- /dev/null +++ b/src/android/app/src/main/res/values-zh-rTW/strings.xml @@ -0,0 +1,336 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <string name="app_disclaimer">此軟體可以執行 Nintendo Switch 主機遊戲,但不包含任何遊戲和金鑰。<br /><br />在您開始前,請找到放置於您的裝置儲存空間的 <![CDATA[<b> prod.keys </b>]]> 檔案。<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">深入瞭解</a>]]></string> + <string name="emulation_notification_channel_name">模擬進行中</string> + <string name="emulation_notification_channel_description">在模擬執行時顯示持續通知。</string> + <string name="emulation_notification_running">yuzu 正在執行</string> + <string name="notice_notification_channel_name">通知和錯誤</string> + <string name="notice_notification_channel_description">發生錯誤時顯示通知。</string> + <string name="notification_permission_not_granted">未授予通知權限!</string> + + <!-- Setup strings --> + <string name="welcome">歡迎!</string> + <string name="welcome_description">瞭解如何設定 <b>yuzu</b> 並進入模擬。</string> + <string name="get_started">開始使用</string> + <string name="keys">金鑰</string> + <string name="keys_description">使用下方的按鈕選取您的 <b>prod.keys</b> 檔案。</string> + <string name="select_keys">選取金鑰</string> + <string name="games">遊戲</string> + <string name="games_description">使用下方的按鈕選取您的<b>遊戲</b>資料夾。</string> + <string name="done">完成</string> + <string name="done_description">您已準備就緒。\n盡情遊玩您的遊戲!</string> + <string name="text_continue">繼續</string> + <string name="next">下一步</string> + <string name="back">上一步</string> + <string name="add_games">新增遊戲</string> + <string name="add_games_description">選取您的遊戲資料夾</string> + + <!-- Home strings --> + <string name="home_games">遊戲</string> + <string name="home_search">搜尋</string> + <string name="home_settings">設定</string> + <string name="empty_gamelist">找不到檔案,或者尚未選取遊戲目錄。</string> + <string name="search_and_filter_games">搜尋並篩選遊戲</string> + <string name="select_games_folder">選取遊戲資料夾</string> + <string name="select_games_folder_description">一律允許 yuzu 填入遊戲清單</string> + <string name="add_games_warning">跳過選取遊戲資料夾?</string> + <string name="add_games_warning_description">如果資料夾未選取,遊戲將不會顯示在遊戲清單。</string> + <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> + <string name="home_search_games">搜尋遊戲</string> + <string name="games_dir_selected">遊戲目錄已選取</string> + <string name="install_prod_keys">安裝 prod.keys</string> + <string name="install_prod_keys_description">需要解密零售遊戲</string> + <string name="install_prod_keys_warning">跳過新增金鑰?</string> + <string name="install_prod_keys_warning_description">模擬零售遊戲需要有效的金鑰,若要繼續,將僅有自製遊戲應用程式可以運作。</string> + <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string> + <string name="notifications">通知</string> + <string name="notifications_description">使用下方的按鈕授予通知權限。</string> + <string name="give_permission">授予權限</string> + <string name="notification_warning">跳過授予通知權限?</string> + <string name="notification_warning_description">yuzu 將無法通知您重要資訊。</string> + <string name="permission_denied">權限遭拒</string> + <string name="permission_denied_description">您曾多次拒絕了權限要求,現在您需要在系統設定中手動授予權限。</string> + <string name="about">關於</string> + <string name="about_description">組建版本、製作群、以及更多</string> + <string name="warning_help">說明</string> + <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="invalid_keys_file">無效的金鑰檔案已選取</string> + <string name="install_keys_success">金鑰已成功安裝</string> + <string name="reading_keys_failure">讀取加密金鑰時出現錯誤</string> + <string name="invalid_keys_error">無效的加密金鑰</string> + <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> + <string name="install_keys_failure_description">選取的檔案不正確或已損毀,請重新傾印您的金鑰。</string> + <string name="install_gpu_driver">安裝 GPU 驅動程式</string> + <string name="install_gpu_driver_description">安裝替代驅動程式以取得潛在的更佳效能或準確度</string> + <string name="advanced_settings">進階設定</string> + <string name="settings_description">進行模擬器設定</string> + <string name="search_recently_played">最近遊玩</string> + <string name="search_recently_added">最近新增</string> + <string name="search_retail">零售</string> + <string name="search_homebrew">自製遊戲</string> + <string name="open_user_folder">開啟 yuzu 資料夾</string> + <string name="open_user_folder_description">管理 yuzu 的內部檔案</string> + <string name="theme_and_color_description">修改應用程式外觀</string> + <string name="no_file_manager">找不到檔案管理員</string> + <string name="notification_no_directory_link">無法開啟 yuzu 目錄</string> + <string name="notification_no_directory_link_description">請使用檔案管理員的側邊面板手動定位到使用者資料夾。</string> + <string name="manage_save_data">管理儲存資料</string> + <string name="manage_save_data_description">已找到儲存資料,請選取下方的選項。</string> + <string name="import_export_saves_description">匯入或匯出儲存檔案</string> + <string name="import_export_saves_no_profile">找不到儲存資料,請啟動遊戲並重試。</string> + <string name="save_file_imported_success">已成功匯入</string> + <string name="save_file_invalid_zip_structure">無效的儲存目錄結構</string> + <string name="save_file_invalid_zip_structure_description">首個子資料夾名稱必須為遊戲標題 ID。</string> + <string name="import_saves">匯入</string> + <string name="export_saves">匯出</string> + + <!-- About screen strings --> + <string name="gaia_is_not_real">Gaia 不真實</string> + <string name="copied_to_clipboard">已複製到剪貼簿</string> + <string name="about_app_description">一個開放原始碼的 Switch 模擬器</string> + <string name="contributors">參與者</string> + <string name="contributors_description">使用來自 yuzu 團隊的 \u2764 製作</string> + <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> + <string name="build">組建</string> + <string name="support_link">https://discord.gg/u77vRWY</string> + <string name="website_link">https://yuzu-emu.org/</string> + <string name="github_link">https://github.com/yuzu-emu</string> + + <!-- Early access upgrade strings --> + <string name="early_access">搶先體驗</string> + <string name="get_early_access">搶先體驗新功能</string> + <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string> + <string name="get_early_access_description">最新的功能、搶先版更新、以及更多</string> + <string name="early_access_benefits">搶先體驗權益</string> + <string name="cutting_edge_features">最新功能</string> + <string name="early_access_updates">搶先版更新</string> + <string name="no_manual_installation">無需手動安裝</string> + <string name="prioritized_support">優先支援</string> + <string name="helping_game_preservation">協助遊戲保留</string> + <string name="our_eternal_gratitude">我們永遠的感激</string> + <string name="are_you_interested">您仍感興趣嗎?</string> + + <!-- General settings strings --> + <string name="frame_limit_enable">啟用限制速度</string> + <string name="frame_limit_enable_description">若啟用,模擬速度將會限制在標準速度的指定百分比。</string> + <string name="frame_limit_slider">限制速度百分比</string> + <string name="frame_limit_slider_description">指定限制模擬速度的百分比。預設為 100%,模擬速度將被限制為標準速度。更高或更低的值將會增加或減少速度限制。</string> + <string name="cpu_accuracy">CPU 準確度</string> + + <!-- System settings strings --> + <string name="use_docked_mode">底座模式</string> + <string name="use_docked_mode_description">以底座模式模擬,以犧牲效能的代價提高解析度。</string> + <string name="emulated_region">模擬區域</string> + <string name="emulated_language">模擬語言</string> + <string name="select_rtc_date">選取 RTC 日期</string> + <string name="select_rtc_time">選取 RTC 時間</string> + <string name="use_custom_rtc">啟用自訂 RTC</string> + <string name="use_custom_rtc_description">此設定允許您設定與您的目前系統時間相互獨立的自訂即時時鐘</string> + <string name="set_custom_rtc">設定自訂 RTC</string> + + <!-- Graphics settings strings --> + <string name="renderer_api">API</string> + <string name="renderer_accuracy">準確度層級</string> + <string name="renderer_resolution">解析度</string> + <string name="renderer_vsync">VSync 模式</string> + <string name="renderer_aspect_ratio">長寬比</string> + <string name="renderer_scaling_filter">視窗適應過濾器</string> + <string name="renderer_anti_aliasing">消除鋸齒方法</string> + <string name="renderer_force_max_clock">強制最大時脈 (僅 Adreno)</string> + <string name="renderer_force_max_clock_description">強制 GPU 以最大可能時脈執行 (熱溫限制仍被套用)。</string> + <string name="renderer_asynchronous_shaders">使用非同步著色器</string> + <string name="renderer_asynchronous_shaders_description">非同步編譯著色器,將會減少間斷,但可能會引入故障。</string> + <string name="renderer_debug">啟用圖形偵錯</string> + <string name="renderer_debug_description">核取時,圖形 API 將會進入慢速偵錯模式。</string> + <string name="use_disk_shader_cache">使用磁碟著色器快取</string> + <string name="use_disk_shader_cache_description">透過將產生的著色器儲存並載入至磁碟,減少中斷。</string> + + <!-- Audio settings strings --> + <string name="audio_volume">音量</string> + <string name="audio_volume_description">指定音訊輸出音量。</string> + + <!-- Miscellaneous --> + <string name="slider_default">預設</string> + <string name="ini_saved">已儲存設定</string> + <string name="gameid_saved">已儲存 %1$s 設定</string> + <string name="error_saving">儲存 %1$s 時發生錯誤 ini: %2$s</string> + <string name="loading">正在載入…</string> + <string name="reset_setting_confirmation">要將此設定重設回預設值嗎?</string> + <string name="reset_to_default">重設為預設值</string> + <string name="reset_all_settings">重設所有設定?</string> + <string name="reset_all_settings_description">所有進階設定將被重設為預設組態,此動作無法復原。</string> + <string name="settings_reset">設定已重設</string> + <string name="close">關閉</string> + <string name="learn_more">深入瞭解</string> + + <!-- GPU driver installation --> + <string name="select_gpu_driver">選取 GPU 驅動程式</string> + <string name="select_gpu_driver_title">要取代您目前的 GPU 驅動程式嗎?</string> + <string name="select_gpu_driver_install">安裝</string> + <string name="select_gpu_driver_default">預設</string> + <string name="select_gpu_driver_install_success">已安裝 %s</string> + <string name="select_gpu_driver_use_default">使用預設 GPU 驅動程式</string> + <string name="select_gpu_driver_error">選取的驅動程式無效,將使用系統預設驅動程式!</string> + <string name="system_gpu_driver">系統 GPU 驅動程式</string> + <string name="installing_driver">正在安裝驅動程式…</string> + + <!-- Preferences Screen --> + <string name="preferences_settings">設定</string> + <string name="preferences_general">一般</string> + <string name="preferences_system">系統</string> + <string name="preferences_graphics">圖形</string> + <string name="preferences_audio">音訊</string> + <string name="preferences_theme">主題和色彩</string> + + <!-- ROM loading errors --> + <string name="loader_error_encrypted">您的 ROM 已加密</string> + <string name="loader_error_encrypted_roms_description"><![CDATA[請依循指南重新傾印您的<a href=\"https://yuzu-emu.org/help/quickstart/#dumping-cartridge-games\">遊戲卡匣</a>或<a href=\"https://yuzu-emu.org/help/quickstart/#dumping-installed-titles-eshop\">安裝標題</a>。]]></string> + <string name="loader_error_encrypted_keys_description"><![CDATA[請確保您的 <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> 檔案已安裝,讓遊戲可以解密。]]></string> + <string name="loader_error_video_core">初始化視訊核心時發生錯誤</string> + <string name="loader_error_video_core_description">這經常由不相容的 GPU 驅動程式造成,安裝自訂 GPU 驅動程式可能會解決此問題。</string> + <string name="loader_error_invalid_format">無法載入 ROM</string> + <string name="loader_error_file_not_found">ROM 檔案不存在</string> + + <!-- Emulation Menu --> + <string name="emulation_exit">結束模擬</string> + <string name="emulation_done">完成</string> + <string name="emulation_fps_counter">FPS 計數器</string> + <string name="emulation_toggle_controls">切換控制</string> + <string name="emulation_rel_stick_center">相對搖桿中心</string> + <string name="emulation_dpad_slide">方向鍵滑動</string> + <string name="emulation_haptics">觸覺回饋技術</string> + <string name="emulation_show_overlay">顯示覆疊</string> + <string name="emulation_toggle_all">全部切換</string> + <string name="emulation_control_adjust">調整覆疊</string> + <string name="emulation_control_scale">縮放</string> + <string name="emulation_control_opacity">不透明度</string> + <string name="emulation_touch_overlay_reset">重設覆疊</string> + <string name="emulation_touch_overlay_edit">編輯覆疊</string> + <string name="emulation_pause">暫停模擬</string> + <string name="emulation_unpause">取消暫停模擬</string> + <string name="emulation_input_overlay">覆疊選項</string> + <string name="emulation_game_loading">遊戲正在載入…</string> + + <string name="load_settings">正在載入設定…</string> + + <!-- Software keyboard --> + <string name="software_keyboard">軟體鍵盤</string> + + <!-- Errors and warnings --> + <string name="abort_button">中止</string> + <string name="continue_button">繼續</string> + <string name="system_archive_not_found">找不到系統檔案</string> + <string name="system_archive_not_found_message">%s 遺失,請傾印您的系統封存。\n繼續模擬可能會造成當機和錯誤。</string> + <string name="system_archive_general">系統封存</string> + <string name="save_load_error">儲存/載入發生錯誤</string> + <string name="fatal_error">嚴重錯誤</string> + <string name="fatal_error_message">發生嚴重錯誤,檢查記錄以取得詳細資訊。\n繼續模擬可能會造成當機和錯誤。</string> + <string name="performance_warning">關閉此設定會顯著降低模擬效能!如需最佳體驗,建議您將此設定保持為啟用狀態。</string> + + <!-- Region Names --> + <string name="region_japan">日本</string> + <string name="region_usa">美國</string> + <string name="region_europe">歐洲</string> + <string name="region_australia">澳洲</string> + <string name="region_china">中國</string> + <string name="region_korea">南韓</string> + <string name="region_taiwan">台灣</string> + + <!-- Language Names --> + <string name="language_japanese">日文 (日本語)</string> + <string name="language_english">英文</string> + <string name="language_french">法文 (Français)</string> + <string name="langauge_german">德文 (Deutsch)</string> + <string name="language_italian">義大利文 (Italiano)</string> + <string name="language_spanish">西班牙文 (Español)</string> + <string name="language_chinese">中文 (简体中文)</string> + <string name="language_korean">韓文 (한국어)</string> + <string name="language_dutch">荷蘭文 (Nederlands)</string> + <string name="language_portuguese">葡萄牙文 (Português)</string> + <string name="language_russian">俄文 (Русский)</string> + <string name="language_taiwanese">台文 (台灣)</string> + <string name="language_british_english">英式英文</string> + <string name="language_canadian_french">加拿大法文 (Français canadien)</string> + <string name="language_latin_american_spanish">拉丁美洲西班牙文 (Español latinoamericano)</string> + <string name="language_simplified_chinese">簡體中文 (简体中文)</string> + <string name="language_traditional_chinese">正體中文 (正體中文)</string> + <string name="language_brazilian_portuguese">巴西葡萄牙文 (Português do Brasil)</string> + + <!-- Renderer APIs --> + <string name="renderer_vulkan">Vulkan</string> + <string name="renderer_none">無</string> + + <!-- Renderer Accuracy --> + <string name="renderer_accuracy_normal">標準</string> + <string name="renderer_accuracy_high">高</string> + <string name="renderer_accuracy_extreme">極高 (慢)</string> + + <!-- Resolutions --> + <string name="resolution_half">0.5X (360p/540p)</string> + <string name="resolution_three_quarter">0.75X (540p/810p)</string> + <string name="resolution_one">1X (720p/1080p)</string> + <string name="resolution_two">2X (1440p/2160p) (慢)</string> + <string name="resolution_three">3X (2160p/3240p) (慢)</string> + <string name="resolution_four">4X (2880p/4320p) (慢)</string> + + <!-- Renderer VSync --> + <string name="renderer_vsync_immediate">即時 (關閉)</string> + <string name="renderer_vsync_mailbox">信箱</string> + <string name="renderer_vsync_fifo">FIFO (開啟)</string> + <string name="renderer_vsync_fifo_relaxed">FIFO 寬鬆</string> + + <!-- Scaling Filters --> + <string name="scaling_filter_nearest_neighbor">最近鄰</string> + <string name="scaling_filter_bilinear">雙線性</string> + <string name="scaling_filter_bicubic">雙立方</string> + <string name="scaling_filter_gaussian">高斯</string> + <string name="scaling_filter_scale_force">強制縮放</string> + <string name="scaling_filter_fsr">AMD Radeon™ 超級解析度</string> + + <!-- Anti-Aliasing --> + <string name="anti_aliasing_none">無</string> + <string name="anti_aliasing_fxaa">FXAA</string> + <string name="anti_aliasing_smaa">SMAA</string> + + <!-- Aspect Ratios --> + <string name="ratio_default">預設 (16:9)</string> + <string name="ratio_force_four_three">強制 4:3</string> + <string name="ratio_force_twenty_one_nine">強制 21:9</string> + <string name="ratio_force_sixteen_ten">強制 16:10</string> + <string name="ratio_stretch">延伸視窗</string> + + <!-- CPU Accuracy --> + <string name="cpu_accuracy_unsafe">低精度</string> + <string name="cpu_accuracy_paranoid">不合理 (慢)</string> + + <!-- Gamepad Buttons --> + <string name="gamepad_d_pad">方向鍵</string> + <string name="gamepad_left_stick">左搖桿</string> + <string name="gamepad_right_stick">右搖桿</string> + <string name="gamepad_home">HOME</string> + <string name="gamepad_screenshot">螢幕截圖</string> + + <!-- Disk shader cache --> + <string name="preparing_shaders">正在準備著色器</string> + <string name="building_shaders">正在建置著色器</string> + + <!-- Theme options --> + <string name="change_app_theme">變更應用程式主題</string> + <string name="theme_default">預設</string> + <string name="theme_material_you">Material You</string> + + <!-- Theme Modes --> + <string name="change_theme_mode">變更主題模式</string> + <string name="theme_mode_follow_system">跟隨系統</string> + <string name="theme_mode_light">淺色</string> + <string name="theme_mode_dark">深色</string> + + <!-- Black backgrounds theme --> + <string name="use_black_backgrounds">使用黑色背景</string> + <string name="use_black_backgrounds_description">使用深色主題時,套用黑色背景。</string> + +</resources> diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml index ea20cb17c3..6d092f7a94 100644 --- a/src/android/app/src/main/res/values/arrays.xml +++ b/src/android/app/src/main/res/values/arrays.xml @@ -119,6 +119,18 @@ <item>3</item> </integer-array> + <string-array name="rendererScreenLayoutNames"> + <item>@string/screen_layout_landscape</item> + <item>@string/screen_layout_portrait</item> + <item>@string/screen_layout_auto</item> + </string-array> + + <integer-array name="rendererScreenLayoutValues"> + <item>5</item> + <item>4</item> + <item>0</item> + </integer-array> + <string-array name="rendererAspectRatioNames"> <item>@string/ratio_default</item> <item>@string/ratio_force_four_three</item> @@ -224,4 +236,15 @@ <item>2</item> </integer-array> + <string-array name="outputEngineEntries"> + <item>@string/auto</item> + <item>@string/cubeb</item> + <item>@string/string_null</item> + </string-array> + <string-array name="outputEngineValues"> + <item>auto</item> + <item>cubeb</item> + <item>null</item> + </string-array> + </resources> diff --git a/src/android/app/src/main/res/values/integers.xml b/src/android/app/src/main/res/values/integers.xml index bc614b81d7..2e93b408cc 100644 --- a/src/android/app/src/main/res/values/integers.xml +++ b/src/android/app/src/main/res/values/integers.xml @@ -34,4 +34,68 @@ <integer name="SWITCH_BUTTON_DPAD_X">260</integer> <integer name="SWITCH_BUTTON_DPAD_Y">790</integer> + <!-- Default SWITCH portrait layout --> + <integer name="SWITCH_BUTTON_A_X_PORTRAIT">840</integer> + <integer name="SWITCH_BUTTON_A_Y_PORTRAIT">840</integer> + <integer name="SWITCH_BUTTON_B_X_PORTRAIT">740</integer> + <integer name="SWITCH_BUTTON_B_Y_PORTRAIT">880</integer> + <integer name="SWITCH_BUTTON_X_X_PORTRAIT">740</integer> + <integer name="SWITCH_BUTTON_X_Y_PORTRAIT">800</integer> + <integer name="SWITCH_BUTTON_Y_X_PORTRAIT">640</integer> + <integer name="SWITCH_BUTTON_Y_Y_PORTRAIT">840</integer> + <integer name="SWITCH_STICK_L_X_PORTRAIT">180</integer> + <integer name="SWITCH_STICK_L_Y_PORTRAIT">660</integer> + <integer name="SWITCH_STICK_R_X_PORTRAIT">820</integer> + <integer name="SWITCH_STICK_R_Y_PORTRAIT">660</integer> + <integer name="SWITCH_TRIGGER_L_X_PORTRAIT">140</integer> + <integer name="SWITCH_TRIGGER_L_Y_PORTRAIT">260</integer> + <integer name="SWITCH_TRIGGER_R_X_PORTRAIT">860</integer> + <integer name="SWITCH_TRIGGER_R_Y_PORTRAIT">260</integer> + <integer name="SWITCH_TRIGGER_ZL_X_PORTRAIT">140</integer> + <integer name="SWITCH_TRIGGER_ZL_Y_PORTRAIT">200</integer> + <integer name="SWITCH_TRIGGER_ZR_X_PORTRAIT">860</integer> + <integer name="SWITCH_TRIGGER_ZR_Y_PORTRAIT">200</integer> + <integer name="SWITCH_BUTTON_MINUS_X_PORTRAIT">440</integer> + <integer name="SWITCH_BUTTON_MINUS_Y_PORTRAIT">950</integer> + <integer name="SWITCH_BUTTON_PLUS_X_PORTRAIT">560</integer> + <integer name="SWITCH_BUTTON_PLUS_Y_PORTRAIT">950</integer> + <integer name="SWITCH_BUTTON_HOME_X_PORTRAIT">680</integer> + <integer name="SWITCH_BUTTON_HOME_Y_PORTRAIT">950</integer> + <integer name="SWITCH_BUTTON_CAPTURE_X_PORTRAIT">320</integer> + <integer name="SWITCH_BUTTON_CAPTURE_Y_PORTRAIT">950</integer> + <integer name="SWITCH_BUTTON_DPAD_X_PORTRAIT">240</integer> + <integer name="SWITCH_BUTTON_DPAD_Y_PORTRAIT">840</integer> + + <!-- Default SWITCH foldable layout --> + <integer name="SWITCH_BUTTON_A_X_FOLDABLE">840</integer> + <integer name="SWITCH_BUTTON_A_Y_FOLDABLE">390</integer> + <integer name="SWITCH_BUTTON_B_X_FOLDABLE">740</integer> + <integer name="SWITCH_BUTTON_B_Y_FOLDABLE">430</integer> + <integer name="SWITCH_BUTTON_X_X_FOLDABLE">740</integer> + <integer name="SWITCH_BUTTON_X_Y_FOLDABLE">350</integer> + <integer name="SWITCH_BUTTON_Y_X_FOLDABLE">640</integer> + <integer name="SWITCH_BUTTON_Y_Y_FOLDABLE">390</integer> + <integer name="SWITCH_STICK_L_X_FOLDABLE">180</integer> + <integer name="SWITCH_STICK_L_Y_FOLDABLE">250</integer> + <integer name="SWITCH_STICK_R_X_FOLDABLE">820</integer> + <integer name="SWITCH_STICK_R_Y_FOLDABLE">250</integer> + <integer name="SWITCH_TRIGGER_L_X_FOLDABLE">140</integer> + <integer name="SWITCH_TRIGGER_L_Y_FOLDABLE">130</integer> + <integer name="SWITCH_TRIGGER_R_X_FOLDABLE">860</integer> + <integer name="SWITCH_TRIGGER_R_Y_FOLDABLE">130</integer> + <integer name="SWITCH_TRIGGER_ZL_X_FOLDABLE">140</integer> + <integer name="SWITCH_TRIGGER_ZL_Y_FOLDABLE">70</integer> + <integer name="SWITCH_TRIGGER_ZR_X_FOLDABLE">860</integer> + <integer name="SWITCH_TRIGGER_ZR_Y_FOLDABLE">70</integer> + <integer name="SWITCH_BUTTON_MINUS_X_FOLDABLE">440</integer> + <integer name="SWITCH_BUTTON_MINUS_Y_FOLDABLE">470</integer> + <integer name="SWITCH_BUTTON_PLUS_X_FOLDABLE">560</integer> + <integer name="SWITCH_BUTTON_PLUS_Y_FOLDABLE">470</integer> + <integer name="SWITCH_BUTTON_HOME_X_FOLDABLE">680</integer> + <integer name="SWITCH_BUTTON_HOME_Y_FOLDABLE">470</integer> + <integer name="SWITCH_BUTTON_CAPTURE_X_FOLDABLE">320</integer> + <integer name="SWITCH_BUTTON_CAPTURE_Y_FOLDABLE">470</integer> + <integer name="SWITCH_BUTTON_DPAD_X_FOLDABLE">240</integer> + <integer name="SWITCH_BUTTON_DPAD_Y_FOLDABLE">390</integer> + </resources> diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 6e9d475573..af7450619f 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<resources> +<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> <!-- General application strings --> <string name="app_name" translatable="false">yuzu</string> @@ -102,6 +102,17 @@ <string name="share_log">Share debug logs</string> <string name="share_log_description">Share yuzu\'s log file to debug issues</string> <string name="share_log_missing">No log file found</string> + <string name="install_game_content">Install game content</string> + <string name="install_game_content_description">Install game updates or DLC</string> + <string name="install_game_content_failure">Error installing file(s) to NAND</string> + <string name="install_game_content_failure_description">Please ensure content(s) are valid and that the prod.keys file is installed.</string> + <string name="install_game_content_failure_base">Installation of base games isn\'t permitted in order to avoid possible conflicts.</string> + <string name="install_game_content_failure_file_extension">Only NSP and XCI content is supported. Please verify the game content(s) are valid.</string> + <string name="install_game_content_failed_count">%1$d installation error(s)</string> + <string name="install_game_content_success">Game content(s) installed successfully</string> + <string name="install_game_content_success_install">%1$d installed successfully</string> + <string name="install_game_content_success_overwrite">%1$d overwritten successfully</string> + <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string> <!-- About screen strings --> <string name="gaia_is_not_real">Gaia isn\'t real</string> @@ -149,10 +160,10 @@ <string name="set_custom_rtc">Set custom RTC</string> <!-- Graphics settings strings --> - <string name="renderer_api">API</string> <string name="renderer_accuracy">Accuracy level</string> <string name="renderer_resolution">Resolution (Handheld/Docked)</string> <string name="renderer_vsync">VSync mode</string> + <string name="renderer_screen_layout">Orientation</string> <string name="renderer_aspect_ratio">Aspect ratio</string> <string name="renderer_scaling_filter">Window adapting filter</string> <string name="renderer_anti_aliasing">Anti-aliasing method</string> @@ -160,12 +171,23 @@ <string name="renderer_force_max_clock_description">Forces the GPU to run at the maximum possible clocks (thermal constraints will still be applied).</string> <string name="renderer_asynchronous_shaders">Use asynchronous shaders</string> <string name="renderer_asynchronous_shaders_description">Compiles shaders asynchronously, reducing stutter but may introduce glitches.</string> - <string name="renderer_debug">Graphics debugging</string> - <string name="renderer_debug_description">Sets the graphics API to a slow debugging mode.</string> + <string name="renderer_reactive_flushing">Use reactive flushing</string> + <string name="renderer_reactive_flushing_description">Improves rendering accuracy in some games at the cost of performance.</string> <string name="use_disk_shader_cache">Disk shader cache</string> <string name="use_disk_shader_cache_description">Reduces stuttering by locally storing and loading generated shaders.</string> + <!-- Debug settings strings --> + <string name="cpu">CPU</string> + <string name="cpu_debug_mode">CPU Debugging</string> + <string name="cpu_debug_mode_description">Puts the CPU in a slow debugging mode.</string> + <string name="gpu">GPU</string> + <string name="renderer_api">API</string> + <string name="renderer_debug">Graphics debugging</string> + <string name="renderer_debug_description">Sets the graphics API to a slow debugging mode.</string> + <string name="fastmem">Fastmem</string> + <!-- Audio settings strings --> + <string name="audio_output_engine">Output engine</string> <string name="audio_volume">Volume</string> <string name="audio_volume_description">Specifies the volume of audio output.</string> @@ -184,6 +206,7 @@ <string name="learn_more">Learn more</string> <string name="auto">Auto</string> <string name="submit">Submit</string> + <string name="string_null">Null</string> <!-- GPU driver installation --> <string name="select_gpu_driver">Select GPU driver</string> @@ -249,6 +272,7 @@ <string name="fatal_error">Fatal Error</string> <string name="fatal_error_message">A fatal error occurred. Check the log for details.\nContinuing emulation may result in crashes and bugs.</string> <string name="performance_warning">Turning off this setting will significantly reduce emulation performance! For the best experience, it is recommended that you leave this setting enabled.</string> + <string name="device_memory_inadequate">Device RAM: %1$s\nRecommended: %2$s</string> <!-- Region Names --> <string name="region_japan">Japan</string> @@ -279,6 +303,15 @@ <string name="language_traditional_chinese">Traditional Chinese (正體中文)</string> <string name="language_brazilian_portuguese">Brazilian Portuguese (Português do Brasil)</string> + <!-- Memory Sizes --> + <string name="memory_byte">Byte</string> + <string name="memory_kilobyte">KB</string> + <string name="memory_megabyte">MB</string> + <string name="memory_gigabyte">GB</string> + <string name="memory_terabyte">TB</string> + <string name="memory_petabyte">PB</string> + <string name="memory_exabyte">EB</string> + <!-- Renderer APIs --> <string name="renderer_vulkan">Vulkan</string> <string name="renderer_none">None</string> @@ -315,6 +348,11 @@ <string name="anti_aliasing_fxaa">FXAA</string> <string name="anti_aliasing_smaa">SMAA</string> + <!-- Screen Layouts --> + <string name="screen_layout_landscape">Landscape</string> + <string name="screen_layout_portrait">Portrait</string> + <string name="screen_layout_auto">Auto</string> + <!-- Aspect Ratios --> <string name="ratio_default">Default (16:9)</string> <string name="ratio_force_four_three">Force 4:3</string> @@ -349,10 +387,21 @@ <string name="theme_mode_light">Light</string> <string name="theme_mode_dark">Dark</string> + <!-- Audio output engines --> + <string name="cubeb">cubeb</string> + <!-- Black backgrounds theme --> <string name="use_black_backgrounds">Black backgrounds</string> <string name="use_black_backgrounds_description">When using the dark theme, apply black backgrounds.</string> + <!-- Picture-In-Picture --> + <string name="picture_in_picture">Picture in Picture</string> + <string name="picture_in_picture_description">Minimize window when placed in the background</string> + <string name="pause">Pause</string> + <string name="play">Play</string> + <string name="mute">Mute</string> + <string name="unmute">Unmute</string> + <!-- Licenses screen strings --> <string name="licenses">Licenses</string> <string name="license_fidelityfx_fsr" translatable="false">FidelityFX-FSR</string> diff --git a/src/android/app/src/main/res/xml/locales_config.xml b/src/android/app/src/main/res/xml/locales_config.xml new file mode 100644 index 0000000000..51b88d9dc3 --- /dev/null +++ b/src/android/app/src/main/res/xml/locales_config.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<locale-config xmlns:android="http://schemas.android.com/apk/res/android"> + <locale android:name="en" /> <!-- English (default) --> + <locale android:name="de" /> <!-- German --> + <locale android:name="es" /> <!-- Spanish --> + <locale android:name="fr" /> <!-- French --> + <locale android:name="it" /> <!-- Italian --> + <locale android:name="ja" /> <!-- Japanese --> + <locale android:name="nb" /> <!-- Norwegian Bokmal --> + <locale android:name="pl" /> <!-- Polish --> + <locale android:name="pt-rBR" /> <!-- Portuguese (Brazil) --> + <locale android:name="pt-RPT" /> <!-- Portuguese (Portugal) --> + <locale android:name="ru" /> <!-- Russian --> + <locale android:name="uk" /> <!-- Ukranian --> + <locale android:name="zh-rCN" /> <!-- Chinese (China) --> + <locale android:name="zh-rTW" /> <!-- Chinese (Taiwan) --> +</locale-config> diff --git a/src/android/build.gradle.kts b/src/android/build.gradle.kts index e19e8ce58e..80f370c164 100644 --- a/src/android/build.gradle.kts +++ b/src/android/build.gradle.kts @@ -11,3 +11,12 @@ plugins { tasks.register("clean").configure { delete(rootProject.buildDir) } + +buildscript { + repositories { + google() + } + dependencies { + classpath("androidx.navigation:navigation-safe-args-gradle-plugin:2.6.0") + } +} diff --git a/src/audio_core/device/audio_buffers.h b/src/audio_core/device/audio_buffers.h index 15082f6c62..5d8ed0ef7e 100644 --- a/src/audio_core/device/audio_buffers.h +++ b/src/audio_core/device/audio_buffers.h @@ -7,6 +7,7 @@ #include <mutex> #include <span> #include <vector> +#include <boost/container/static_vector.hpp> #include "audio_buffer.h" #include "audio_core/device/device_session.h" @@ -48,7 +49,7 @@ public: * * @param out_buffers - The buffers which were registered. */ - void RegisterBuffers(std::vector<AudioBuffer>& out_buffers) { + void RegisterBuffers(boost::container::static_vector<AudioBuffer, N>& out_buffers) { std::scoped_lock l{lock}; const s32 to_register{std::min(std::min(appended_count, BufferAppendLimit), BufferAppendLimit - registered_count)}; @@ -162,7 +163,8 @@ public: * @param max_buffers - Maximum number of buffers to released. * @return The number of buffers released. */ - u32 GetRegisteredAppendedBuffers(std::vector<AudioBuffer>& buffers_flushed, u32 max_buffers) { + u32 GetRegisteredAppendedBuffers( + boost::container::static_vector<AudioBuffer, N>& buffers_flushed, u32 max_buffers) { std::scoped_lock l{lock}; if (registered_count + appended_count == 0) { return 0; @@ -270,7 +272,7 @@ public: */ bool FlushBuffers(u32& buffers_released) { std::scoped_lock l{lock}; - std::vector<AudioBuffer> buffers_flushed{}; + boost::container::static_vector<AudioBuffer, N> buffers_flushed{}; buffers_released = GetRegisteredAppendedBuffers(buffers_flushed, append_limit); diff --git a/src/audio_core/device/device_session.cpp b/src/audio_core/device/device_session.cpp index b5c0ef0e6f..86811fcb8f 100644 --- a/src/audio_core/device/device_session.cpp +++ b/src/audio_core/device/device_session.cpp @@ -79,7 +79,7 @@ void DeviceSession::ClearBuffers() { } } -void DeviceSession::AppendBuffers(std::span<const AudioBuffer> buffers) const { +void DeviceSession::AppendBuffers(std::span<const AudioBuffer> buffers) { for (const auto& buffer : buffers) { Sink::SinkBuffer new_buffer{ .frames = buffer.size / (channel_count * sizeof(s16)), @@ -88,13 +88,13 @@ void DeviceSession::AppendBuffers(std::span<const AudioBuffer> buffers) const { .consumed = false, }; + tmp_samples.resize_destructive(buffer.size / sizeof(s16)); if (type == Sink::StreamType::In) { - std::vector<s16> samples{}; - stream->AppendBuffer(new_buffer, samples); + stream->AppendBuffer(new_buffer, tmp_samples); } else { - std::vector<s16> samples(buffer.size / sizeof(s16)); - system.ApplicationMemory().ReadBlockUnsafe(buffer.samples, samples.data(), buffer.size); - stream->AppendBuffer(new_buffer, samples); + system.ApplicationMemory().ReadBlockUnsafe(buffer.samples, tmp_samples.data(), + buffer.size); + stream->AppendBuffer(new_buffer, tmp_samples); } } } diff --git a/src/audio_core/device/device_session.h b/src/audio_core/device/device_session.h index 75f766c68f..7d52f362d9 100644 --- a/src/audio_core/device/device_session.h +++ b/src/audio_core/device/device_session.h @@ -10,6 +10,7 @@ #include "audio_core/common/common.h" #include "audio_core/sink/sink.h" +#include "common/scratch_buffer.h" #include "core/hle/service/audio/errors.h" namespace Core { @@ -62,7 +63,7 @@ public: * * @param buffers - The buffers to play. */ - void AppendBuffers(std::span<const AudioBuffer> buffers) const; + void AppendBuffers(std::span<const AudioBuffer> buffers); /** * (Audio In only) Pop samples from the backend, and write them back to this buffer's address. @@ -146,8 +147,8 @@ private: std::shared_ptr<Core::Timing::EventType> thread_event; /// Is this session initialised? bool initialized{}; - /// Buffer queue - std::vector<AudioBuffer> buffer_queue{}; + /// Temporary sample buffer + Common::ScratchBuffer<s16> tmp_samples{}; }; } // namespace AudioCore diff --git a/src/audio_core/in/audio_in_system.cpp b/src/audio_core/in/audio_in_system.cpp index e23e51758c..5791291213 100644 --- a/src/audio_core/in/audio_in_system.cpp +++ b/src/audio_core/in/audio_in_system.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include <mutex> + #include "audio_core/audio_event.h" #include "audio_core/audio_manager.h" #include "audio_core/in/audio_in_system.h" @@ -89,7 +90,7 @@ Result System::Start() { session->Start(); state = State::Started; - std::vector<AudioBuffer> buffers_to_flush{}; + boost::container::static_vector<AudioBuffer, BufferCount> buffers_to_flush{}; buffers.RegisterBuffers(buffers_to_flush); session->AppendBuffers(buffers_to_flush); session->SetRingSize(static_cast<u32>(buffers_to_flush.size())); @@ -134,7 +135,7 @@ bool System::AppendBuffer(const AudioInBuffer& buffer, const u64 tag) { void System::RegisterBuffers() { if (state == State::Started) { - std::vector<AudioBuffer> registered_buffers{}; + boost::container::static_vector<AudioBuffer, BufferCount> registered_buffers{}; buffers.RegisterBuffers(registered_buffers); session->AppendBuffers(registered_buffers); } diff --git a/src/audio_core/out/audio_out_system.cpp b/src/audio_core/out/audio_out_system.cpp index bd13f72194..0adf64bd34 100644 --- a/src/audio_core/out/audio_out_system.cpp +++ b/src/audio_core/out/audio_out_system.cpp @@ -89,7 +89,7 @@ Result System::Start() { session->Start(); state = State::Started; - std::vector<AudioBuffer> buffers_to_flush{}; + boost::container::static_vector<AudioBuffer, BufferCount> buffers_to_flush{}; buffers.RegisterBuffers(buffers_to_flush); session->AppendBuffers(buffers_to_flush); session->SetRingSize(static_cast<u32>(buffers_to_flush.size())); @@ -134,7 +134,7 @@ bool System::AppendBuffer(const AudioOutBuffer& buffer, u64 tag) { void System::RegisterBuffers() { if (state == State::Started) { - std::vector<AudioBuffer> registered_buffers{}; + boost::container::static_vector<AudioBuffer, BufferCount> registered_buffers{}; buffers.RegisterBuffers(registered_buffers); session->AppendBuffers(registered_buffers); } diff --git a/src/audio_core/renderer/adsp/adsp.cpp b/src/audio_core/renderer/adsp/adsp.cpp index 74772fc50c..b1db31e931 100644 --- a/src/audio_core/renderer/adsp/adsp.cpp +++ b/src/audio_core/renderer/adsp/adsp.cpp @@ -7,7 +7,6 @@ #include "common/logging/log.h" #include "core/core.h" #include "core/core_timing.h" -#include "core/core_timing_util.h" #include "core/memory.h" namespace AudioCore::AudioRenderer::ADSP { diff --git a/src/audio_core/renderer/adsp/audio_renderer.cpp b/src/audio_core/renderer/adsp/audio_renderer.cpp index 8bc39f9f9e..9ca716b604 100644 --- a/src/audio_core/renderer/adsp/audio_renderer.cpp +++ b/src/audio_core/renderer/adsp/audio_renderer.cpp @@ -13,7 +13,6 @@ #include "common/thread.h" #include "core/core.h" #include "core/core_timing.h" -#include "core/core_timing_util.h" MICROPROFILE_DEFINE(Audio_Renderer, "Audio", "DSP", MP_RGB(60, 19, 97)); @@ -144,6 +143,7 @@ void AudioRenderer::ThreadFunc(std::stop_token stop_token) { mailbox->ADSPSendMessage(RenderMessage::AudioRenderer_InitializeOK); + // 0.12 seconds (2304000 / 19200000) constexpr u64 max_process_time{2'304'000ULL}; while (!stop_token.stop_requested()) { @@ -184,8 +184,7 @@ void AudioRenderer::ThreadFunc(std::stop_token stop_token) { u64 max_time{max_process_time}; if (index == 1 && command_buffer.applet_resource_user_id == mailbox->GetCommandBuffer(0).applet_resource_user_id) { - max_time = max_process_time - - Core::Timing::CyclesToNs(render_times_taken[0]).count(); + max_time = max_process_time - render_times_taken[0]; if (render_times_taken[0] > max_process_time) { max_time = 0; } diff --git a/src/audio_core/renderer/adsp/command_list_processor.cpp b/src/audio_core/renderer/adsp/command_list_processor.cpp index 7a300d2162..3a0f1ae389 100644 --- a/src/audio_core/renderer/adsp/command_list_processor.cpp +++ b/src/audio_core/renderer/adsp/command_list_processor.cpp @@ -9,7 +9,6 @@ #include "common/settings.h" #include "core/core.h" #include "core/core_timing.h" -#include "core/core_timing_util.h" #include "core/memory.h" namespace AudioCore::AudioRenderer::ADSP { diff --git a/src/audio_core/renderer/command/data_source/decode.cpp b/src/audio_core/renderer/command/data_source/decode.cpp index ff5d31bd6f..f459332034 100644 --- a/src/audio_core/renderer/command/data_source/decode.cpp +++ b/src/audio_core/renderer/command/data_source/decode.cpp @@ -8,6 +8,7 @@ #include "audio_core/renderer/command/resample/resample.h" #include "common/fixed_point.h" #include "common/logging/log.h" +#include "common/scratch_buffer.h" #include "core/memory.h" namespace AudioCore::AudioRenderer { @@ -27,6 +28,7 @@ constexpr std::array<u8, 3> PitchBySrcQuality = {4, 8, 4}; template <typename T> static u32 DecodePcm(Core::Memory::Memory& memory, std::span<s16> out_buffer, const DecodeArg& req) { + std::array<T, TempBufferSize> tmp_samples{}; constexpr s32 min{std::numeric_limits<s16>::min()}; constexpr s32 max{std::numeric_limits<s16>::max()}; @@ -49,18 +51,17 @@ static u32 DecodePcm(Core::Memory::Memory& memory, std::span<s16> out_buffer, const u64 size{channel_count * samples_to_decode}; const u64 size_bytes{size * sizeof(T)}; - std::vector<T> samples(size); - memory.ReadBlockUnsafe(source, samples.data(), size_bytes); + memory.ReadBlockUnsafe(source, tmp_samples.data(), size_bytes); if constexpr (std::is_floating_point_v<T>) { for (u32 i = 0; i < samples_to_decode; i++) { - auto sample{static_cast<s32>(samples[i * channel_count + req.target_channel] * + auto sample{static_cast<s32>(tmp_samples[i * channel_count + req.target_channel] * std::numeric_limits<s16>::max())}; out_buffer[i] = static_cast<s16>(std::clamp(sample, min, max)); } } else { for (u32 i = 0; i < samples_to_decode; i++) { - out_buffer[i] = samples[i * channel_count + req.target_channel]; + out_buffer[i] = tmp_samples[i * channel_count + req.target_channel]; } } } break; @@ -73,17 +74,16 @@ static u32 DecodePcm(Core::Memory::Memory& memory, std::span<s16> out_buffer, } const VAddr source{req.buffer + ((req.start_offset + req.offset) * sizeof(T))}; - std::vector<T> samples(samples_to_decode); - memory.ReadBlockUnsafe(source, samples.data(), samples_to_decode * sizeof(T)); + memory.ReadBlockUnsafe(source, tmp_samples.data(), samples_to_decode * sizeof(T)); if constexpr (std::is_floating_point_v<T>) { for (u32 i = 0; i < samples_to_decode; i++) { - auto sample{static_cast<s32>(samples[i * channel_count + req.target_channel] * + auto sample{static_cast<s32>(tmp_samples[i * channel_count + req.target_channel] * std::numeric_limits<s16>::max())}; out_buffer[i] = static_cast<s16>(std::clamp(sample, min, max)); } } else { - std::memcpy(out_buffer.data(), samples.data(), samples_to_decode * sizeof(s16)); + std::memcpy(out_buffer.data(), tmp_samples.data(), samples_to_decode * sizeof(s16)); } break; } @@ -101,6 +101,7 @@ static u32 DecodePcm(Core::Memory::Memory& memory, std::span<s16> out_buffer, */ static u32 DecodeAdpcm(Core::Memory::Memory& memory, std::span<s16> out_buffer, const DecodeArg& req) { + std::array<u8, TempBufferSize> wavebuffer{}; constexpr u32 SamplesPerFrame{14}; constexpr u32 NibblesPerFrame{16}; @@ -138,9 +139,7 @@ static u32 DecodeAdpcm(Core::Memory::Memory& memory, std::span<s16> out_buffer, } const auto size{std::max((samples_to_process / 8U) * SamplesPerFrame, 8U)}; - std::vector<u8> wavebuffer(size); - memory.ReadBlockUnsafe(req.buffer + position_in_frame / 2, wavebuffer.data(), - wavebuffer.size()); + memory.ReadBlockUnsafe(req.buffer + position_in_frame / 2, wavebuffer.data(), size); auto context{req.adpcm_context}; auto header{context->header}; @@ -258,7 +257,7 @@ void DecodeFromWaveBuffers(Core::Memory::Memory& memory, const DecodeFromWaveBuf u32 offset{voice_state.offset}; auto output_buffer{args.output}; - std::vector<s16> temp_buffer(TempBufferSize, 0); + std::array<s16, TempBufferSize> temp_buffer{}; while (remaining_sample_count > 0) { const auto samples_to_write{std::min(remaining_sample_count, max_remaining_sample_count)}; diff --git a/src/audio_core/renderer/command/effect/compressor.cpp b/src/audio_core/renderer/command/effect/compressor.cpp index 7229618e87..ee9b68d5bd 100644 --- a/src/audio_core/renderer/command/effect/compressor.cpp +++ b/src/audio_core/renderer/command/effect/compressor.cpp @@ -44,8 +44,8 @@ static void InitializeCompressorEffect(const CompressorInfo::ParameterVersion2& static void ApplyCompressorEffect(const CompressorInfo::ParameterVersion2& params, CompressorInfo::State& state, bool enabled, - std::vector<std::span<const s32>> input_buffers, - std::vector<std::span<s32>> output_buffers, u32 sample_count) { + std::span<std::span<const s32>> input_buffers, + std::span<std::span<s32>> output_buffers, u32 sample_count) { if (enabled) { auto state_00{state.unk_00}; auto state_04{state.unk_04}; @@ -124,8 +124,8 @@ void CompressorCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& } void CompressorCommand::Process(const ADSP::CommandListProcessor& processor) { - std::vector<std::span<const s32>> input_buffers(parameter.channel_count); - std::vector<std::span<s32>> output_buffers(parameter.channel_count); + std::array<std::span<const s32>, MaxChannels> input_buffers{}; + std::array<std::span<s32>, MaxChannels> output_buffers{}; for (s16 i = 0; i < parameter.channel_count; i++) { input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count, diff --git a/src/audio_core/renderer/command/effect/delay.cpp b/src/audio_core/renderer/command/effect/delay.cpp index a4e408d403..e536cbb1e4 100644 --- a/src/audio_core/renderer/command/effect/delay.cpp +++ b/src/audio_core/renderer/command/effect/delay.cpp @@ -51,7 +51,7 @@ static void InitializeDelayEffect(const DelayInfo::ParameterVersion1& params, state.delay_lines[channel].sample_count_max = sample_count_max.to_int_floor(); state.delay_lines[channel].sample_count = sample_count.to_int_floor(); state.delay_lines[channel].buffer.resize(state.delay_lines[channel].sample_count, 0); - if (state.delay_lines[channel].buffer.size() == 0) { + if (state.delay_lines[channel].sample_count == 0) { state.delay_lines[channel].buffer.push_back(0); } state.delay_lines[channel].buffer_pos = 0; @@ -74,8 +74,8 @@ static void InitializeDelayEffect(const DelayInfo::ParameterVersion1& params, */ template <size_t NumChannels> static void ApplyDelay(const DelayInfo::ParameterVersion1& params, DelayInfo::State& state, - std::vector<std::span<const s32>>& inputs, - std::vector<std::span<s32>>& outputs, const u32 sample_count) { + std::span<std::span<const s32>> inputs, std::span<std::span<s32>> outputs, + const u32 sample_count) { for (u32 sample_index = 0; sample_index < sample_count; sample_index++) { std::array<Common::FixedPoint<50, 14>, NumChannels> input_samples{}; for (u32 channel = 0; channel < NumChannels; channel++) { @@ -153,8 +153,8 @@ static void ApplyDelay(const DelayInfo::ParameterVersion1& params, DelayInfo::St * @param sample_count - Number of samples to process. */ static void ApplyDelayEffect(const DelayInfo::ParameterVersion1& params, DelayInfo::State& state, - const bool enabled, std::vector<std::span<const s32>>& inputs, - std::vector<std::span<s32>>& outputs, const u32 sample_count) { + const bool enabled, std::span<std::span<const s32>> inputs, + std::span<std::span<s32>> outputs, const u32 sample_count) { if (!IsChannelCountValid(params.channel_count)) { LOG_ERROR(Service_Audio, "Invalid delay channels {}", params.channel_count); @@ -208,8 +208,8 @@ void DelayCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& proce } void DelayCommand::Process(const ADSP::CommandListProcessor& processor) { - std::vector<std::span<const s32>> input_buffers(parameter.channel_count); - std::vector<std::span<s32>> output_buffers(parameter.channel_count); + std::array<std::span<const s32>, MaxChannels> input_buffers{}; + std::array<std::span<s32>, MaxChannels> output_buffers{}; for (s16 i = 0; i < parameter.channel_count; i++) { input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count, diff --git a/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp b/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp index 27d8b98445..d2bfb67cc0 100644 --- a/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp +++ b/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp @@ -408,8 +408,8 @@ void I3dl2ReverbCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& } void I3dl2ReverbCommand::Process(const ADSP::CommandListProcessor& processor) { - std::vector<std::span<const s32>> input_buffers(parameter.channel_count); - std::vector<std::span<s32>> output_buffers(parameter.channel_count); + std::array<std::span<const s32>, MaxChannels> input_buffers{}; + std::array<std::span<s32>, MaxChannels> output_buffers{}; for (u32 i = 0; i < parameter.channel_count; i++) { input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count, diff --git a/src/audio_core/renderer/command/effect/light_limiter.cpp b/src/audio_core/renderer/command/effect/light_limiter.cpp index e8fb0e2fc7..4161a98217 100644 --- a/src/audio_core/renderer/command/effect/light_limiter.cpp +++ b/src/audio_core/renderer/command/effect/light_limiter.cpp @@ -47,8 +47,8 @@ static void InitializeLightLimiterEffect(const LightLimiterInfo::ParameterVersio */ static void ApplyLightLimiterEffect(const LightLimiterInfo::ParameterVersion2& params, LightLimiterInfo::State& state, const bool enabled, - std::vector<std::span<const s32>>& inputs, - std::vector<std::span<s32>>& outputs, const u32 sample_count, + std::span<std::span<const s32>> inputs, + std::span<std::span<s32>> outputs, const u32 sample_count, LightLimiterInfo::StatisticsInternal* statistics) { constexpr s64 min{std::numeric_limits<s32>::min()}; constexpr s64 max{std::numeric_limits<s32>::max()}; @@ -147,8 +147,8 @@ void LightLimiterVersion1Command::Dump([[maybe_unused]] const ADSP::CommandListP } void LightLimiterVersion1Command::Process(const ADSP::CommandListProcessor& processor) { - std::vector<std::span<const s32>> input_buffers(parameter.channel_count); - std::vector<std::span<s32>> output_buffers(parameter.channel_count); + std::array<std::span<const s32>, MaxChannels> input_buffers{}; + std::array<std::span<s32>, MaxChannels> output_buffers{}; for (u32 i = 0; i < parameter.channel_count; i++) { input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count, @@ -190,8 +190,8 @@ void LightLimiterVersion2Command::Dump([[maybe_unused]] const ADSP::CommandListP } void LightLimiterVersion2Command::Process(const ADSP::CommandListProcessor& processor) { - std::vector<std::span<const s32>> input_buffers(parameter.channel_count); - std::vector<std::span<s32>> output_buffers(parameter.channel_count); + std::array<std::span<const s32>, MaxChannels> input_buffers{}; + std::array<std::span<s32>, MaxChannels> output_buffers{}; for (u32 i = 0; i < parameter.channel_count; i++) { input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count, diff --git a/src/audio_core/renderer/command/effect/reverb.cpp b/src/audio_core/renderer/command/effect/reverb.cpp index 8b9b65214a..fc2f15a5ee 100644 --- a/src/audio_core/renderer/command/effect/reverb.cpp +++ b/src/audio_core/renderer/command/effect/reverb.cpp @@ -250,8 +250,8 @@ static Common::FixedPoint<50, 14> Axfx2AllPassTick(ReverbInfo::ReverbDelayLine& */ template <size_t NumChannels> static void ApplyReverbEffect(const ReverbInfo::ParameterVersion2& params, ReverbInfo::State& state, - std::vector<std::span<const s32>>& inputs, - std::vector<std::span<s32>>& outputs, const u32 sample_count) { + std::span<std::span<const s32>> inputs, + std::span<std::span<s32>> outputs, const u32 sample_count) { static constexpr std::array<u8, ReverbInfo::MaxDelayTaps> OutTapIndexes1Ch{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; @@ -369,8 +369,8 @@ static void ApplyReverbEffect(const ReverbInfo::ParameterVersion2& params, Rever * @param sample_count - Number of samples to process. */ static void ApplyReverbEffect(const ReverbInfo::ParameterVersion2& params, ReverbInfo::State& state, - const bool enabled, std::vector<std::span<const s32>>& inputs, - std::vector<std::span<s32>>& outputs, const u32 sample_count) { + const bool enabled, std::span<std::span<const s32>> inputs, + std::span<std::span<s32>> outputs, const u32 sample_count) { if (enabled) { switch (params.channel_count) { case 0: @@ -412,8 +412,8 @@ void ReverbCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& proc } void ReverbCommand::Process(const ADSP::CommandListProcessor& processor) { - std::vector<std::span<const s32>> input_buffers(parameter.channel_count); - std::vector<std::span<s32>> output_buffers(parameter.channel_count); + std::array<std::span<const s32>, MaxChannels> input_buffers{}; + std::array<std::span<s32>, MaxChannels> output_buffers{}; for (u32 i = 0; i < parameter.channel_count; i++) { input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count, diff --git a/src/audio_core/renderer/command/performance/performance.cpp b/src/audio_core/renderer/command/performance/performance.cpp index 985958b039..4a881547f2 100644 --- a/src/audio_core/renderer/command/performance/performance.cpp +++ b/src/audio_core/renderer/command/performance/performance.cpp @@ -5,7 +5,6 @@ #include "audio_core/renderer/command/performance/performance.h" #include "core/core.h" #include "core/core_timing.h" -#include "core/core_timing_util.h" namespace AudioCore::AudioRenderer { @@ -18,20 +17,18 @@ void PerformanceCommand::Process(const ADSP::CommandListProcessor& processor) { auto base{entry_address.translated_address}; if (state == PerformanceState::Start) { auto start_time_ptr{reinterpret_cast<u32*>(base + entry_address.entry_start_time_offset)}; - *start_time_ptr = static_cast<u32>( - Core::Timing::CyclesToUs(processor.system->CoreTiming().GetClockTicks() - - processor.start_time - processor.current_processing_time) - .count()); + *start_time_ptr = + static_cast<u32>(processor.system->CoreTiming().GetClockTicks() - processor.start_time - + processor.current_processing_time); } else if (state == PerformanceState::Stop) { auto processed_time_ptr{ reinterpret_cast<u32*>(base + entry_address.entry_processed_time_offset)}; auto entry_count_ptr{ reinterpret_cast<u32*>(base + entry_address.header_entry_count_offset)}; - *processed_time_ptr = static_cast<u32>( - Core::Timing::CyclesToUs(processor.system->CoreTiming().GetClockTicks() - - processor.start_time - processor.current_processing_time) - .count()); + *processed_time_ptr = + static_cast<u32>(processor.system->CoreTiming().GetClockTicks() - processor.start_time - + processor.current_processing_time); (*entry_count_ptr)++; } } diff --git a/src/audio_core/renderer/command/sink/circular_buffer.cpp b/src/audio_core/renderer/command/sink/circular_buffer.cpp index ded5afc94f..e2ce597923 100644 --- a/src/audio_core/renderer/command/sink/circular_buffer.cpp +++ b/src/audio_core/renderer/command/sink/circular_buffer.cpp @@ -24,7 +24,7 @@ void CircularBufferSinkCommand::Process(const ADSP::CommandListProcessor& proces constexpr s32 min{std::numeric_limits<s16>::min()}; constexpr s32 max{std::numeric_limits<s16>::max()}; - std::vector<s16> output(processor.sample_count); + std::array<s16, TargetSampleCount * MaxChannels> output{}; for (u32 channel = 0; channel < input_count; channel++) { auto input{processor.mix_buffers.subspan(inputs[channel] * processor.sample_count, processor.sample_count)}; @@ -33,7 +33,7 @@ void CircularBufferSinkCommand::Process(const ADSP::CommandListProcessor& proces } processor.memory->WriteBlockUnsafe(address + pos, output.data(), - output.size() * sizeof(s16)); + processor.sample_count * sizeof(s16)); pos += static_cast<u32>(processor.sample_count * sizeof(s16)); if (pos >= size) { pos = 0; diff --git a/src/audio_core/renderer/command/sink/device.cpp b/src/audio_core/renderer/command/sink/device.cpp index e88372a75e..5f74dd7adb 100644 --- a/src/audio_core/renderer/command/sink/device.cpp +++ b/src/audio_core/renderer/command/sink/device.cpp @@ -33,8 +33,7 @@ void DeviceSinkCommand::Process(const ADSP::CommandListProcessor& processor) { .consumed{false}, }; - std::vector<s16> samples(out_buffer.frames * input_count); - + std::array<s16, TargetSampleCount * MaxChannels> samples{}; for (u32 channel = 0; channel < input_count; channel++) { const auto offset{inputs[channel] * out_buffer.frames}; @@ -45,7 +44,7 @@ void DeviceSinkCommand::Process(const ADSP::CommandListProcessor& processor) { } out_buffer.tag = reinterpret_cast<u64>(samples.data()); - stream->AppendBuffer(out_buffer, samples); + stream->AppendBuffer(out_buffer, {samples.data(), out_buffer.frames * input_count}); if (stream->IsPaused()) { stream->Start(); diff --git a/src/audio_core/renderer/mix/mix_context.cpp b/src/audio_core/renderer/mix/mix_context.cpp index 35b748ede8..3a18ae7c2b 100644 --- a/src/audio_core/renderer/mix/mix_context.cpp +++ b/src/audio_core/renderer/mix/mix_context.cpp @@ -125,10 +125,10 @@ bool MixContext::TSortInfo(const SplitterContext& splitter_context) { return false; } - std::vector<s32> sorted_results{node_states.GetSortedResuls()}; - const auto result_size{std::min(count, static_cast<s32>(sorted_results.size()))}; + auto sorted_results{node_states.GetSortedResuls()}; + const auto result_size{std::min(count, static_cast<s32>(sorted_results.second))}; for (s32 i = 0; i < result_size; i++) { - sorted_mix_infos[i] = &mix_infos[sorted_results[i]]; + sorted_mix_infos[i] = &mix_infos[sorted_results.first[i]]; } CalcMixBufferOffset(); diff --git a/src/audio_core/renderer/nodes/node_states.cpp b/src/audio_core/renderer/nodes/node_states.cpp index 1821a51e66..b7a44a54cf 100644 --- a/src/audio_core/renderer/nodes/node_states.cpp +++ b/src/audio_core/renderer/nodes/node_states.cpp @@ -134,8 +134,8 @@ u32 NodeStates::GetNodeCount() const { return node_count; } -std::vector<s32> NodeStates::GetSortedResuls() const { - return {results.rbegin(), results.rbegin() + result_pos}; +std::pair<std::span<u32>::reverse_iterator, size_t> NodeStates::GetSortedResuls() const { + return {results.rbegin(), result_pos}; } } // namespace AudioCore::AudioRenderer diff --git a/src/audio_core/renderer/nodes/node_states.h b/src/audio_core/renderer/nodes/node_states.h index 94b1d1254e..e768cd4b54 100644 --- a/src/audio_core/renderer/nodes/node_states.h +++ b/src/audio_core/renderer/nodes/node_states.h @@ -175,7 +175,7 @@ public: * * @return Vector of nodes in reverse order. */ - std::vector<s32> GetSortedResuls() const; + std::pair<std::span<u32>::reverse_iterator, size_t> GetSortedResuls() const; private: /// Number of nodes in the graph diff --git a/src/audio_core/renderer/system.cpp b/src/audio_core/renderer/system.cpp index 53b258c4f4..a23627472f 100644 --- a/src/audio_core/renderer/system.cpp +++ b/src/audio_core/renderer/system.cpp @@ -444,6 +444,7 @@ Result System::Update(std::span<const u8> input, std::span<u8> performance, std: std::scoped_lock l{lock}; const auto start_time{core.CoreTiming().GetClockTicks()}; + std::memset(output.data(), 0, output.size()); InfoUpdater info_updater(input, output, process_handle, behavior); diff --git a/src/audio_core/sink/null_sink.h b/src/audio_core/sink/null_sink.h index 1215d3cd25..b6b43c93e1 100644 --- a/src/audio_core/sink/null_sink.h +++ b/src/audio_core/sink/null_sink.h @@ -20,7 +20,7 @@ public: explicit NullSinkStreamImpl(Core::System& system_, StreamType type_) : SinkStream{system_, type_} {} ~NullSinkStreamImpl() override {} - void AppendBuffer(SinkBuffer&, std::vector<s16>&) override {} + void AppendBuffer(SinkBuffer&, std::span<s16>) override {} std::vector<s16> ReleaseBuffer(u64) override { return {}; } diff --git a/src/audio_core/sink/sink_stream.cpp b/src/audio_core/sink/sink_stream.cpp index f44fedfd5e..404dcd0e90 100644 --- a/src/audio_core/sink/sink_stream.cpp +++ b/src/audio_core/sink/sink_stream.cpp @@ -15,11 +15,10 @@ #include "common/settings.h" #include "core/core.h" #include "core/core_timing.h" -#include "core/core_timing_util.h" namespace AudioCore::Sink { -void SinkStream::AppendBuffer(SinkBuffer& buffer, std::vector<s16>& samples) { +void SinkStream::AppendBuffer(SinkBuffer& buffer, std::span<s16> samples) { if (type == StreamType::In) { queue.enqueue(buffer); queued_buffers++; @@ -67,15 +66,16 @@ void SinkStream::AppendBuffer(SinkBuffer& buffer, std::vector<s16>& samples) { static_cast<s16>(std::clamp(right_sample, min, max)); } - samples.resize(samples.size() / system_channels * device_channels); + samples = samples.subspan(0, samples.size() / system_channels * device_channels); } else if (system_channels == 2 && device_channels == 6) { // We need moar samples! Not all games will provide 6 channel audio. // TODO: Implement some upmixing here. Currently just passthrough, with other // channels left as silence. - std::vector<s16> new_samples(samples.size() / system_channels * device_channels, 0); + auto new_size = samples.size() / system_channels * device_channels; + tmp_samples.resize_destructive(new_size); - for (u32 read_index = 0, write_index = 0; read_index < samples.size(); + for (u32 read_index = 0, write_index = 0; read_index < new_size; read_index += system_channels, write_index += device_channels) { const auto left_sample{static_cast<s16>(std::clamp( static_cast<s32>( @@ -83,7 +83,7 @@ void SinkStream::AppendBuffer(SinkBuffer& buffer, std::vector<s16>& samples) { volume), min, max))}; - new_samples[write_index + static_cast<u32>(Channels::FrontLeft)] = left_sample; + tmp_samples[write_index + static_cast<u32>(Channels::FrontLeft)] = left_sample; const auto right_sample{static_cast<s16>(std::clamp( static_cast<s32>( @@ -91,9 +91,9 @@ void SinkStream::AppendBuffer(SinkBuffer& buffer, std::vector<s16>& samples) { volume), min, max))}; - new_samples[write_index + static_cast<u32>(Channels::FrontRight)] = right_sample; + tmp_samples[write_index + static_cast<u32>(Channels::FrontRight)] = right_sample; } - samples = std::move(new_samples); + samples = std::span<s16>(tmp_samples); } else if (volume != 1.0f) { for (u32 i = 0; i < samples.size(); i++) { diff --git a/src/audio_core/sink/sink_stream.h b/src/audio_core/sink/sink_stream.h index 41cbadc9cc..98d72ace18 100644 --- a/src/audio_core/sink/sink_stream.h +++ b/src/audio_core/sink/sink_stream.h @@ -16,6 +16,7 @@ #include "common/polyfill_thread.h" #include "common/reader_writer_queue.h" #include "common/ring_buffer.h" +#include "common/scratch_buffer.h" #include "common/thread.h" namespace Core { @@ -170,7 +171,7 @@ public: * @param buffer - Audio buffer information to be queued. * @param samples - The s16 samples to be queue for playback. */ - virtual void AppendBuffer(SinkBuffer& buffer, std::vector<s16>& samples); + virtual void AppendBuffer(SinkBuffer& buffer, std::span<s16> samples); /** * Release a buffer. Audio In only, will fill a buffer with recorded samples. @@ -255,6 +256,8 @@ private: /// Signalled when ring buffer entries are consumed std::condition_variable_any release_cv; std::mutex release_mutex; + /// Temporary buffer for appending samples when upmixing + Common::ScratchBuffer<s16> tmp_samples{}; }; using SinkStreamPtr = std::unique_ptr<SinkStream>; diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index efc4a9fe93..3adf13a3f4 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -172,6 +172,8 @@ if(ARCHITECTURE_x86_64) x64/cpu_wait.h x64/native_clock.cpp x64/native_clock.h + x64/rdtsc.cpp + x64/rdtsc.h x64/xbyak_abi.h x64/xbyak_util.h ) diff --git a/src/common/fs/fs.cpp b/src/common/fs/fs.cpp index e1716c62de..36e67c145c 100644 --- a/src/common/fs/fs.cpp +++ b/src/common/fs/fs.cpp @@ -3,6 +3,9 @@ #include "common/fs/file.h" #include "common/fs/fs.h" +#ifdef ANDROID +#include "common/fs/fs_android.h" +#endif #include "common/fs/path_util.h" #include "common/logging/log.h" @@ -433,7 +436,7 @@ void IterateDirEntries(const std::filesystem::path& path, const DirEntryCallable if (True(filter & DirEntryFilter::File) && entry.status().type() == fs::file_type::regular) { - if (!callback(entry.path())) { + if (!callback(entry)) { callback_error = true; break; } @@ -441,7 +444,7 @@ void IterateDirEntries(const std::filesystem::path& path, const DirEntryCallable if (True(filter & DirEntryFilter::Directory) && entry.status().type() == fs::file_type::directory) { - if (!callback(entry.path())) { + if (!callback(entry)) { callback_error = true; break; } @@ -490,7 +493,7 @@ void IterateDirEntriesRecursively(const std::filesystem::path& path, if (True(filter & DirEntryFilter::File) && entry.status().type() == fs::file_type::regular) { - if (!callback(entry.path())) { + if (!callback(entry)) { callback_error = true; break; } @@ -498,7 +501,7 @@ void IterateDirEntriesRecursively(const std::filesystem::path& path, if (True(filter & DirEntryFilter::Directory) && entry.status().type() == fs::file_type::directory) { - if (!callback(entry.path())) { + if (!callback(entry)) { callback_error = true; break; } @@ -525,15 +528,39 @@ void IterateDirEntriesRecursively(const std::filesystem::path& path, // Generic Filesystem Operations bool Exists(const fs::path& path) { +#ifdef ANDROID + if (Android::IsContentUri(path)) { + return Android::Exists(path); + } else { + return fs::exists(path); + } +#else return fs::exists(path); +#endif } bool IsFile(const fs::path& path) { +#ifdef ANDROID + if (Android::IsContentUri(path)) { + return !Android::IsDirectory(path); + } else { + return fs::is_regular_file(path); + } +#else return fs::is_regular_file(path); +#endif } bool IsDir(const fs::path& path) { +#ifdef ANDROID + if (Android::IsContentUri(path)) { + return Android::IsDirectory(path); + } else { + return fs::is_directory(path); + } +#else return fs::is_directory(path); +#endif } fs::path GetCurrentDir() { @@ -578,6 +605,12 @@ fs::file_type GetEntryType(const fs::path& path) { } u64 GetSize(const fs::path& path) { +#ifdef ANDROID + if (Android::IsContentUri(path)) { + return Android::GetSize(path); + } +#endif + std::error_code ec; const auto file_size = fs::file_size(path, ec); diff --git a/src/common/fs/fs_android.h b/src/common/fs/fs_android.h index bb8a526487..b441c2a128 100644 --- a/src/common/fs/fs_android.h +++ b/src/common/fs/fs_android.h @@ -12,7 +12,10 @@ "openContentUri", "(Ljava/lang/String;Ljava/lang/String;)I") #define ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(V) \ - V(GetSize, std::uint64_t, get_size, CallStaticLongMethod, "getSize", "(Ljava/lang/String;)J") + V(GetSize, std::uint64_t, get_size, CallStaticLongMethod, "getSize", "(Ljava/lang/String;)J") \ + V(IsDirectory, bool, is_directory, CallStaticBooleanMethod, "isDirectory", \ + "(Ljava/lang/String;)Z") \ + V(Exists, bool, file_exists, CallStaticBooleanMethod, "exists", "(Ljava/lang/String;)Z") namespace Common::FS::Android { diff --git a/src/common/fs/fs_types.h b/src/common/fs/fs_types.h index 5a4090c190..900f85d24e 100644 --- a/src/common/fs/fs_types.h +++ b/src/common/fs/fs_types.h @@ -66,6 +66,6 @@ DECLARE_ENUM_FLAG_OPERATORS(DirEntryFilter); * @returns A boolean value. * Return true to indicate whether the callback is successful, false otherwise. */ -using DirEntryCallable = std::function<bool(const std::filesystem::path& path)>; +using DirEntryCallable = std::function<bool(const std::filesystem::directory_entry& entry)>; } // namespace Common::FS diff --git a/src/common/input.h b/src/common/input.h index 66fb15f0a7..ea30770aee 100644 --- a/src/common/input.h +++ b/src/common/input.h @@ -86,7 +86,7 @@ enum class NfcState { NewAmiibo, WaitingForAmiibo, AmiiboRemoved, - NotAnAmiibo, + InvalidTagType, NotSupported, WrongDeviceState, WriteFailed, @@ -218,8 +218,22 @@ struct CameraStatus { }; struct NfcStatus { - NfcState state{}; - std::vector<u8> data{}; + NfcState state{NfcState::Unknown}; + u8 uuid_length; + u8 protocol; + u8 tag_type; + std::array<u8, 10> uuid; +}; + +struct MifareData { + u8 command; + u8 sector; + std::array<u8, 0x6> key; + std::array<u8, 0x10> data; +}; + +struct MifareRequest { + std::array<MifareData, 0x10> data; }; // List of buttons to be passed to Qt that can be translated @@ -294,7 +308,7 @@ struct CallbackStatus { BatteryStatus battery_status{}; VibrationStatus vibration_status{}; CameraFormat camera_status{CameraFormat::None}; - NfcState nfc_status{NfcState::Unknown}; + NfcStatus nfc_status{}; std::vector<u8> raw_data{}; }; @@ -356,9 +370,30 @@ public: return NfcState::NotSupported; } + virtual NfcState StartNfcPolling() { + return NfcState::NotSupported; + } + + virtual NfcState StopNfcPolling() { + return NfcState::NotSupported; + } + + virtual NfcState ReadAmiiboData([[maybe_unused]] std::vector<u8>& out_data) { + return NfcState::NotSupported; + } + virtual NfcState WriteNfcData([[maybe_unused]] const std::vector<u8>& data) { return NfcState::NotSupported; } + + virtual NfcState ReadMifareData([[maybe_unused]] const MifareRequest& request, + [[maybe_unused]] MifareRequest& out_data) { + return NfcState::NotSupported; + } + + virtual NfcState WriteMifareData([[maybe_unused]] const MifareRequest& request) { + return NfcState::NotSupported; + } }; /// An abstract class template for a factory that can create input devices. diff --git a/src/common/ring_buffer.h b/src/common/ring_buffer.h index 4c328ab446..416680d445 100644 --- a/src/common/ring_buffer.h +++ b/src/common/ring_buffer.h @@ -9,6 +9,7 @@ #include <cstddef> #include <cstring> #include <new> +#include <span> #include <type_traits> #include <vector> @@ -53,7 +54,7 @@ public: return push_count; } - std::size_t Push(const std::vector<T>& input) { + std::size_t Push(const std::span<T> input) { return Push(input.data(), input.size()); } diff --git a/src/common/scratch_buffer.h b/src/common/scratch_buffer.h index a69a5a7af9..6fe9079531 100644 --- a/src/common/scratch_buffer.h +++ b/src/common/scratch_buffer.h @@ -3,6 +3,9 @@ #pragma once +#include <iterator> + +#include "common/concepts.h" #include "common/make_unique_for_overwrite.h" namespace Common { @@ -16,6 +19,12 @@ namespace Common { template <typename T> class ScratchBuffer { public: + using iterator = T*; + using const_iterator = const T*; + using value_type = T; + using element_type = T; + using iterator_category = std::contiguous_iterator_tag; + ScratchBuffer() = default; explicit ScratchBuffer(size_t initial_capacity) diff --git a/src/common/settings.cpp b/src/common/settings.cpp index 9ff3edabba..66dffc9bf4 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp @@ -1,12 +1,16 @@ // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#if __cpp_lib_chrono >= 201907L +#include <chrono> +#endif #include <string_view> #include "common/assert.h" #include "common/fs/path_util.h" #include "common/logging/log.h" #include "common/settings.h" +#include "common/time_zone.h" namespace Settings { @@ -14,18 +18,23 @@ Values values; static bool configuring_global = true; std::string GetTimeZoneString() { - static constexpr std::array timezones{ - "auto", "default", "CET", "CST6CDT", "Cuba", "EET", "Egypt", "Eire", - "EST", "EST5EDT", "GB", "GB-Eire", "GMT", "GMT+0", "GMT-0", "GMT0", - "Greenwich", "Hongkong", "HST", "Iceland", "Iran", "Israel", "Jamaica", "Japan", - "Kwajalein", "Libya", "MET", "MST", "MST7MDT", "Navajo", "NZ", "NZ-CHAT", - "Poland", "Portugal", "PRC", "PST8PDT", "ROC", "ROK", "Singapore", "Turkey", - "UCT", "Universal", "UTC", "W-SU", "WET", "Zulu", - }; - const auto time_zone_index = static_cast<std::size_t>(values.time_zone_index.GetValue()); - ASSERT(time_zone_index < timezones.size()); - return timezones[time_zone_index]; + ASSERT(time_zone_index < Common::TimeZone::GetTimeZoneStrings().size()); + + std::string location_name; + if (time_zone_index == 0) { // Auto +#if __cpp_lib_chrono >= 201907L + const struct std::chrono::tzdb& time_zone_data = std::chrono::get_tzdb(); + const std::chrono::time_zone* current_zone = time_zone_data.current_zone(); + std::string_view current_zone_name = current_zone->name(); + location_name = current_zone_name; +#else + location_name = Common::TimeZone::FindSystemTimeZone(); +#endif + } else { + location_name = Common::TimeZone::GetTimeZoneStrings()[time_zone_index]; + } + return location_name; } void LogSettings() { diff --git a/src/common/settings.h b/src/common/settings.h index 3c775d3d21..ae5ed93d8d 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -483,6 +483,7 @@ struct Values { AstcRecompression::Uncompressed, AstcRecompression::Uncompressed, AstcRecompression::Bc3, "astc_recompression"}; SwitchableSetting<bool> use_video_framerate{false, "use_video_framerate"}; + SwitchableSetting<bool> barrier_feedback_loops{true, "barrier_feedback_loops"}; SwitchableSetting<u8> bg_red{0, "bg_red"}; SwitchableSetting<u8> bg_green{0, "bg_green"}; diff --git a/src/common/steady_clock.cpp b/src/common/steady_clock.cpp index 7828591960..9415eed29b 100644 --- a/src/common/steady_clock.cpp +++ b/src/common/steady_clock.cpp @@ -28,13 +28,12 @@ static s64 GetSystemTimeNS() { // GetSystemTimePreciseAsFileTime returns the file time in 100ns units. static constexpr s64 Multiplier = 100; // Convert Windows epoch to Unix epoch. - static constexpr s64 WindowsEpochToUnixEpochNS = 0x19DB1DED53E8000LL; + static constexpr s64 WindowsEpochToUnixEpoch = 0x19DB1DED53E8000LL; FILETIME filetime; GetSystemTimePreciseAsFileTime(&filetime); return Multiplier * ((static_cast<s64>(filetime.dwHighDateTime) << 32) + - static_cast<s64>(filetime.dwLowDateTime)) - - WindowsEpochToUnixEpochNS; + static_cast<s64>(filetime.dwLowDateTime) - WindowsEpochToUnixEpoch); } #endif diff --git a/src/common/thread.h b/src/common/thread.h index 8ae169b4eb..c6976fb6cd 100644 --- a/src/common/thread.h +++ b/src/common/thread.h @@ -55,7 +55,7 @@ public: is_set = false; } - [[nodiscard]] bool IsSet() { + [[nodiscard]] bool IsSet() const { return is_set; } diff --git a/src/common/time_zone.cpp b/src/common/time_zone.cpp index 126836b019..d8d7896c61 100644 --- a/src/common/time_zone.cpp +++ b/src/common/time_zone.cpp @@ -2,14 +2,33 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include <chrono> +#include <exception> #include <iomanip> #include <sstream> +#include <stdexcept> +#include <fmt/chrono.h> +#include <fmt/core.h> #include "common/logging/log.h" +#include "common/settings.h" #include "common/time_zone.h" namespace Common::TimeZone { +// Time zone strings +constexpr std::array timezones{ + "GMT", "GMT", "CET", "CST6CDT", "Cuba", "EET", "Egypt", "Eire", + "EST", "EST5EDT", "GB", "GB-Eire", "GMT", "GMT+0", "GMT-0", "GMT0", + "Greenwich", "Hongkong", "HST", "Iceland", "Iran", "Israel", "Jamaica", "Japan", + "Kwajalein", "Libya", "MET", "MST", "MST7MDT", "Navajo", "NZ", "NZ-CHAT", + "Poland", "Portugal", "PRC", "PST8PDT", "ROC", "ROK", "Singapore", "Turkey", + "UCT", "Universal", "UTC", "W-SU", "WET", "Zulu", +}; + +const std::array<const char*, 46>& GetTimeZoneStrings() { + return timezones; +} + std::string GetDefaultTimeZone() { return "GMT"; } @@ -18,10 +37,7 @@ static std::string GetOsTimeZoneOffset() { const std::time_t t{std::time(nullptr)}; const std::tm tm{*std::localtime(&t)}; - std::stringstream ss; - ss << std::put_time(&tm, "%z"); // Get the current timezone offset, e.g. "-400", as a string - - return ss.str(); + return fmt::format("{:%z}", tm); } static int ConvertOsTimeZoneOffsetToInt(const std::string& timezone) { @@ -45,4 +61,43 @@ std::chrono::seconds GetCurrentOffsetSeconds() { return std::chrono::seconds{seconds}; } +// Key is [Hours * 100 + Minutes], multiplied by 100 if DST +const static std::map<s64, const char*> off_timezones = { + {530, "Asia/Calcutta"}, {930, "Australia/Darwin"}, {845, "Australia/Eucla"}, + {103000, "Australia/Adelaide"}, {1030, "Australia/Lord_Howe"}, {630, "Indian/Cocos"}, + {1245, "Pacific/Chatham"}, {134500, "Pacific/Chatham"}, {-330, "Canada/Newfoundland"}, + {-23000, "Canada/Newfoundland"}, {430, "Asia/Kabul"}, {330, "Asia/Tehran"}, + {43000, "Asia/Tehran"}, {545, "Asia/Kathmandu"}, {-930, "Asia/Marquesas"}, +}; + +std::string FindSystemTimeZone() { +#if defined(MINGW) + // MinGW has broken strftime -- https://sourceforge.net/p/mingw-w64/bugs/793/ + // e.g. fmt::format("{:%z}") -- returns "Eastern Daylight Time" when it should be "-0400" + return timezones[0]; +#else + const s64 seconds = static_cast<s64>(GetCurrentOffsetSeconds().count()); + + const s64 minutes = seconds / 60; + const s64 hours = minutes / 60; + + const s64 minutes_off = minutes - hours * 60; + + if (minutes_off != 0) { + const auto the_time = std::time(nullptr); + const struct std::tm& local = *std::localtime(&the_time); + const bool is_dst = local.tm_isdst != 0; + + const s64 tz_index = (hours * 100 + minutes_off) * (is_dst ? 100 : 1); + + try { + return off_timezones.at(tz_index); + } catch (std::out_of_range&) { + LOG_ERROR(Common, "Time zone {} not handled, defaulting to hour offset.", tz_index); + } + } + return fmt::format("Etc/GMT{:s}{:d}", hours > 0 ? "-" : "+", std::abs(hours)); +#endif +} + } // namespace Common::TimeZone diff --git a/src/common/time_zone.h b/src/common/time_zone.h index 99cae6ef2f..f574d5c046 100644 --- a/src/common/time_zone.h +++ b/src/common/time_zone.h @@ -3,15 +3,21 @@ #pragma once +#include <array> #include <chrono> #include <string> namespace Common::TimeZone { +[[nodiscard]] const std::array<const char*, 46>& GetTimeZoneStrings(); + /// Gets the default timezone, i.e. "GMT" [[nodiscard]] std::string GetDefaultTimeZone(); /// Gets the offset of the current timezone (from the default), in seconds [[nodiscard]] std::chrono::seconds GetCurrentOffsetSeconds(); +/// Searches time zone offsets for the closest offset to the system time zone +[[nodiscard]] std::string FindSystemTimeZone(); + } // namespace Common::TimeZone diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp index 817e71d52f..dc0dcbd687 100644 --- a/src/common/wall_clock.cpp +++ b/src/common/wall_clock.cpp @@ -2,88 +2,75 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "common/steady_clock.h" -#include "common/uint128.h" #include "common/wall_clock.h" #ifdef ARCHITECTURE_x86_64 #include "common/x64/cpu_detect.h" #include "common/x64/native_clock.h" +#include "common/x64/rdtsc.h" #endif namespace Common { class StandardWallClock final : public WallClock { public: - explicit StandardWallClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequency_) - : WallClock{emulated_cpu_frequency_, emulated_clock_frequency_, false}, - start_time{SteadyClock::Now()} {} + explicit StandardWallClock() : start_time{SteadyClock::Now()} {} - std::chrono::nanoseconds GetTimeNS() override { + std::chrono::nanoseconds GetTimeNS() const override { return SteadyClock::Now() - start_time; } - std::chrono::microseconds GetTimeUS() override { - return std::chrono::duration_cast<std::chrono::microseconds>(GetTimeNS()); + std::chrono::microseconds GetTimeUS() const override { + return static_cast<std::chrono::microseconds>(GetHostTicksElapsed() / NsToUsRatio::den); } - std::chrono::milliseconds GetTimeMS() override { - return std::chrono::duration_cast<std::chrono::milliseconds>(GetTimeNS()); + std::chrono::milliseconds GetTimeMS() const override { + return static_cast<std::chrono::milliseconds>(GetHostTicksElapsed() / NsToMsRatio::den); } - u64 GetClockCycles() override { - const u128 temp = Common::Multiply64Into128(GetTimeNS().count(), emulated_clock_frequency); - return Common::Divide128On32(temp, NS_RATIO).first; + u64 GetCNTPCT() const override { + return GetHostTicksElapsed() * NsToCNTPCTRatio::num / NsToCNTPCTRatio::den; } - u64 GetCPUCycles() override { - const u128 temp = Common::Multiply64Into128(GetTimeNS().count(), emulated_cpu_frequency); - return Common::Divide128On32(temp, NS_RATIO).first; + u64 GetGPUTick() const override { + return GetHostTicksElapsed() * NsToGPUTickRatio::num / NsToGPUTickRatio::den; } - void Pause([[maybe_unused]] bool is_paused) override { - // Do nothing in this clock type. + u64 GetHostTicksNow() const override { + return static_cast<u64>(SteadyClock::Now().time_since_epoch().count()); + } + + u64 GetHostTicksElapsed() const override { + return static_cast<u64>(GetTimeNS().count()); + } + + bool IsNative() const override { + return false; } private: SteadyClock::time_point start_time; }; +std::unique_ptr<WallClock> CreateOptimalClock() { #ifdef ARCHITECTURE_x86_64 - -std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency, - u64 emulated_clock_frequency) { const auto& caps = GetCPUCaps(); - u64 rtsc_frequency = 0; - if (caps.invariant_tsc) { - rtsc_frequency = caps.tsc_frequency ? caps.tsc_frequency : EstimateRDTSCFrequency(); - } - // Fallback to StandardWallClock if the hardware TSC does not have the precision greater than: - // - A nanosecond - // - The emulated CPU frequency - // - The emulated clock counter frequency (CNTFRQ) - if (rtsc_frequency <= WallClock::NS_RATIO || rtsc_frequency <= emulated_cpu_frequency || - rtsc_frequency <= emulated_clock_frequency) { - return std::make_unique<StandardWallClock>(emulated_cpu_frequency, - emulated_clock_frequency); + if (caps.invariant_tsc && caps.tsc_frequency >= WallClock::GPUTickFreq) { + return std::make_unique<X64::NativeClock>(caps.tsc_frequency); } else { - return std::make_unique<X64::NativeClock>(emulated_cpu_frequency, emulated_clock_frequency, - rtsc_frequency); + // Fallback to StandardWallClock if the hardware TSC + // - Is not invariant + // - Is not more precise than GPUTickFreq + return std::make_unique<StandardWallClock>(); } -} - #else - -std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency, - u64 emulated_clock_frequency) { - return std::make_unique<StandardWallClock>(emulated_cpu_frequency, emulated_clock_frequency); -} - + return std::make_unique<StandardWallClock>(); #endif +} -std::unique_ptr<WallClock> CreateStandardWallClock(u64 emulated_cpu_frequency, - u64 emulated_clock_frequency) { - return std::make_unique<StandardWallClock>(emulated_cpu_frequency, emulated_clock_frequency); +std::unique_ptr<WallClock> CreateStandardWallClock() { + return std::make_unique<StandardWallClock>(); } } // namespace Common diff --git a/src/common/wall_clock.h b/src/common/wall_clock.h index 157ec5eaea..f45d3d8c52 100644 --- a/src/common/wall_clock.h +++ b/src/common/wall_clock.h @@ -5,6 +5,7 @@ #include <chrono> #include <memory> +#include <ratio> #include "common/common_types.h" @@ -12,50 +13,82 @@ namespace Common { class WallClock { public: - static constexpr u64 NS_RATIO = 1'000'000'000; - static constexpr u64 US_RATIO = 1'000'000; - static constexpr u64 MS_RATIO = 1'000; + static constexpr u64 CNTFRQ = 19'200'000; // CNTPCT_EL0 Frequency = 19.2 MHz + static constexpr u64 GPUTickFreq = 614'400'000; // GM20B GPU Tick Frequency = 614.4 MHz + static constexpr u64 CPUTickFreq = 1'020'000'000; // T210/4 A57 CPU Tick Frequency = 1020.0 MHz virtual ~WallClock() = default; - /// Returns current wall time in nanoseconds - [[nodiscard]] virtual std::chrono::nanoseconds GetTimeNS() = 0; + /// @returns The time in nanoseconds since the construction of this clock. + virtual std::chrono::nanoseconds GetTimeNS() const = 0; - /// Returns current wall time in microseconds - [[nodiscard]] virtual std::chrono::microseconds GetTimeUS() = 0; + /// @returns The time in microseconds since the construction of this clock. + virtual std::chrono::microseconds GetTimeUS() const = 0; - /// Returns current wall time in milliseconds - [[nodiscard]] virtual std::chrono::milliseconds GetTimeMS() = 0; + /// @returns The time in milliseconds since the construction of this clock. + virtual std::chrono::milliseconds GetTimeMS() const = 0; - /// Returns current wall time in emulated clock cycles - [[nodiscard]] virtual u64 GetClockCycles() = 0; + /// @returns The guest CNTPCT ticks since the construction of this clock. + virtual u64 GetCNTPCT() const = 0; - /// Returns current wall time in emulated cpu cycles - [[nodiscard]] virtual u64 GetCPUCycles() = 0; + /// @returns The guest GPU ticks since the construction of this clock. + virtual u64 GetGPUTick() const = 0; - virtual void Pause(bool is_paused) = 0; + /// @returns The raw host timer ticks since an indeterminate epoch. + virtual u64 GetHostTicksNow() const = 0; - /// Tells if the wall clock, uses the host CPU's hardware clock - [[nodiscard]] bool IsNative() const { - return is_native; + /// @returns The raw host timer ticks since the construction of this clock. + virtual u64 GetHostTicksElapsed() const = 0; + + /// @returns Whether the clock directly uses the host's hardware clock. + virtual bool IsNative() const = 0; + + static inline u64 NSToCNTPCT(u64 ns) { + return ns * NsToCNTPCTRatio::num / NsToCNTPCTRatio::den; + } + + static inline u64 NSToGPUTick(u64 ns) { + return ns * NsToGPUTickRatio::num / NsToGPUTickRatio::den; + } + + // Cycle Timing + + static inline u64 CPUTickToNS(u64 cpu_tick) { + return cpu_tick * CPUTickToNsRatio::num / CPUTickToNsRatio::den; + } + + static inline u64 CPUTickToUS(u64 cpu_tick) { + return cpu_tick * CPUTickToUsRatio::num / CPUTickToUsRatio::den; + } + + static inline u64 CPUTickToCNTPCT(u64 cpu_tick) { + return cpu_tick * CPUTickToCNTPCTRatio::num / CPUTickToCNTPCTRatio::den; + } + + static inline u64 CPUTickToGPUTick(u64 cpu_tick) { + return cpu_tick * CPUTickToGPUTickRatio::num / CPUTickToGPUTickRatio::den; } protected: - explicit WallClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequency_, bool is_native_) - : emulated_cpu_frequency{emulated_cpu_frequency_}, - emulated_clock_frequency{emulated_clock_frequency_}, is_native{is_native_} {} + using NsRatio = std::nano; + using UsRatio = std::micro; + using MsRatio = std::milli; + + using NsToUsRatio = std::ratio_divide<std::nano, std::micro>; + using NsToMsRatio = std::ratio_divide<std::nano, std::milli>; + using NsToCNTPCTRatio = std::ratio<CNTFRQ, std::nano::den>; + using NsToGPUTickRatio = std::ratio<GPUTickFreq, std::nano::den>; - u64 emulated_cpu_frequency; - u64 emulated_clock_frequency; + // Cycle Timing -private: - bool is_native; + using CPUTickToNsRatio = std::ratio<std::nano::den, CPUTickFreq>; + using CPUTickToUsRatio = std::ratio<std::micro::den, CPUTickFreq>; + using CPUTickToCNTPCTRatio = std::ratio<CNTFRQ, CPUTickFreq>; + using CPUTickToGPUTickRatio = std::ratio<GPUTickFreq, CPUTickFreq>; }; -[[nodiscard]] std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency, - u64 emulated_clock_frequency); +std::unique_ptr<WallClock> CreateOptimalClock(); -[[nodiscard]] std::unique_ptr<WallClock> CreateStandardWallClock(u64 emulated_cpu_frequency, - u64 emulated_clock_frequency); +std::unique_ptr<WallClock> CreateStandardWallClock(); } // namespace Common diff --git a/src/common/x64/cpu_detect.cpp b/src/common/x64/cpu_detect.cpp index 72ed6e96c9..c998b1197e 100644 --- a/src/common/x64/cpu_detect.cpp +++ b/src/common/x64/cpu_detect.cpp @@ -14,6 +14,7 @@ #include "common/common_types.h" #include "common/logging/log.h" #include "common/x64/cpu_detect.h" +#include "common/x64/rdtsc.h" #ifdef _WIN32 #include <windows.h> @@ -187,6 +188,8 @@ static CPUCaps Detect() { caps.tsc_frequency = static_cast<u64>(caps.crystal_frequency) * caps.tsc_crystal_ratio_numerator / caps.tsc_crystal_ratio_denominator; + } else { + caps.tsc_frequency = X64::EstimateRDTSCFrequency(); } } diff --git a/src/common/x64/cpu_wait.cpp b/src/common/x64/cpu_wait.cpp index cfeef6a3d3..c53dd49453 100644 --- a/src/common/x64/cpu_wait.cpp +++ b/src/common/x64/cpu_wait.cpp @@ -9,19 +9,11 @@ #include "common/x64/cpu_detect.h" #include "common/x64/cpu_wait.h" +#include "common/x64/rdtsc.h" namespace Common::X64 { #ifdef _MSC_VER -__forceinline static u64 FencedRDTSC() { - _mm_lfence(); - _ReadWriteBarrier(); - const u64 result = __rdtsc(); - _mm_lfence(); - _ReadWriteBarrier(); - return result; -} - __forceinline static void TPAUSE() { // 100,000 cycles is a reasonable amount of time to wait to save on CPU resources. // For reference: @@ -32,16 +24,6 @@ __forceinline static void TPAUSE() { _tpause(0, FencedRDTSC() + PauseCycles); } #else -static u64 FencedRDTSC() { - u64 eax; - u64 edx; - asm volatile("lfence\n\t" - "rdtsc\n\t" - "lfence\n\t" - : "=a"(eax), "=d"(edx)); - return (edx << 32) | eax; -} - static void TPAUSE() { // 100,000 cycles is a reasonable amount of time to wait to save on CPU resources. // For reference: diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp index 277b006625..7d2a26bd9a 100644 --- a/src/common/x64/native_clock.cpp +++ b/src/common/x64/native_clock.cpp @@ -1,164 +1,50 @@ // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include <array> -#include <chrono> -#include <thread> - -#include "common/atomic_ops.h" -#include "common/steady_clock.h" #include "common/uint128.h" #include "common/x64/native_clock.h" +#include "common/x64/rdtsc.h" -#ifdef _MSC_VER -#include <intrin.h> -#endif - -namespace Common { +namespace Common::X64 { -#ifdef _MSC_VER -__forceinline static u64 FencedRDTSC() { - _mm_lfence(); - _ReadWriteBarrier(); - const u64 result = __rdtsc(); - _mm_lfence(); - _ReadWriteBarrier(); - return result; -} -#else -static u64 FencedRDTSC() { - u64 eax; - u64 edx; - asm volatile("lfence\n\t" - "rdtsc\n\t" - "lfence\n\t" - : "=a"(eax), "=d"(edx)); - return (edx << 32) | eax; -} -#endif +NativeClock::NativeClock(u64 rdtsc_frequency_) + : start_ticks{FencedRDTSC()}, rdtsc_frequency{rdtsc_frequency_}, + ns_rdtsc_factor{GetFixedPoint64Factor(NsRatio::den, rdtsc_frequency)}, + us_rdtsc_factor{GetFixedPoint64Factor(UsRatio::den, rdtsc_frequency)}, + ms_rdtsc_factor{GetFixedPoint64Factor(MsRatio::den, rdtsc_frequency)}, + cntpct_rdtsc_factor{GetFixedPoint64Factor(CNTFRQ, rdtsc_frequency)}, + gputick_rdtsc_factor{GetFixedPoint64Factor(GPUTickFreq, rdtsc_frequency)} {} -template <u64 Nearest> -static u64 RoundToNearest(u64 value) { - const auto mod = value % Nearest; - return mod >= (Nearest / 2) ? (value - mod + Nearest) : (value - mod); +std::chrono::nanoseconds NativeClock::GetTimeNS() const { + return std::chrono::nanoseconds{MultiplyHigh(GetHostTicksElapsed(), ns_rdtsc_factor)}; } -u64 EstimateRDTSCFrequency() { - // Discard the first result measuring the rdtsc. - FencedRDTSC(); - std::this_thread::sleep_for(std::chrono::milliseconds{1}); - FencedRDTSC(); - - // Get the current time. - const auto start_time = Common::RealTimeClock::Now(); - const u64 tsc_start = FencedRDTSC(); - // Wait for 250 milliseconds. - std::this_thread::sleep_for(std::chrono::milliseconds{250}); - const auto end_time = Common::RealTimeClock::Now(); - const u64 tsc_end = FencedRDTSC(); - // Calculate differences. - const u64 timer_diff = static_cast<u64>( - std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count()); - const u64 tsc_diff = tsc_end - tsc_start; - const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, timer_diff); - return RoundToNearest<1000>(tsc_freq); +std::chrono::microseconds NativeClock::GetTimeUS() const { + return std::chrono::microseconds{MultiplyHigh(GetHostTicksElapsed(), us_rdtsc_factor)}; } -namespace X64 { -NativeClock::NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequency_, - u64 rtsc_frequency_) - : WallClock(emulated_cpu_frequency_, emulated_clock_frequency_, true), rtsc_frequency{ - rtsc_frequency_} { - // Thread to re-adjust the RDTSC frequency after 10 seconds has elapsed. - time_sync_thread = std::jthread{[this](std::stop_token token) { - // Get the current time. - const auto start_time = Common::RealTimeClock::Now(); - const u64 tsc_start = FencedRDTSC(); - // Wait for 10 seconds. - if (!Common::StoppableTimedWait(token, std::chrono::seconds{10})) { - return; - } - const auto end_time = Common::RealTimeClock::Now(); - const u64 tsc_end = FencedRDTSC(); - // Calculate differences. - const u64 timer_diff = static_cast<u64>( - std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count()); - const u64 tsc_diff = tsc_end - tsc_start; - const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, timer_diff); - rtsc_frequency = tsc_freq; - CalculateAndSetFactors(); - }}; - - time_point.inner.last_measure = FencedRDTSC(); - time_point.inner.accumulated_ticks = 0U; - CalculateAndSetFactors(); +std::chrono::milliseconds NativeClock::GetTimeMS() const { + return std::chrono::milliseconds{MultiplyHigh(GetHostTicksElapsed(), ms_rdtsc_factor)}; } -u64 NativeClock::GetRTSC() { - TimePoint new_time_point{}; - TimePoint current_time_point{}; - - current_time_point.pack = Common::AtomicLoad128(time_point.pack.data()); - do { - const u64 current_measure = FencedRDTSC(); - u64 diff = current_measure - current_time_point.inner.last_measure; - diff = diff & ~static_cast<u64>(static_cast<s64>(diff) >> 63); // max(diff, 0) - new_time_point.inner.last_measure = current_measure > current_time_point.inner.last_measure - ? current_measure - : current_time_point.inner.last_measure; - new_time_point.inner.accumulated_ticks = current_time_point.inner.accumulated_ticks + diff; - } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack, - current_time_point.pack, current_time_point.pack)); - return new_time_point.inner.accumulated_ticks; +u64 NativeClock::GetCNTPCT() const { + return MultiplyHigh(GetHostTicksElapsed(), cntpct_rdtsc_factor); } -void NativeClock::Pause(bool is_paused) { - if (!is_paused) { - TimePoint current_time_point{}; - TimePoint new_time_point{}; - - current_time_point.pack = Common::AtomicLoad128(time_point.pack.data()); - do { - new_time_point.pack = current_time_point.pack; - new_time_point.inner.last_measure = FencedRDTSC(); - } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack, - current_time_point.pack, current_time_point.pack)); - } +u64 NativeClock::GetGPUTick() const { + return MultiplyHigh(GetHostTicksElapsed(), gputick_rdtsc_factor); } -std::chrono::nanoseconds NativeClock::GetTimeNS() { - const u64 rtsc_value = GetRTSC(); - return std::chrono::nanoseconds{MultiplyHigh(rtsc_value, ns_rtsc_factor)}; +u64 NativeClock::GetHostTicksNow() const { + return FencedRDTSC(); } -std::chrono::microseconds NativeClock::GetTimeUS() { - const u64 rtsc_value = GetRTSC(); - return std::chrono::microseconds{MultiplyHigh(rtsc_value, us_rtsc_factor)}; +u64 NativeClock::GetHostTicksElapsed() const { + return FencedRDTSC() - start_ticks; } -std::chrono::milliseconds NativeClock::GetTimeMS() { - const u64 rtsc_value = GetRTSC(); - return std::chrono::milliseconds{MultiplyHigh(rtsc_value, ms_rtsc_factor)}; +bool NativeClock::IsNative() const { + return true; } -u64 NativeClock::GetClockCycles() { - const u64 rtsc_value = GetRTSC(); - return MultiplyHigh(rtsc_value, clock_rtsc_factor); -} - -u64 NativeClock::GetCPUCycles() { - const u64 rtsc_value = GetRTSC(); - return MultiplyHigh(rtsc_value, cpu_rtsc_factor); -} - -void NativeClock::CalculateAndSetFactors() { - ns_rtsc_factor = GetFixedPoint64Factor(NS_RATIO, rtsc_frequency); - us_rtsc_factor = GetFixedPoint64Factor(US_RATIO, rtsc_frequency); - ms_rtsc_factor = GetFixedPoint64Factor(MS_RATIO, rtsc_frequency); - clock_rtsc_factor = GetFixedPoint64Factor(emulated_clock_frequency, rtsc_frequency); - cpu_rtsc_factor = GetFixedPoint64Factor(emulated_cpu_frequency, rtsc_frequency); -} - -} // namespace X64 - -} // namespace Common +} // namespace Common::X64 diff --git a/src/common/x64/native_clock.h b/src/common/x64/native_clock.h index 03ca291d80..334415eff3 100644 --- a/src/common/x64/native_clock.h +++ b/src/common/x64/native_clock.h @@ -3,58 +3,39 @@ #pragma once -#include "common/polyfill_thread.h" #include "common/wall_clock.h" -namespace Common { +namespace Common::X64 { -namespace X64 { class NativeClock final : public WallClock { public: - explicit NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequency_, - u64 rtsc_frequency_); + explicit NativeClock(u64 rdtsc_frequency_); - std::chrono::nanoseconds GetTimeNS() override; + std::chrono::nanoseconds GetTimeNS() const override; - std::chrono::microseconds GetTimeUS() override; + std::chrono::microseconds GetTimeUS() const override; - std::chrono::milliseconds GetTimeMS() override; + std::chrono::milliseconds GetTimeMS() const override; - u64 GetClockCycles() override; + u64 GetCNTPCT() const override; - u64 GetCPUCycles() override; + u64 GetGPUTick() const override; - void Pause(bool is_paused) override; + u64 GetHostTicksNow() const override; -private: - u64 GetRTSC(); - - void CalculateAndSetFactors(); - - union alignas(16) TimePoint { - TimePoint() : pack{} {} - u128 pack{}; - struct Inner { - u64 last_measure{}; - u64 accumulated_ticks{}; - } inner; - }; - - TimePoint time_point; + u64 GetHostTicksElapsed() const override; - // factors - u64 clock_rtsc_factor{}; - u64 cpu_rtsc_factor{}; - u64 ns_rtsc_factor{}; - u64 us_rtsc_factor{}; - u64 ms_rtsc_factor{}; + bool IsNative() const override; - u64 rtsc_frequency; - - std::jthread time_sync_thread; +private: + u64 start_ticks; + u64 rdtsc_frequency; + + u64 ns_rdtsc_factor; + u64 us_rdtsc_factor; + u64 ms_rdtsc_factor; + u64 cntpct_rdtsc_factor; + u64 gputick_rdtsc_factor; }; -} // namespace X64 - -u64 EstimateRDTSCFrequency(); -} // namespace Common +} // namespace Common::X64 diff --git a/src/common/x64/rdtsc.cpp b/src/common/x64/rdtsc.cpp new file mode 100644 index 0000000000..9273274a39 --- /dev/null +++ b/src/common/x64/rdtsc.cpp @@ -0,0 +1,39 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <thread> + +#include "common/steady_clock.h" +#include "common/uint128.h" +#include "common/x64/rdtsc.h" + +namespace Common::X64 { + +template <u64 Nearest> +static u64 RoundToNearest(u64 value) { + const auto mod = value % Nearest; + return mod >= (Nearest / 2) ? (value - mod + Nearest) : (value - mod); +} + +u64 EstimateRDTSCFrequency() { + // Discard the first result measuring the rdtsc. + FencedRDTSC(); + std::this_thread::sleep_for(std::chrono::milliseconds{1}); + FencedRDTSC(); + + // Get the current time. + const auto start_time = RealTimeClock::Now(); + const u64 tsc_start = FencedRDTSC(); + // Wait for 100 milliseconds. + std::this_thread::sleep_for(std::chrono::milliseconds{100}); + const auto end_time = RealTimeClock::Now(); + const u64 tsc_end = FencedRDTSC(); + // Calculate differences. + const u64 timer_diff = static_cast<u64>( + std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count()); + const u64 tsc_diff = tsc_end - tsc_start; + const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, timer_diff); + return RoundToNearest<100'000>(tsc_freq); +} + +} // namespace Common::X64 diff --git a/src/common/x64/rdtsc.h b/src/common/x64/rdtsc.h new file mode 100644 index 0000000000..0ec4f52f9d --- /dev/null +++ b/src/common/x64/rdtsc.h @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#ifdef _MSC_VER +#include <intrin.h> +#endif + +#include "common/common_types.h" + +namespace Common::X64 { + +#ifdef _MSC_VER +__forceinline static u64 FencedRDTSC() { + _mm_lfence(); + _ReadWriteBarrier(); + const u64 result = __rdtsc(); + _mm_lfence(); + _ReadWriteBarrier(); + return result; +} +#else +static inline u64 FencedRDTSC() { + u64 eax; + u64 edx; + asm volatile("lfence\n\t" + "rdtsc\n\t" + "lfence\n\t" + : "=a"(eax), "=d"(edx)); + return (edx << 32) | eax; +} +#endif + +u64 EstimateRDTSCFrequency(); + +} // namespace Common::X64 diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 99602699a2..3655b84788 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -4,8 +4,6 @@ add_library(core STATIC arm/arm_interface.h arm/arm_interface.cpp - arm/dynarmic/arm_exclusive_monitor.cpp - arm/dynarmic/arm_exclusive_monitor.h arm/exclusive_monitor.cpp arm/exclusive_monitor.h arm/symbols.cpp @@ -16,7 +14,6 @@ add_library(core STATIC core.h core_timing.cpp core_timing.h - core_timing_util.h cpu_manager.cpp cpu_manager.h crypto/aes_util.cpp @@ -836,7 +833,7 @@ endif() create_target_directory_groups(core) -target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core) +target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core nx_tzdb) target_link_libraries(core PUBLIC Boost::headers PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::opus) if (MINGW) target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY}) @@ -849,12 +846,15 @@ endif() if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) target_sources(core PRIVATE + arm/dynarmic/arm_dynarmic.h arm/dynarmic/arm_dynarmic_64.cpp arm/dynarmic/arm_dynarmic_64.h arm/dynarmic/arm_dynarmic_32.cpp arm/dynarmic/arm_dynarmic_32.h - arm/dynarmic/arm_dynarmic_cp15.cpp - arm/dynarmic/arm_dynarmic_cp15.h + arm/dynarmic/dynarmic_cp15.cpp + arm/dynarmic/dynarmic_cp15.h + arm/dynarmic/dynarmic_exclusive_monitor.cpp + arm/dynarmic/dynarmic_exclusive_monitor.h hle/service/jit/jit_context.cpp hle/service/jit/jit_context.h hle/service/jit/jit.cpp diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp index d30914b7a8..beaea64b37 100644 --- a/src/core/arm/arm_interface.cpp +++ b/src/core/arm/arm_interface.cpp @@ -13,25 +13,68 @@ #include "core/core.h" #include "core/debugger/debugger.h" #include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/svc.h" #include "core/loader/loader.h" #include "core/memory.h" -#include "core/arm/dynarmic/arm_dynarmic_32.h" -#include "core/arm/dynarmic/arm_dynarmic_64.h" - namespace Core { constexpr u64 SEGMENT_BASE = 0x7100000000ull; std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContext( Core::System& system, const ARM_Interface::ThreadContext32& ctx) { - return ARM_Dynarmic_32::GetBacktraceFromContext(system, ctx); + std::vector<BacktraceEntry> out; + auto& memory = system.ApplicationMemory(); + + const auto& reg = ctx.cpu_registers; + u32 pc = reg[15], lr = reg[14], fp = reg[11]; + out.push_back({"", 0, pc, 0, ""}); + + // fp (= r11) points to the last frame record. + // Frame records are two words long: + // fp+0 : pointer to previous frame record + // fp+4 : value of lr for frame + for (size_t i = 0; i < 256; i++) { + out.push_back({"", 0, lr, 0, ""}); + if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 8)) { + break; + } + lr = memory.Read32(fp + 4); + fp = memory.Read32(fp); + } + + SymbolicateBacktrace(system, out); + + return out; } std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContext( Core::System& system, const ARM_Interface::ThreadContext64& ctx) { - return ARM_Dynarmic_64::GetBacktraceFromContext(system, ctx); + std::vector<BacktraceEntry> out; + auto& memory = system.ApplicationMemory(); + + const auto& reg = ctx.cpu_registers; + u64 pc = ctx.pc, lr = reg[30], fp = reg[29]; + + out.push_back({"", 0, pc, 0, ""}); + + // fp (= x29) points to the previous frame record. + // Frame records are two words long: + // fp+0 : pointer to previous frame record + // fp+8 : value of lr for frame + for (size_t i = 0; i < 256; i++) { + out.push_back({"", 0, lr, 0, ""}); + if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 16)) { + break; + } + lr = memory.Read64(fp + 8); + fp = memory.Read64(fp); + } + + SymbolicateBacktrace(system, out); + + return out; } void ARM_Interface::SymbolicateBacktrace(Core::System& system, std::vector<BacktraceEntry>& out) { @@ -76,6 +119,18 @@ void ARM_Interface::SymbolicateBacktrace(Core::System& system, std::vector<Backt } } +std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const { + if (GetArchitecture() == Architecture::Aarch64) { + ThreadContext64 ctx; + SaveContext(ctx); + return GetBacktraceFromContext(system, ctx); + } else { + ThreadContext32 ctx; + SaveContext(ctx); + return GetBacktraceFromContext(system, ctx); + } +} + void ARM_Interface::LogBacktrace() const { const VAddr sp = GetSP(); const VAddr pc = GetPC(); @@ -83,7 +138,6 @@ void ARM_Interface::LogBacktrace() const { LOG_ERROR(Core_ARM, "{:20}{:20}{:20}{:20}{}", "Module Name", "Address", "Original Address", "Offset", "Symbol"); LOG_ERROR(Core_ARM, ""); - const auto backtrace = GetBacktrace(); for (const auto& entry : backtrace) { LOG_ERROR(Core_ARM, "{:20}{:016X} {:016X} {:016X} {}", entry.module, entry.address, @@ -97,7 +151,7 @@ void ARM_Interface::Run() { while (true) { Kernel::KThread* current_thread{Kernel::GetCurrentThreadPointer(system.Kernel())}; - Dynarmic::HaltReason hr{}; + HaltReason hr{}; // Notify the debugger and go to sleep if a step was performed // and this thread has been scheduled again. @@ -108,17 +162,17 @@ void ARM_Interface::Run() { } // Otherwise, run the thread. - system.EnterDynarmicProfile(); + system.EnterCPUProfile(); if (current_thread->GetStepState() == StepState::StepPending) { hr = StepJit(); - if (Has(hr, step_thread)) { + if (True(hr & HaltReason::StepThread)) { current_thread->SetStepState(StepState::StepPerformed); } } else { hr = RunJit(); } - system.ExitDynarmicProfile(); + system.ExitCPUProfile(); // If the thread is scheduled for termination, exit the thread. if (current_thread->HasDpc()) { @@ -130,8 +184,8 @@ void ARM_Interface::Run() { // Notify the debugger and go to sleep if a breakpoint was hit, // or if the thread is unable to continue for any reason. - if (Has(hr, breakpoint) || Has(hr, no_execute)) { - if (!Has(hr, no_execute)) { + if (True(hr & HaltReason::InstructionBreakpoint) || True(hr & HaltReason::PrefetchAbort)) { + if (!True(hr & HaltReason::InstructionBreakpoint)) { RewindBreakpointInstruction(); } if (system.DebuggerEnabled()) { @@ -144,7 +198,7 @@ void ARM_Interface::Run() { } // Notify the debugger and go to sleep if a watchpoint was hit. - if (Has(hr, watchpoint)) { + if (True(hr & HaltReason::DataAbort)) { if (system.DebuggerEnabled()) { system.GetDebugger().NotifyThreadWatchpoint(current_thread, *HaltedWatchpoint()); } @@ -153,11 +207,11 @@ void ARM_Interface::Run() { } // Handle syscalls and scheduling (this may change the current thread/core) - if (Has(hr, svc_call)) { + if (True(hr & HaltReason::SupervisorCall)) { Kernel::Svc::Call(system, GetSvcNumber()); break; } - if (Has(hr, break_loop) || !uses_wall_clock) { + if (True(hr & HaltReason::BreakLoop) || !uses_wall_clock) { break; } } diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index 8e40702cc9..d5f2fa09a0 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h @@ -8,8 +8,6 @@ #include <string> #include <vector> -#include <dynarmic/interface/halt_reason.h> - #include "common/common_funcs.h" #include "common/common_types.h" #include "core/hardware_properties.h" @@ -30,6 +28,22 @@ class CPUInterruptHandler; using WatchpointArray = std::array<Kernel::DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS>; +// NOTE: these values match the HaltReason enum in Dynarmic +enum class HaltReason : u64 { + StepThread = 0x00000001, + DataAbort = 0x00000004, + BreakLoop = 0x02000000, + SupervisorCall = 0x04000000, + InstructionBreakpoint = 0x08000000, + PrefetchAbort = 0x20000000, +}; +DECLARE_ENUM_FLAG_OPERATORS(HaltReason); + +enum class Architecture { + Aarch32, + Aarch64, +}; + /// Generic ARMv8 CPU interface class ARM_Interface { public: @@ -167,8 +181,9 @@ public: */ virtual void SetTPIDR_EL0(u64 value) = 0; - virtual void SaveContext(ThreadContext32& ctx) = 0; - virtual void SaveContext(ThreadContext64& ctx) = 0; + virtual Architecture GetArchitecture() const = 0; + virtual void SaveContext(ThreadContext32& ctx) const = 0; + virtual void SaveContext(ThreadContext64& ctx) const = 0; virtual void LoadContext(const ThreadContext32& ctx) = 0; virtual void LoadContext(const ThreadContext64& ctx) = 0; void LoadWatchpointArray(const WatchpointArray& wp); @@ -195,17 +210,9 @@ public: static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system, const ThreadContext64& ctx); - virtual std::vector<BacktraceEntry> GetBacktrace() const = 0; - + std::vector<BacktraceEntry> GetBacktrace() const; void LogBacktrace() const; - static constexpr Dynarmic::HaltReason step_thread = Dynarmic::HaltReason::Step; - static constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2; - static constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3; - static constexpr Dynarmic::HaltReason breakpoint = Dynarmic::HaltReason::UserDefined4; - static constexpr Dynarmic::HaltReason watchpoint = Dynarmic::HaltReason::MemoryAbort; - static constexpr Dynarmic::HaltReason no_execute = Dynarmic::HaltReason::UserDefined6; - protected: /// System context that this ARM interface is running under. System& system; @@ -216,8 +223,8 @@ protected: const Kernel::DebugWatchpoint* MatchingWatchpoint( u64 addr, u64 size, Kernel::DebugWatchpointType access_type) const; - virtual Dynarmic::HaltReason RunJit() = 0; - virtual Dynarmic::HaltReason StepJit() = 0; + virtual HaltReason RunJit() = 0; + virtual HaltReason StepJit() = 0; virtual u32 GetSvcNumber() const = 0; virtual const Kernel::DebugWatchpoint* HaltedWatchpoint() const = 0; virtual void RewindBreakpointInstruction() = 0; diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h new file mode 100644 index 0000000000..eef7c31160 --- /dev/null +++ b/src/core/arm/dynarmic/arm_dynarmic.h @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <dynarmic/interface/halt_reason.h> + +#include "core/arm/arm_interface.h" + +namespace Core { + +constexpr Dynarmic::HaltReason StepThread = Dynarmic::HaltReason::Step; +constexpr Dynarmic::HaltReason DataAbort = Dynarmic::HaltReason::MemoryAbort; +constexpr Dynarmic::HaltReason BreakLoop = Dynarmic::HaltReason::UserDefined2; +constexpr Dynarmic::HaltReason SupervisorCall = Dynarmic::HaltReason::UserDefined3; +constexpr Dynarmic::HaltReason InstructionBreakpoint = Dynarmic::HaltReason::UserDefined4; +constexpr Dynarmic::HaltReason PrefetchAbort = Dynarmic::HaltReason::UserDefined6; + +constexpr HaltReason TranslateHaltReason(Dynarmic::HaltReason hr) { + static_assert(static_cast<u64>(HaltReason::StepThread) == static_cast<u64>(StepThread)); + static_assert(static_cast<u64>(HaltReason::DataAbort) == static_cast<u64>(DataAbort)); + static_assert(static_cast<u64>(HaltReason::BreakLoop) == static_cast<u64>(BreakLoop)); + static_assert(static_cast<u64>(HaltReason::SupervisorCall) == static_cast<u64>(SupervisorCall)); + static_assert(static_cast<u64>(HaltReason::InstructionBreakpoint) == + static_cast<u64>(InstructionBreakpoint)); + static_assert(static_cast<u64>(HaltReason::PrefetchAbort) == static_cast<u64>(PrefetchAbort)); + + return static_cast<HaltReason>(hr); +} + +} // namespace Core diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index dfdcbe35ac..5acf9008d0 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -10,9 +10,10 @@ #include "common/logging/log.h" #include "common/page_table.h" #include "common/settings.h" +#include "core/arm/dynarmic/arm_dynarmic.h" #include "core/arm/dynarmic/arm_dynarmic_32.h" -#include "core/arm/dynarmic/arm_dynarmic_cp15.h" -#include "core/arm/dynarmic/arm_exclusive_monitor.h" +#include "core/arm/dynarmic/dynarmic_cp15.h" +#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h" #include "core/core.h" #include "core/core_timing.h" #include "core/debugger/debugger.h" @@ -104,11 +105,11 @@ public: switch (exception) { case Dynarmic::A32::Exception::NoExecuteFault: LOG_CRITICAL(Core_ARM, "Cannot execute instruction at unmapped address {:#08x}", pc); - ReturnException(pc, ARM_Interface::no_execute); + ReturnException(pc, PrefetchAbort); return; default: if (debugger_enabled) { - ReturnException(pc, ARM_Interface::breakpoint); + ReturnException(pc, InstructionBreakpoint); return; } @@ -121,7 +122,7 @@ public: void CallSVC(u32 swi) override { parent.svc_swi = swi; - parent.jit.load()->HaltExecution(ARM_Interface::svc_call); + parent.jit.load()->HaltExecution(SupervisorCall); } void AddTicks(u64 ticks) override { @@ -162,7 +163,7 @@ public: if (!memory.IsValidVirtualAddressRange(addr, size)) { LOG_CRITICAL(Core_ARM, "Stopping execution due to unmapped memory access at {:#x}", addr); - parent.jit.load()->HaltExecution(ARM_Interface::no_execute); + parent.jit.load()->HaltExecution(PrefetchAbort); return false; } @@ -173,7 +174,7 @@ public: const auto match{parent.MatchingWatchpoint(addr, size, type)}; if (match) { parent.halted_watchpoint = match; - parent.jit.load()->HaltExecution(ARM_Interface::watchpoint); + parent.jit.load()->HaltExecution(DataAbort); return false; } @@ -329,12 +330,12 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable* return std::make_unique<Dynarmic::A32::Jit>(config); } -Dynarmic::HaltReason ARM_Dynarmic_32::RunJit() { - return jit.load()->Run(); +HaltReason ARM_Dynarmic_32::RunJit() { + return TranslateHaltReason(jit.load()->Run()); } -Dynarmic::HaltReason ARM_Dynarmic_32::StepJit() { - return jit.load()->Step(); +HaltReason ARM_Dynarmic_32::StepJit() { + return TranslateHaltReason(jit.load()->Step()); } u32 ARM_Dynarmic_32::GetSvcNumber() const { @@ -408,7 +409,7 @@ void ARM_Dynarmic_32::SetTPIDR_EL0(u64 value) { cp15->uprw = static_cast<u32>(value); } -void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) { +void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) const { Dynarmic::A32::Jit* j = jit.load(); ctx.cpu_registers = j->Regs(); ctx.extension_registers = j->ExtRegs(); @@ -425,11 +426,11 @@ void ARM_Dynarmic_32::LoadContext(const ThreadContext32& ctx) { } void ARM_Dynarmic_32::SignalInterrupt() { - jit.load()->HaltExecution(break_loop); + jit.load()->HaltExecution(BreakLoop); } void ARM_Dynarmic_32::ClearInterrupt() { - jit.load()->ClearHalt(break_loop); + jit.load()->ClearHalt(BreakLoop); } void ARM_Dynarmic_32::ClearInstructionCache() { @@ -462,39 +463,4 @@ void ARM_Dynarmic_32::PageTableChanged(Common::PageTable& page_table, jit_cache.emplace(key, std::move(new_jit)); } -std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktrace(Core::System& system, - u64 fp, u64 lr, u64 pc) { - std::vector<BacktraceEntry> out; - auto& memory = system.ApplicationMemory(); - - out.push_back({"", 0, pc, 0, ""}); - - // fp (= r11) points to the last frame record. - // Frame records are two words long: - // fp+0 : pointer to previous frame record - // fp+4 : value of lr for frame - for (size_t i = 0; i < 256; i++) { - out.push_back({"", 0, lr, 0, ""}); - if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 8)) { - break; - } - lr = memory.Read32(fp + 4); - fp = memory.Read32(fp); - } - - SymbolicateBacktrace(system, out); - - return out; -} - -std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktraceFromContext( - System& system, const ThreadContext32& ctx) { - const auto& reg = ctx.cpu_registers; - return GetBacktrace(system, reg[11], reg[14], reg[15]); -} - -std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktrace() const { - return GetBacktrace(system, GetReg(11), GetReg(14), GetReg(15)); -} - } // namespace Core diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h index bce695daf1..a990845cbf 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.h +++ b/src/core/arm/dynarmic/arm_dynarmic_32.h @@ -50,8 +50,11 @@ public: return (GetPSTATE() & 0x20) != 0; } - void SaveContext(ThreadContext32& ctx) override; - void SaveContext(ThreadContext64& ctx) override {} + Architecture GetArchitecture() const override { + return Architecture::Aarch32; + } + void SaveContext(ThreadContext32& ctx) const override; + void SaveContext(ThreadContext64& ctx) const override {} void LoadContext(const ThreadContext32& ctx) override; void LoadContext(const ThreadContext64& ctx) override {} @@ -64,14 +67,9 @@ public: void PageTableChanged(Common::PageTable& new_page_table, std::size_t new_address_space_size_in_bits) override; - static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system, - const ThreadContext32& ctx); - - std::vector<BacktraceEntry> GetBacktrace() const override; - protected: - Dynarmic::HaltReason RunJit() override; - Dynarmic::HaltReason StepJit() override; + HaltReason RunJit() override; + HaltReason StepJit() override; u32 GetSvcNumber() const override; const Kernel::DebugWatchpoint* HaltedWatchpoint() const override; void RewindBreakpointInstruction() override; diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index bbbcb4f9d0..bb97ed5bc6 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -10,8 +10,9 @@ #include "common/logging/log.h" #include "common/page_table.h" #include "common/settings.h" +#include "core/arm/dynarmic/arm_dynarmic.h" #include "core/arm/dynarmic/arm_dynarmic_64.h" -#include "core/arm/dynarmic/arm_exclusive_monitor.h" +#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h" #include "core/core.h" #include "core/core_timing.h" #include "core/debugger/debugger.h" @@ -113,7 +114,7 @@ public: LOG_ERROR(Core_ARM, "Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc, num_instructions, memory.Read32(pc)); - ReturnException(pc, ARM_Interface::no_execute); + ReturnException(pc, PrefetchAbort); } void InstructionCacheOperationRaised(Dynarmic::A64::InstructionCacheOperation op, @@ -148,11 +149,11 @@ public: return; case Dynarmic::A64::Exception::NoExecuteFault: LOG_CRITICAL(Core_ARM, "Cannot execute instruction at unmapped address {:#016x}", pc); - ReturnException(pc, ARM_Interface::no_execute); + ReturnException(pc, PrefetchAbort); return; default: if (debugger_enabled) { - ReturnException(pc, ARM_Interface::breakpoint); + ReturnException(pc, InstructionBreakpoint); return; } @@ -164,7 +165,7 @@ public: void CallSVC(u32 swi) override { parent.svc_swi = swi; - parent.jit.load()->HaltExecution(ARM_Interface::svc_call); + parent.jit.load()->HaltExecution(SupervisorCall); } void AddTicks(u64 ticks) override { @@ -207,7 +208,7 @@ public: if (!memory.IsValidVirtualAddressRange(addr, size)) { LOG_CRITICAL(Core_ARM, "Stopping execution due to unmapped memory access at {:#x}", addr); - parent.jit.load()->HaltExecution(ARM_Interface::no_execute); + parent.jit.load()->HaltExecution(PrefetchAbort); return false; } @@ -218,7 +219,7 @@ public: const auto match{parent.MatchingWatchpoint(addr, size, type)}; if (match) { parent.halted_watchpoint = match; - parent.jit.load()->HaltExecution(ARM_Interface::watchpoint); + parent.jit.load()->HaltExecution(DataAbort); return false; } @@ -383,12 +384,12 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable* return std::make_shared<Dynarmic::A64::Jit>(config); } -Dynarmic::HaltReason ARM_Dynarmic_64::RunJit() { - return jit.load()->Run(); +HaltReason ARM_Dynarmic_64::RunJit() { + return TranslateHaltReason(jit.load()->Run()); } -Dynarmic::HaltReason ARM_Dynarmic_64::StepJit() { - return jit.load()->Step(); +HaltReason ARM_Dynarmic_64::StepJit() { + return TranslateHaltReason(jit.load()->Step()); } u32 ARM_Dynarmic_64::GetSvcNumber() const { @@ -464,7 +465,7 @@ void ARM_Dynarmic_64::SetTPIDR_EL0(u64 value) { cb->tpidr_el0 = value; } -void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) { +void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) const { Dynarmic::A64::Jit* j = jit.load(); ctx.cpu_registers = j->GetRegisters(); ctx.sp = j->GetSP(); @@ -489,11 +490,11 @@ void ARM_Dynarmic_64::LoadContext(const ThreadContext64& ctx) { } void ARM_Dynarmic_64::SignalInterrupt() { - jit.load()->HaltExecution(break_loop); + jit.load()->HaltExecution(BreakLoop); } void ARM_Dynarmic_64::ClearInterrupt() { - jit.load()->ClearHalt(break_loop); + jit.load()->ClearHalt(BreakLoop); } void ARM_Dynarmic_64::ClearInstructionCache() { @@ -526,39 +527,4 @@ void ARM_Dynarmic_64::PageTableChanged(Common::PageTable& page_table, jit_cache.emplace(key, std::move(new_jit)); } -std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace(Core::System& system, - u64 fp, u64 lr, u64 pc) { - std::vector<BacktraceEntry> out; - auto& memory = system.ApplicationMemory(); - - out.push_back({"", 0, pc, 0, ""}); - - // fp (= x29) points to the previous frame record. - // Frame records are two words long: - // fp+0 : pointer to previous frame record - // fp+8 : value of lr for frame - for (size_t i = 0; i < 256; i++) { - out.push_back({"", 0, lr, 0, ""}); - if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 16)) { - break; - } - lr = memory.Read64(fp + 8); - fp = memory.Read64(fp); - } - - SymbolicateBacktrace(system, out); - - return out; -} - -std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktraceFromContext( - System& system, const ThreadContext64& ctx) { - const auto& reg = ctx.cpu_registers; - return GetBacktrace(system, reg[29], reg[30], ctx.pc); -} - -std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace() const { - return GetBacktrace(system, GetReg(29), GetReg(30), GetPC()); -} - } // namespace Core diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h index e83599e82b..af2aa1f1c3 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.h +++ b/src/core/arm/dynarmic/arm_dynarmic_64.h @@ -43,8 +43,11 @@ public: void SetTPIDR_EL0(u64 value) override; u64 GetTPIDR_EL0() const override; - void SaveContext(ThreadContext32& ctx) override {} - void SaveContext(ThreadContext64& ctx) override; + Architecture GetArchitecture() const override { + return Architecture::Aarch64; + } + void SaveContext(ThreadContext32& ctx) const override {} + void SaveContext(ThreadContext64& ctx) const override; void LoadContext(const ThreadContext32& ctx) override {} void LoadContext(const ThreadContext64& ctx) override; @@ -57,14 +60,9 @@ public: void PageTableChanged(Common::PageTable& new_page_table, std::size_t new_address_space_size_in_bits) override; - static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system, - const ThreadContext64& ctx); - - std::vector<BacktraceEntry> GetBacktrace() const override; - protected: - Dynarmic::HaltReason RunJit() override; - Dynarmic::HaltReason StepJit() override; + HaltReason RunJit() override; + HaltReason StepJit() override; u32 GetSvcNumber() const override; const Kernel::DebugWatchpoint* HaltedWatchpoint() const override; void RewindBreakpointInstruction() override; @@ -73,8 +71,6 @@ private: std::shared_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable* page_table, std::size_t address_space_bits) const; - static std::vector<BacktraceEntry> GetBacktrace(Core::System& system, u64 fp, u64 lr, u64 pc); - using JitCacheKey = std::pair<Common::PageTable*, std::size_t>; using JitCacheType = std::unordered_map<JitCacheKey, std::shared_ptr<Dynarmic::A64::Jit>, Common::PairHash>; diff --git a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp b/src/core/arm/dynarmic/dynarmic_cp15.cpp index 5a4eba3eb0..92c548db08 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp +++ b/src/core/arm/dynarmic/dynarmic_cp15.cpp @@ -4,7 +4,7 @@ #include <fmt/format.h> #include "common/logging/log.h" #include "core/arm/dynarmic/arm_dynarmic_32.h" -#include "core/arm/dynarmic/arm_dynarmic_cp15.h" +#include "core/arm/dynarmic/dynarmic_cp15.h" #include "core/core.h" #include "core/core_timing.h" diff --git a/src/core/arm/dynarmic/arm_dynarmic_cp15.h b/src/core/arm/dynarmic/dynarmic_cp15.h index d90b3e5686..d90b3e5686 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_cp15.h +++ b/src/core/arm/dynarmic/dynarmic_cp15.h diff --git a/src/core/arm/dynarmic/arm_exclusive_monitor.cpp b/src/core/arm/dynarmic/dynarmic_exclusive_monitor.cpp index fa0c48b25f..b5c9c43c42 100644 --- a/src/core/arm/dynarmic/arm_exclusive_monitor.cpp +++ b/src/core/arm/dynarmic/dynarmic_exclusive_monitor.cpp @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "core/arm/dynarmic/arm_exclusive_monitor.h" +#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h" #include "core/memory.h" namespace Core { diff --git a/src/core/arm/dynarmic/arm_exclusive_monitor.h b/src/core/arm/dynarmic/dynarmic_exclusive_monitor.h index 57e6dd0d0b..57e6dd0d0b 100644 --- a/src/core/arm/dynarmic/arm_exclusive_monitor.h +++ b/src/core/arm/dynarmic/dynarmic_exclusive_monitor.h diff --git a/src/core/arm/exclusive_monitor.cpp b/src/core/arm/exclusive_monitor.cpp index 20550faeb3..6d9a862e19 100644 --- a/src/core/arm/exclusive_monitor.cpp +++ b/src/core/arm/exclusive_monitor.cpp @@ -2,7 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64) -#include "core/arm/dynarmic/arm_exclusive_monitor.h" +#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h" #endif #include "core/arm/exclusive_monitor.h" #include "core/memory.h" diff --git a/src/core/core.cpp b/src/core/core.cpp index 7ba704f189..b74fd0a583 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -54,10 +54,10 @@ #include "video_core/renderer_base.h" #include "video_core/video_core.h" -MICROPROFILE_DEFINE(ARM_Jit_Dynarmic_CPU0, "ARM JIT", "Dynarmic CPU 0", MP_RGB(255, 64, 64)); -MICROPROFILE_DEFINE(ARM_Jit_Dynarmic_CPU1, "ARM JIT", "Dynarmic CPU 1", MP_RGB(255, 64, 64)); -MICROPROFILE_DEFINE(ARM_Jit_Dynarmic_CPU2, "ARM JIT", "Dynarmic CPU 2", MP_RGB(255, 64, 64)); -MICROPROFILE_DEFINE(ARM_Jit_Dynarmic_CPU3, "ARM JIT", "Dynarmic CPU 3", MP_RGB(255, 64, 64)); +MICROPROFILE_DEFINE(ARM_CPU0, "ARM", "CPU 0", MP_RGB(255, 64, 64)); +MICROPROFILE_DEFINE(ARM_CPU1, "ARM", "CPU 1", MP_RGB(255, 64, 64)); +MICROPROFILE_DEFINE(ARM_CPU2, "ARM", "CPU 2", MP_RGB(255, 64, 64)); +MICROPROFILE_DEFINE(ARM_CPU3, "ARM", "CPU 3", MP_RGB(255, 64, 64)); namespace Core { @@ -259,10 +259,10 @@ struct System::Impl { is_powered_on = true; exit_lock = false; - microprofile_dynarmic[0] = MICROPROFILE_TOKEN(ARM_Jit_Dynarmic_CPU0); - microprofile_dynarmic[1] = MICROPROFILE_TOKEN(ARM_Jit_Dynarmic_CPU1); - microprofile_dynarmic[2] = MICROPROFILE_TOKEN(ARM_Jit_Dynarmic_CPU2); - microprofile_dynarmic[3] = MICROPROFILE_TOKEN(ARM_Jit_Dynarmic_CPU3); + microprofile_cpu[0] = MICROPROFILE_TOKEN(ARM_CPU0); + microprofile_cpu[1] = MICROPROFILE_TOKEN(ARM_CPU1); + microprofile_cpu[2] = MICROPROFILE_TOKEN(ARM_CPU2); + microprofile_cpu[3] = MICROPROFILE_TOKEN(ARM_CPU3); LOG_DEBUG(Core, "Initialized OK"); @@ -539,7 +539,7 @@ struct System::Impl { ExitCallback exit_callback; std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{}; - std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_dynarmic{}; + std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_cpu{}; }; System::System() : impl{std::make_unique<Impl>(*this)} {} @@ -927,14 +927,14 @@ void System::RegisterHostThread() { impl->kernel.RegisterHostThread(); } -void System::EnterDynarmicProfile() { +void System::EnterCPUProfile() { std::size_t core = impl->kernel.GetCurrentHostThreadID(); - impl->dynarmic_ticks[core] = MicroProfileEnter(impl->microprofile_dynarmic[core]); + impl->dynarmic_ticks[core] = MicroProfileEnter(impl->microprofile_cpu[core]); } -void System::ExitDynarmicProfile() { +void System::ExitCPUProfile() { std::size_t core = impl->kernel.GetCurrentHostThreadID(); - MicroProfileLeave(impl->microprofile_dynarmic[core], impl->dynarmic_ticks[core]); + MicroProfileLeave(impl->microprofile_cpu[core], impl->dynarmic_ticks[core]); } bool System::IsMulticore() const { diff --git a/src/core/core.h b/src/core/core.h index ff2e4bd305..93afc93037 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -412,11 +412,11 @@ public: /// Register a host thread as an auxiliary thread. void RegisterHostThread(); - /// Enter Dynarmic Microprofile - void EnterDynarmicProfile(); + /// Enter CPU Microprofile + void EnterCPUProfile(); - /// Exit Dynarmic Microprofile - void ExitDynarmicProfile(); + /// Exit CPU Microprofile + void ExitCPUProfile(); /// Tells if system is running on multicore. [[nodiscard]] bool IsMulticore() const; diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index 4f2692b05c..4f0a3f8eab 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -16,12 +16,11 @@ #include "common/microprofile.h" #include "core/core_timing.h" -#include "core/core_timing_util.h" #include "core/hardware_properties.h" namespace Core::Timing { -constexpr s64 MAX_SLICE_LENGTH = 4000; +constexpr s64 MAX_SLICE_LENGTH = 10000; std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callback) { return std::make_shared<EventType>(std::move(callback), std::move(name)); @@ -45,9 +44,7 @@ struct CoreTiming::Event { } }; -CoreTiming::CoreTiming() - : cpu_clock{Common::CreateBestMatchingClock(Hardware::BASE_CLOCK_RATE, Hardware::CNTFREQ)}, - event_clock{Common::CreateStandardWallClock(Hardware::BASE_CLOCK_RATE, Hardware::CNTFREQ)} {} +CoreTiming::CoreTiming() : clock{Common::CreateOptimalClock()} {} CoreTiming::~CoreTiming() { Reset(); @@ -68,7 +65,7 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) { on_thread_init = std::move(on_thread_init_); event_fifo_id = 0; shutting_down = false; - ticks = 0; + cpu_ticks = 0; const auto empty_timed_callback = [](std::uintptr_t, u64, std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> { return std::nullopt; }; ev_lost = CreateEvent("_lost_event", empty_timed_callback); @@ -173,38 +170,30 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, } void CoreTiming::AddTicks(u64 ticks_to_add) { - ticks += ticks_to_add; - downcount -= static_cast<s64>(ticks); + cpu_ticks += ticks_to_add; + downcount -= static_cast<s64>(cpu_ticks); } void CoreTiming::Idle() { - if (!event_queue.empty()) { - const u64 next_event_time = event_queue.front().time; - const u64 next_ticks = nsToCycles(std::chrono::nanoseconds(next_event_time)) + 10U; - if (next_ticks > ticks) { - ticks = next_ticks; - } - return; - } - ticks += 1000U; + cpu_ticks += 1000U; } void CoreTiming::ResetTicks() { downcount = MAX_SLICE_LENGTH; } -u64 CoreTiming::GetCPUTicks() const { +u64 CoreTiming::GetClockTicks() const { if (is_multicore) [[likely]] { - return cpu_clock->GetCPUCycles(); + return clock->GetCNTPCT(); } - return ticks; + return Common::WallClock::CPUTickToCNTPCT(cpu_ticks); } -u64 CoreTiming::GetClockTicks() const { +u64 CoreTiming::GetGPUTicks() const { if (is_multicore) [[likely]] { - return cpu_clock->GetClockCycles(); + return clock->GetGPUTick(); } - return CpuCyclesToClockCycles(ticks); + return Common::WallClock::CPUTickToGPUTick(cpu_ticks); } std::optional<s64> CoreTiming::Advance() { @@ -297,9 +286,7 @@ void CoreTiming::ThreadLoop() { } paused_set = true; - event_clock->Pause(true); pause_event.Wait(); - event_clock->Pause(false); } } @@ -315,25 +302,18 @@ void CoreTiming::Reset() { has_started = false; } -std::chrono::nanoseconds CoreTiming::GetCPUTimeNs() const { - if (is_multicore) [[likely]] { - return cpu_clock->GetTimeNS(); - } - return CyclesToNs(ticks); -} - std::chrono::nanoseconds CoreTiming::GetGlobalTimeNs() const { if (is_multicore) [[likely]] { - return event_clock->GetTimeNS(); + return clock->GetTimeNS(); } - return CyclesToNs(ticks); + return std::chrono::nanoseconds{Common::WallClock::CPUTickToNS(cpu_ticks)}; } std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const { if (is_multicore) [[likely]] { - return event_clock->GetTimeUS(); + return clock->GetTimeUS(); } - return CyclesToUs(ticks); + return std::chrono::microseconds{Common::WallClock::CPUTickToUS(cpu_ticks)}; } } // namespace Core::Timing diff --git a/src/core/core_timing.h b/src/core/core_timing.h index e7c4a949f6..10db1de552 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h @@ -116,14 +116,11 @@ public: return downcount; } - /// Returns current time in emulated CPU cycles - u64 GetCPUTicks() const; - - /// Returns current time in emulated in Clock cycles + /// Returns the current CNTPCT tick value. u64 GetClockTicks() const; - /// Returns current time in nanoseconds. - std::chrono::nanoseconds GetCPUTimeNs() const; + /// Returns the current GPU tick value. + u64 GetGPUTicks() const; /// Returns current time in microseconds. std::chrono::microseconds GetGlobalTimeUs() const; @@ -142,8 +139,7 @@ private: void Reset(); - std::unique_ptr<Common::WallClock> cpu_clock; - std::unique_ptr<Common::WallClock> event_clock; + std::unique_ptr<Common::WallClock> clock; s64 global_timer = 0; @@ -171,7 +167,7 @@ private: s64 pause_end_time{}; /// Cycle timing - u64 ticks{}; + u64 cpu_ticks{}; s64 downcount{}; }; diff --git a/src/core/core_timing_util.h b/src/core/core_timing_util.h deleted file mode 100644 index fe5aaefc70..0000000000 --- a/src/core/core_timing_util.h +++ /dev/null @@ -1,58 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <chrono> - -#include "common/common_types.h" -#include "core/hardware_properties.h" - -namespace Core::Timing { - -namespace detail { -constexpr u64 CNTFREQ_ADJUSTED = Hardware::CNTFREQ / 1000; -constexpr u64 BASE_CLOCK_RATE_ADJUSTED = Hardware::BASE_CLOCK_RATE / 1000; -} // namespace detail - -[[nodiscard]] constexpr s64 msToCycles(std::chrono::milliseconds ms) { - return ms.count() * detail::BASE_CLOCK_RATE_ADJUSTED; -} - -[[nodiscard]] constexpr s64 usToCycles(std::chrono::microseconds us) { - return us.count() * detail::BASE_CLOCK_RATE_ADJUSTED / 1000; -} - -[[nodiscard]] constexpr s64 nsToCycles(std::chrono::nanoseconds ns) { - return ns.count() * detail::BASE_CLOCK_RATE_ADJUSTED / 1000000; -} - -[[nodiscard]] constexpr u64 msToClockCycles(std::chrono::milliseconds ms) { - return static_cast<u64>(ms.count()) * detail::CNTFREQ_ADJUSTED; -} - -[[nodiscard]] constexpr u64 usToClockCycles(std::chrono::microseconds us) { - return us.count() * detail::CNTFREQ_ADJUSTED / 1000; -} - -[[nodiscard]] constexpr u64 nsToClockCycles(std::chrono::nanoseconds ns) { - return ns.count() * detail::CNTFREQ_ADJUSTED / 1000000; -} - -[[nodiscard]] constexpr u64 CpuCyclesToClockCycles(u64 ticks) { - return ticks * detail::CNTFREQ_ADJUSTED / detail::BASE_CLOCK_RATE_ADJUSTED; -} - -[[nodiscard]] constexpr std::chrono::milliseconds CyclesToMs(s64 cycles) { - return std::chrono::milliseconds(cycles / detail::BASE_CLOCK_RATE_ADJUSTED); -} - -[[nodiscard]] constexpr std::chrono::nanoseconds CyclesToNs(s64 cycles) { - return std::chrono::nanoseconds(cycles * 1000000 / detail::BASE_CLOCK_RATE_ADJUSTED); -} - -[[nodiscard]] constexpr std::chrono::microseconds CyclesToUs(s64 cycles) { - return std::chrono::microseconds(cycles * 1000 / detail::BASE_CLOCK_RATE_ADJUSTED); -} - -} // namespace Core::Timing diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index 4e61d4335a..d3286b3520 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp @@ -153,7 +153,7 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id); std::vector<VirtualDir> patch_dirs = {sdmc_load_dir}; - if (load_dir != nullptr && load_dir->GetSize() > 0) { + if (load_dir != nullptr) { const auto load_patch_dirs = load_dir->GetSubdirectories(); patch_dirs.insert(patch_dirs.end(), load_patch_dirs.begin(), load_patch_dirs.end()); } @@ -354,8 +354,7 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t const auto load_dir = fs_controller.GetModificationLoadRoot(title_id); const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id); if ((type != ContentRecordType::Program && type != ContentRecordType::Data) || - ((load_dir == nullptr || load_dir->GetSize() <= 0) && - (sdmc_load_dir == nullptr || sdmc_load_dir->GetSize() <= 0))) { + (load_dir == nullptr && sdmc_load_dir == nullptr)) { return; } @@ -496,7 +495,7 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u // General Mods (LayeredFS and IPS) const auto mod_dir = fs_controller.GetModificationLoadRoot(title_id); - if (mod_dir != nullptr && mod_dir->GetSize() > 0) { + if (mod_dir != nullptr) { for (const auto& mod : mod_dir->GetSubdirectories()) { std::string types; @@ -540,7 +539,7 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u // SDMC mod directory (RomFS LayeredFS) const auto sdmc_mod_dir = fs_controller.GetSDMCModificationLoadRoot(title_id); - if (sdmc_mod_dir != nullptr && sdmc_mod_dir->GetSize() > 0) { + if (sdmc_mod_dir != nullptr) { std::string types; if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "exefs"))) { AppendCommaIfNotEmpty(types, "LayeredExeFS"); diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h index 3226b884a0..27f97c7251 100644 --- a/src/core/file_sys/submission_package.h +++ b/src/core/file_sys/submission_package.h @@ -8,6 +8,7 @@ #include <set> #include <vector> #include "common/common_types.h" +#include "core/file_sys/nca_metadata.h" #include "core/file_sys/vfs.h" namespace Core::Crypto { diff --git a/src/core/file_sys/system_archive/time_zone_binary.cpp b/src/core/file_sys/system_archive/time_zone_binary.cpp index 85383998d4..7c17bbefa5 100644 --- a/src/core/file_sys/system_archive/time_zone_binary.cpp +++ b/src/core/file_sys/system_archive/time_zone_binary.cpp @@ -1,7 +1,6 @@ // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include <array> #include <vector> #include "common/swap.h" @@ -9,656 +8,79 @@ #include "core/file_sys/vfs_vector.h" #include "core/hle/service/time/time_zone_types.h" -namespace FileSys::SystemArchive { - -static constexpr std::array<u8, 9633> LOCATION_NAMES{ - 0x43, 0x45, 0x54, 0x0d, 0x0a, 0x43, 0x53, 0x54, 0x36, 0x43, 0x44, 0x54, 0x0d, 0x0a, 0x43, 0x75, - 0x62, 0x61, 0x0d, 0x0a, 0x45, 0x45, 0x54, 0x0d, 0x0a, 0x45, 0x67, 0x79, 0x70, 0x74, 0x0d, 0x0a, - 0x45, 0x69, 0x72, 0x65, 0x0d, 0x0a, 0x45, 0x53, 0x54, 0x0d, 0x0a, 0x45, 0x53, 0x54, 0x35, 0x45, - 0x44, 0x54, 0x0d, 0x0a, 0x47, 0x42, 0x0d, 0x0a, 0x47, 0x42, 0x2d, 0x45, 0x69, 0x72, 0x65, 0x0d, - 0x0a, 0x47, 0x4d, 0x54, 0x0d, 0x0a, 0x47, 0x4d, 0x54, 0x2b, 0x30, 0x0d, 0x0a, 0x47, 0x4d, 0x54, - 0x2d, 0x30, 0x0d, 0x0a, 0x47, 0x4d, 0x54, 0x30, 0x0d, 0x0a, 0x47, 0x72, 0x65, 0x65, 0x6e, 0x77, - 0x69, 0x63, 0x68, 0x0d, 0x0a, 0x48, 0x6f, 0x6e, 0x67, 0x6b, 0x6f, 0x6e, 0x67, 0x0d, 0x0a, 0x48, - 0x53, 0x54, 0x0d, 0x0a, 0x49, 0x63, 0x65, 0x6c, 0x61, 0x6e, 0x64, 0x0d, 0x0a, 0x49, 0x72, 0x61, - 0x6e, 0x0d, 0x0a, 0x49, 0x73, 0x72, 0x61, 0x65, 0x6c, 0x0d, 0x0a, 0x4a, 0x61, 0x6d, 0x61, 0x69, - 0x63, 0x61, 0x0d, 0x0a, 0x4a, 0x61, 0x70, 0x61, 0x6e, 0x0d, 0x0a, 0x4b, 0x77, 0x61, 0x6a, 0x61, - 0x6c, 0x65, 0x69, 0x6e, 0x0d, 0x0a, 0x4c, 0x69, 0x62, 0x79, 0x61, 0x0d, 0x0a, 0x4d, 0x45, 0x54, - 0x0d, 0x0a, 0x4d, 0x53, 0x54, 0x0d, 0x0a, 0x4d, 0x53, 0x54, 0x37, 0x4d, 0x44, 0x54, 0x0d, 0x0a, - 0x4e, 0x61, 0x76, 0x61, 0x6a, 0x6f, 0x0d, 0x0a, 0x4e, 0x5a, 0x0d, 0x0a, 0x4e, 0x5a, 0x2d, 0x43, - 0x48, 0x41, 0x54, 0x0d, 0x0a, 0x50, 0x6f, 0x6c, 0x61, 0x6e, 0x64, 0x0d, 0x0a, 0x50, 0x6f, 0x72, - 0x74, 0x75, 0x67, 0x61, 0x6c, 0x0d, 0x0a, 0x50, 0x52, 0x43, 0x0d, 0x0a, 0x50, 0x53, 0x54, 0x38, - 0x50, 0x44, 0x54, 0x0d, 0x0a, 0x52, 0x4f, 0x43, 0x0d, 0x0a, 0x52, 0x4f, 0x4b, 0x0d, 0x0a, 0x53, - 0x69, 0x6e, 0x67, 0x61, 0x70, 0x6f, 0x72, 0x65, 0x0d, 0x0a, 0x54, 0x75, 0x72, 0x6b, 0x65, 0x79, - 0x0d, 0x0a, 0x55, 0x43, 0x54, 0x0d, 0x0a, 0x55, 0x6e, 0x69, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6c, - 0x0d, 0x0a, 0x55, 0x54, 0x43, 0x0d, 0x0a, 0x57, 0x2d, 0x53, 0x55, 0x0d, 0x0a, 0x57, 0x45, 0x54, - 0x0d, 0x0a, 0x5a, 0x75, 0x6c, 0x75, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, - 0x62, 0x69, 0x64, 0x6a, 0x61, 0x6e, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, - 0x63, 0x63, 0x72, 0x61, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x64, 0x64, - 0x69, 0x73, 0x5f, 0x41, 0x62, 0x61, 0x62, 0x61, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, - 0x2f, 0x41, 0x6c, 0x67, 0x69, 0x65, 0x72, 0x73, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, - 0x2f, 0x41, 0x73, 0x6d, 0x61, 0x72, 0x61, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, - 0x41, 0x73, 0x6d, 0x65, 0x72, 0x61, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, - 0x61, 0x6d, 0x61, 0x6b, 0x6f, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x61, - 0x6e, 0x67, 0x75, 0x69, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x61, 0x6e, - 0x6a, 0x75, 0x6c, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x69, 0x73, 0x73, - 0x61, 0x75, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x6c, 0x61, 0x6e, 0x74, - 0x79, 0x72, 0x65, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x72, 0x61, 0x7a, - 0x7a, 0x61, 0x76, 0x69, 0x6c, 0x6c, 0x65, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, - 0x42, 0x75, 0x6a, 0x75, 0x6d, 0x62, 0x75, 0x72, 0x61, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, - 0x61, 0x2f, 0x43, 0x61, 0x69, 0x72, 0x6f, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, - 0x43, 0x61, 0x73, 0x61, 0x62, 0x6c, 0x61, 0x6e, 0x63, 0x61, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, - 0x63, 0x61, 0x2f, 0x43, 0x65, 0x75, 0x74, 0x61, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, - 0x2f, 0x43, 0x6f, 0x6e, 0x61, 0x6b, 0x72, 0x79, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, - 0x2f, 0x44, 0x61, 0x6b, 0x61, 0x72, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x44, - 0x61, 0x72, 0x5f, 0x65, 0x73, 0x5f, 0x53, 0x61, 0x6c, 0x61, 0x61, 0x6d, 0x0d, 0x0a, 0x41, 0x66, - 0x72, 0x69, 0x63, 0x61, 0x2f, 0x44, 0x6a, 0x69, 0x62, 0x6f, 0x75, 0x74, 0x69, 0x0d, 0x0a, 0x41, - 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x44, 0x6f, 0x75, 0x61, 0x6c, 0x61, 0x0d, 0x0a, 0x41, 0x66, - 0x72, 0x69, 0x63, 0x61, 0x2f, 0x45, 0x6c, 0x5f, 0x41, 0x61, 0x69, 0x75, 0x6e, 0x0d, 0x0a, 0x41, - 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x46, 0x72, 0x65, 0x65, 0x74, 0x6f, 0x77, 0x6e, 0x0d, 0x0a, - 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x47, 0x61, 0x62, 0x6f, 0x72, 0x6f, 0x6e, 0x65, 0x0d, - 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x48, 0x61, 0x72, 0x61, 0x72, 0x65, 0x0d, 0x0a, - 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4a, 0x6f, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x73, 0x62, - 0x75, 0x72, 0x67, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4a, 0x75, 0x62, 0x61, - 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4b, 0x61, 0x6d, 0x70, 0x61, 0x6c, 0x61, - 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4b, 0x68, 0x61, 0x72, 0x74, 0x6f, 0x75, - 0x6d, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4b, 0x69, 0x67, 0x61, 0x6c, 0x69, - 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4b, 0x69, 0x6e, 0x73, 0x68, 0x61, 0x73, - 0x61, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4c, 0x61, 0x67, 0x6f, 0x73, 0x0d, - 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4c, 0x69, 0x62, 0x72, 0x65, 0x76, 0x69, 0x6c, - 0x6c, 0x65, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4c, 0x6f, 0x6d, 0x65, 0x0d, - 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4c, 0x75, 0x61, 0x6e, 0x64, 0x61, 0x0d, 0x0a, - 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4c, 0x75, 0x62, 0x75, 0x6d, 0x62, 0x61, 0x73, 0x68, - 0x69, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4c, 0x75, 0x73, 0x61, 0x6b, 0x61, - 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x61, 0x6c, 0x61, 0x62, 0x6f, 0x0d, - 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x61, 0x70, 0x75, 0x74, 0x6f, 0x0d, 0x0a, - 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x61, 0x73, 0x65, 0x72, 0x75, 0x0d, 0x0a, 0x41, - 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x62, 0x61, 0x62, 0x61, 0x6e, 0x65, 0x0d, 0x0a, 0x41, - 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x6f, 0x67, 0x61, 0x64, 0x69, 0x73, 0x68, 0x75, 0x0d, - 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x6f, 0x6e, 0x72, 0x6f, 0x76, 0x69, 0x61, - 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4e, 0x61, 0x69, 0x72, 0x6f, 0x62, 0x69, - 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4e, 0x64, 0x6a, 0x61, 0x6d, 0x65, 0x6e, - 0x61, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4e, 0x69, 0x61, 0x6d, 0x65, 0x79, - 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4e, 0x6f, 0x75, 0x61, 0x6b, 0x63, 0x68, - 0x6f, 0x74, 0x74, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4f, 0x75, 0x61, 0x67, - 0x61, 0x64, 0x6f, 0x75, 0x67, 0x6f, 0x75, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, - 0x50, 0x6f, 0x72, 0x74, 0x6f, 0x2d, 0x4e, 0x6f, 0x76, 0x6f, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, - 0x63, 0x61, 0x2f, 0x53, 0x61, 0x6f, 0x5f, 0x54, 0x6f, 0x6d, 0x65, 0x0d, 0x0a, 0x41, 0x66, 0x72, - 0x69, 0x63, 0x61, 0x2f, 0x54, 0x69, 0x6d, 0x62, 0x75, 0x6b, 0x74, 0x75, 0x0d, 0x0a, 0x41, 0x66, - 0x72, 0x69, 0x63, 0x61, 0x2f, 0x54, 0x72, 0x69, 0x70, 0x6f, 0x6c, 0x69, 0x0d, 0x0a, 0x41, 0x66, - 0x72, 0x69, 0x63, 0x61, 0x2f, 0x54, 0x75, 0x6e, 0x69, 0x73, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, - 0x63, 0x61, 0x2f, 0x57, 0x69, 0x6e, 0x64, 0x68, 0x6f, 0x65, 0x6b, 0x0d, 0x0a, 0x41, 0x6d, 0x65, - 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x64, 0x61, 0x6b, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, - 0x63, 0x61, 0x2f, 0x41, 0x6e, 0x63, 0x68, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x0d, 0x0a, 0x41, 0x6d, - 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x6e, 0x67, 0x75, 0x69, 0x6c, 0x6c, 0x61, 0x0d, 0x0a, - 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x6e, 0x74, 0x69, 0x67, 0x75, 0x61, 0x0d, - 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x72, 0x61, 0x67, 0x75, 0x61, 0x69, - 0x6e, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x72, 0x75, 0x62, - 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x73, 0x75, 0x6e, 0x63, - 0x69, 0x6f, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x74, 0x69, - 0x6b, 0x6f, 0x6b, 0x61, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, - 0x74, 0x6b, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x61, 0x68, - 0x69, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x61, 0x68, 0x69, - 0x61, 0x5f, 0x42, 0x61, 0x6e, 0x64, 0x65, 0x72, 0x61, 0x73, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, - 0x69, 0x63, 0x61, 0x2f, 0x42, 0x61, 0x72, 0x62, 0x61, 0x64, 0x6f, 0x73, 0x0d, 0x0a, 0x41, 0x6d, - 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x65, 0x6c, 0x65, 0x6d, 0x0d, 0x0a, 0x41, 0x6d, 0x65, - 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x65, 0x6c, 0x69, 0x7a, 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65, - 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x6c, 0x61, 0x6e, 0x63, 0x2d, 0x53, 0x61, 0x62, 0x6c, 0x6f, - 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x6f, 0x61, 0x5f, 0x56, - 0x69, 0x73, 0x74, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x6f, - 0x67, 0x6f, 0x74, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x6f, - 0x69, 0x73, 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x75, 0x65, - 0x6e, 0x6f, 0x73, 0x5f, 0x41, 0x69, 0x72, 0x65, 0x73, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, - 0x63, 0x61, 0x2f, 0x43, 0x61, 0x6d, 0x62, 0x72, 0x69, 0x64, 0x67, 0x65, 0x5f, 0x42, 0x61, 0x79, - 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x43, 0x61, 0x6d, 0x70, 0x6f, 0x5f, - 0x47, 0x72, 0x61, 0x6e, 0x64, 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, - 0x43, 0x61, 0x6e, 0x63, 0x75, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, - 0x43, 0x61, 0x72, 0x61, 0x63, 0x61, 0x73, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, - 0x2f, 0x43, 0x61, 0x74, 0x61, 0x6d, 0x61, 0x72, 0x63, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, - 0x69, 0x63, 0x61, 0x2f, 0x43, 0x61, 0x79, 0x65, 0x6e, 0x6e, 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65, - 0x72, 0x69, 0x63, 0x61, 0x2f, 0x43, 0x61, 0x79, 0x6d, 0x61, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, - 0x72, 0x69, 0x63, 0x61, 0x2f, 0x43, 0x68, 0x69, 0x63, 0x61, 0x67, 0x6f, 0x0d, 0x0a, 0x41, 0x6d, - 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x43, 0x68, 0x69, 0x68, 0x75, 0x61, 0x68, 0x75, 0x61, 0x0d, - 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x43, 0x6f, 0x72, 0x61, 0x6c, 0x5f, 0x48, - 0x61, 0x72, 0x62, 0x6f, 0x75, 0x72, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, - 0x43, 0x6f, 0x72, 0x64, 0x6f, 0x62, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, - 0x2f, 0x43, 0x6f, 0x73, 0x74, 0x61, 0x5f, 0x52, 0x69, 0x63, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, - 0x72, 0x69, 0x63, 0x61, 0x2f, 0x43, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, - 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x43, 0x75, 0x69, 0x61, 0x62, 0x61, 0x0d, 0x0a, 0x41, 0x6d, - 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x43, 0x75, 0x72, 0x61, 0x63, 0x61, 0x6f, 0x0d, 0x0a, 0x41, - 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x44, 0x61, 0x6e, 0x6d, 0x61, 0x72, 0x6b, 0x73, 0x68, - 0x61, 0x76, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x44, 0x61, 0x77, - 0x73, 0x6f, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x44, 0x61, 0x77, - 0x73, 0x6f, 0x6e, 0x5f, 0x43, 0x72, 0x65, 0x65, 0x6b, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, - 0x63, 0x61, 0x2f, 0x44, 0x65, 0x6e, 0x76, 0x65, 0x72, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, - 0x63, 0x61, 0x2f, 0x44, 0x65, 0x74, 0x72, 0x6f, 0x69, 0x74, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, - 0x69, 0x63, 0x61, 0x2f, 0x44, 0x6f, 0x6d, 0x69, 0x6e, 0x69, 0x63, 0x61, 0x0d, 0x0a, 0x41, 0x6d, - 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x45, 0x64, 0x6d, 0x6f, 0x6e, 0x74, 0x6f, 0x6e, 0x0d, 0x0a, - 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x45, 0x69, 0x72, 0x75, 0x6e, 0x65, 0x70, 0x65, - 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x45, 0x6c, 0x5f, 0x53, 0x61, 0x6c, - 0x76, 0x61, 0x64, 0x6f, 0x72, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x45, - 0x6e, 0x73, 0x65, 0x6e, 0x61, 0x64, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, - 0x2f, 0x46, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x65, 0x7a, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, - 0x69, 0x63, 0x61, 0x2f, 0x46, 0x6f, 0x72, 0x74, 0x5f, 0x4e, 0x65, 0x6c, 0x73, 0x6f, 0x6e, 0x0d, - 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x46, 0x6f, 0x72, 0x74, 0x5f, 0x57, 0x61, - 0x79, 0x6e, 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x47, 0x6c, 0x61, - 0x63, 0x65, 0x5f, 0x42, 0x61, 0x79, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, - 0x47, 0x6f, 0x64, 0x74, 0x68, 0x61, 0x62, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, - 0x2f, 0x47, 0x6f, 0x6f, 0x73, 0x65, 0x5f, 0x42, 0x61, 0x79, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, - 0x69, 0x63, 0x61, 0x2f, 0x47, 0x72, 0x61, 0x6e, 0x64, 0x5f, 0x54, 0x75, 0x72, 0x6b, 0x0d, 0x0a, - 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x47, 0x72, 0x65, 0x6e, 0x61, 0x64, 0x61, 0x0d, - 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x47, 0x75, 0x61, 0x64, 0x65, 0x6c, 0x6f, - 0x75, 0x70, 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x47, 0x75, 0x61, - 0x74, 0x65, 0x6d, 0x61, 0x6c, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, - 0x47, 0x75, 0x61, 0x79, 0x61, 0x71, 0x75, 0x69, 0x6c, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, - 0x63, 0x61, 0x2f, 0x47, 0x75, 0x79, 0x61, 0x6e, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, - 0x63, 0x61, 0x2f, 0x48, 0x61, 0x6c, 0x69, 0x66, 0x61, 0x78, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, - 0x69, 0x63, 0x61, 0x2f, 0x48, 0x61, 0x76, 0x61, 0x6e, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, - 0x69, 0x63, 0x61, 0x2f, 0x48, 0x65, 0x72, 0x6d, 0x6f, 0x73, 0x69, 0x6c, 0x6c, 0x6f, 0x0d, 0x0a, - 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x49, 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x61, 0x70, - 0x6f, 0x6c, 0x69, 0x73, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x49, 0x6e, - 0x75, 0x76, 0x69, 0x6b, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x49, 0x71, - 0x61, 0x6c, 0x75, 0x69, 0x74, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4a, - 0x61, 0x6d, 0x61, 0x69, 0x63, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, - 0x4a, 0x75, 0x6a, 0x75, 0x79, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4a, - 0x75, 0x6e, 0x65, 0x61, 0x75, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4b, - 0x6e, 0x6f, 0x78, 0x5f, 0x49, 0x4e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, - 0x4b, 0x72, 0x61, 0x6c, 0x65, 0x6e, 0x64, 0x69, 0x6a, 0x6b, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, - 0x69, 0x63, 0x61, 0x2f, 0x4c, 0x61, 0x5f, 0x50, 0x61, 0x7a, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, - 0x69, 0x63, 0x61, 0x2f, 0x4c, 0x69, 0x6d, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, - 0x61, 0x2f, 0x4c, 0x6f, 0x73, 0x5f, 0x41, 0x6e, 0x67, 0x65, 0x6c, 0x65, 0x73, 0x0d, 0x0a, 0x41, - 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4c, 0x6f, 0x75, 0x69, 0x73, 0x76, 0x69, 0x6c, 0x6c, - 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4c, 0x6f, 0x77, 0x65, 0x72, - 0x5f, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x65, 0x73, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, - 0x61, 0x2f, 0x4d, 0x61, 0x63, 0x65, 0x69, 0x6f, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, - 0x61, 0x2f, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x75, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, - 0x63, 0x61, 0x2f, 0x4d, 0x61, 0x6e, 0x61, 0x75, 0x73, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, - 0x63, 0x61, 0x2f, 0x4d, 0x61, 0x72, 0x69, 0x67, 0x6f, 0x74, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, - 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x61, 0x72, 0x74, 0x69, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x0d, 0x0a, - 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x61, 0x74, 0x61, 0x6d, 0x6f, 0x72, 0x6f, - 0x73, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x61, 0x7a, 0x61, 0x74, - 0x6c, 0x61, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x65, 0x6e, - 0x64, 0x6f, 0x7a, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x65, - 0x6e, 0x6f, 0x6d, 0x69, 0x6e, 0x65, 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, - 0x2f, 0x4d, 0x65, 0x72, 0x69, 0x64, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, - 0x2f, 0x4d, 0x65, 0x74, 0x6c, 0x61, 0x6b, 0x61, 0x74, 0x6c, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, - 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x65, 0x78, 0x69, 0x63, 0x6f, 0x5f, 0x43, 0x69, 0x74, 0x79, - 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x69, 0x71, 0x75, 0x65, 0x6c, - 0x6f, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x6f, 0x6e, 0x63, - 0x74, 0x6f, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x6f, 0x6e, - 0x74, 0x65, 0x72, 0x72, 0x65, 0x79, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, - 0x4d, 0x6f, 0x6e, 0x74, 0x65, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, - 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x6f, 0x6e, 0x74, 0x72, 0x65, 0x61, 0x6c, 0x0d, 0x0a, 0x41, 0x6d, - 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x6f, 0x6e, 0x74, 0x73, 0x65, 0x72, 0x72, 0x61, 0x74, - 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4e, 0x61, 0x73, 0x73, 0x61, 0x75, - 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4e, 0x65, 0x77, 0x5f, 0x59, 0x6f, - 0x72, 0x6b, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4e, 0x69, 0x70, 0x69, - 0x67, 0x6f, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4e, 0x6f, 0x6d, - 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4e, 0x6f, 0x72, 0x6f, 0x6e, - 0x68, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4f, 0x6a, 0x69, 0x6e, - 0x61, 0x67, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x50, 0x61, 0x6e, - 0x61, 0x6d, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x50, 0x61, 0x6e, - 0x67, 0x6e, 0x69, 0x72, 0x74, 0x75, 0x6e, 0x67, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, - 0x61, 0x2f, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x61, 0x72, 0x69, 0x62, 0x6f, 0x0d, 0x0a, 0x41, 0x6d, - 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x50, 0x68, 0x6f, 0x65, 0x6e, 0x69, 0x78, 0x0d, 0x0a, 0x41, - 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x50, 0x6f, 0x72, 0x74, 0x2d, 0x61, 0x75, 0x2d, 0x50, - 0x72, 0x69, 0x6e, 0x63, 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x50, - 0x6f, 0x72, 0x74, 0x6f, 0x5f, 0x41, 0x63, 0x72, 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, - 0x63, 0x61, 0x2f, 0x50, 0x6f, 0x72, 0x74, 0x6f, 0x5f, 0x56, 0x65, 0x6c, 0x68, 0x6f, 0x0d, 0x0a, - 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x50, 0x6f, 0x72, 0x74, 0x5f, 0x6f, 0x66, 0x5f, - 0x53, 0x70, 0x61, 0x69, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x50, - 0x75, 0x65, 0x72, 0x74, 0x6f, 0x5f, 0x52, 0x69, 0x63, 0x6f, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, - 0x69, 0x63, 0x61, 0x2f, 0x50, 0x75, 0x6e, 0x74, 0x61, 0x5f, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x73, - 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x52, 0x61, 0x69, 0x6e, 0x79, 0x5f, - 0x52, 0x69, 0x76, 0x65, 0x72, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x52, - 0x61, 0x6e, 0x6b, 0x69, 0x6e, 0x5f, 0x49, 0x6e, 0x6c, 0x65, 0x74, 0x0d, 0x0a, 0x41, 0x6d, 0x65, - 0x72, 0x69, 0x63, 0x61, 0x2f, 0x52, 0x65, 0x63, 0x69, 0x66, 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65, - 0x72, 0x69, 0x63, 0x61, 0x2f, 0x52, 0x65, 0x67, 0x69, 0x6e, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, - 0x72, 0x69, 0x63, 0x61, 0x2f, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x65, 0x0d, 0x0a, 0x41, - 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x52, 0x69, 0x6f, 0x5f, 0x42, 0x72, 0x61, 0x6e, 0x63, - 0x6f, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x52, 0x6f, 0x73, 0x61, 0x72, - 0x69, 0x6f, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x53, 0x61, 0x6e, 0x74, - 0x61, 0x72, 0x65, 0x6d, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x53, 0x61, - 0x6e, 0x74, 0x61, 0x5f, 0x49, 0x73, 0x61, 0x62, 0x65, 0x6c, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, - 0x69, 0x63, 0x61, 0x2f, 0x53, 0x61, 0x6e, 0x74, 0x69, 0x61, 0x67, 0x6f, 0x0d, 0x0a, 0x41, 0x6d, - 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x53, 0x61, 0x6e, 0x74, 0x6f, 0x5f, 0x44, 0x6f, 0x6d, 0x69, - 0x6e, 0x67, 0x6f, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x53, 0x61, 0x6f, - 0x5f, 0x50, 0x61, 0x75, 0x6c, 0x6f, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, - 0x53, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x62, 0x79, 0x73, 0x75, 0x6e, 0x64, 0x0d, 0x0a, 0x41, 0x6d, - 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x53, 0x68, 0x69, 0x70, 0x72, 0x6f, 0x63, 0x6b, 0x0d, 0x0a, - 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x53, 0x69, 0x74, 0x6b, 0x61, 0x0d, 0x0a, 0x41, - 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x53, 0x74, 0x5f, 0x42, 0x61, 0x72, 0x74, 0x68, 0x65, - 0x6c, 0x65, 0x6d, 0x79, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x53, 0x74, - 0x5f, 0x4a, 0x6f, 0x68, 0x6e, 0x73, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, - 0x53, 0x74, 0x5f, 0x4b, 0x69, 0x74, 0x74, 0x73, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, - 0x61, 0x2f, 0x53, 0x74, 0x5f, 0x4c, 0x75, 0x63, 0x69, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, - 0x69, 0x63, 0x61, 0x2f, 0x53, 0x74, 0x5f, 0x54, 0x68, 0x6f, 0x6d, 0x61, 0x73, 0x0d, 0x0a, 0x41, - 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x53, 0x74, 0x5f, 0x56, 0x69, 0x6e, 0x63, 0x65, 0x6e, - 0x74, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x53, 0x77, 0x69, 0x66, 0x74, - 0x5f, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, - 0x61, 0x2f, 0x54, 0x65, 0x67, 0x75, 0x63, 0x69, 0x67, 0x61, 0x6c, 0x70, 0x61, 0x0d, 0x0a, 0x41, - 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x54, 0x68, 0x75, 0x6c, 0x65, 0x0d, 0x0a, 0x41, 0x6d, - 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x54, 0x68, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x42, 0x61, - 0x79, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x54, 0x69, 0x6a, 0x75, 0x61, - 0x6e, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x54, 0x6f, 0x72, 0x6f, - 0x6e, 0x74, 0x6f, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x54, 0x6f, 0x72, - 0x74, 0x6f, 0x6c, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x56, 0x61, - 0x6e, 0x63, 0x6f, 0x75, 0x76, 0x65, 0x72, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, - 0x2f, 0x56, 0x69, 0x72, 0x67, 0x69, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, - 0x2f, 0x57, 0x68, 0x69, 0x74, 0x65, 0x68, 0x6f, 0x72, 0x73, 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65, - 0x72, 0x69, 0x63, 0x61, 0x2f, 0x57, 0x69, 0x6e, 0x6e, 0x69, 0x70, 0x65, 0x67, 0x0d, 0x0a, 0x41, - 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x59, 0x61, 0x6b, 0x75, 0x74, 0x61, 0x74, 0x0d, 0x0a, - 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x59, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6b, 0x6e, - 0x69, 0x66, 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x72, 0x67, - 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x61, 0x2f, 0x42, 0x75, 0x65, 0x6e, 0x6f, 0x73, 0x5f, 0x41, 0x69, - 0x72, 0x65, 0x73, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x72, 0x67, - 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x61, 0x2f, 0x43, 0x61, 0x74, 0x61, 0x6d, 0x61, 0x72, 0x63, 0x61, - 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x72, 0x67, 0x65, 0x6e, 0x74, - 0x69, 0x6e, 0x61, 0x2f, 0x43, 0x6f, 0x6d, 0x6f, 0x64, 0x52, 0x69, 0x76, 0x61, 0x64, 0x61, 0x76, - 0x69, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x72, 0x67, 0x65, - 0x6e, 0x74, 0x69, 0x6e, 0x61, 0x2f, 0x43, 0x6f, 0x72, 0x64, 0x6f, 0x62, 0x61, 0x0d, 0x0a, 0x41, - 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x72, 0x67, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x61, - 0x2f, 0x4a, 0x75, 0x6a, 0x75, 0x79, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, - 0x41, 0x72, 0x67, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x61, 0x2f, 0x4c, 0x61, 0x5f, 0x52, 0x69, 0x6f, - 0x6a, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x72, 0x67, 0x65, - 0x6e, 0x74, 0x69, 0x6e, 0x61, 0x2f, 0x4d, 0x65, 0x6e, 0x64, 0x6f, 0x7a, 0x61, 0x0d, 0x0a, 0x41, - 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x72, 0x67, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x61, - 0x2f, 0x52, 0x69, 0x6f, 0x5f, 0x47, 0x61, 0x6c, 0x6c, 0x65, 0x67, 0x6f, 0x73, 0x0d, 0x0a, 0x41, - 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x72, 0x67, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x61, - 0x2f, 0x53, 0x61, 0x6c, 0x74, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, - 0x41, 0x72, 0x67, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x61, 0x2f, 0x53, 0x61, 0x6e, 0x5f, 0x4a, 0x75, - 0x61, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x72, 0x67, 0x65, - 0x6e, 0x74, 0x69, 0x6e, 0x61, 0x2f, 0x53, 0x61, 0x6e, 0x5f, 0x4c, 0x75, 0x69, 0x73, 0x0d, 0x0a, - 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x72, 0x67, 0x65, 0x6e, 0x74, 0x69, 0x6e, - 0x61, 0x2f, 0x54, 0x75, 0x63, 0x75, 0x6d, 0x61, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, - 0x63, 0x61, 0x2f, 0x41, 0x72, 0x67, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x61, 0x2f, 0x55, 0x73, 0x68, - 0x75, 0x61, 0x69, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x49, 0x6e, - 0x64, 0x69, 0x61, 0x6e, 0x61, 0x2f, 0x49, 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x61, 0x70, 0x6f, 0x6c, - 0x69, 0x73, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x49, 0x6e, 0x64, 0x69, - 0x61, 0x6e, 0x61, 0x2f, 0x4b, 0x6e, 0x6f, 0x78, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, - 0x61, 0x2f, 0x49, 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x61, 0x2f, 0x4d, 0x61, 0x72, 0x65, 0x6e, 0x67, - 0x6f, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x49, 0x6e, 0x64, 0x69, 0x61, - 0x6e, 0x61, 0x2f, 0x50, 0x65, 0x74, 0x65, 0x72, 0x73, 0x62, 0x75, 0x72, 0x67, 0x0d, 0x0a, 0x41, - 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x49, 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x61, 0x2f, 0x54, - 0x65, 0x6c, 0x6c, 0x5f, 0x43, 0x69, 0x74, 0x79, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, - 0x61, 0x2f, 0x49, 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x61, 0x2f, 0x56, 0x65, 0x76, 0x61, 0x79, 0x0d, - 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x49, 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x61, - 0x2f, 0x56, 0x69, 0x6e, 0x63, 0x65, 0x6e, 0x6e, 0x65, 0x73, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, - 0x69, 0x63, 0x61, 0x2f, 0x49, 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x61, 0x2f, 0x57, 0x69, 0x6e, 0x61, - 0x6d, 0x61, 0x63, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4b, 0x65, 0x6e, - 0x74, 0x75, 0x63, 0x6b, 0x79, 0x2f, 0x4c, 0x6f, 0x75, 0x69, 0x73, 0x76, 0x69, 0x6c, 0x6c, 0x65, - 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4b, 0x65, 0x6e, 0x74, 0x75, 0x63, - 0x6b, 0x79, 0x2f, 0x4d, 0x6f, 0x6e, 0x74, 0x69, 0x63, 0x65, 0x6c, 0x6c, 0x6f, 0x0d, 0x0a, 0x41, - 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4e, 0x6f, 0x72, 0x74, 0x68, 0x5f, 0x44, 0x61, 0x6b, - 0x6f, 0x74, 0x61, 0x2f, 0x42, 0x65, 0x75, 0x6c, 0x61, 0x68, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, - 0x69, 0x63, 0x61, 0x2f, 0x4e, 0x6f, 0x72, 0x74, 0x68, 0x5f, 0x44, 0x61, 0x6b, 0x6f, 0x74, 0x61, - 0x2f, 0x43, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, - 0x2f, 0x4e, 0x6f, 0x72, 0x74, 0x68, 0x5f, 0x44, 0x61, 0x6b, 0x6f, 0x74, 0x61, 0x2f, 0x4e, 0x65, - 0x77, 0x5f, 0x53, 0x61, 0x6c, 0x65, 0x6d, 0x0d, 0x0a, 0x41, 0x6e, 0x74, 0x61, 0x72, 0x63, 0x74, - 0x69, 0x63, 0x61, 0x2f, 0x43, 0x61, 0x73, 0x65, 0x79, 0x0d, 0x0a, 0x41, 0x6e, 0x74, 0x61, 0x72, - 0x63, 0x74, 0x69, 0x63, 0x61, 0x2f, 0x44, 0x61, 0x76, 0x69, 0x73, 0x0d, 0x0a, 0x41, 0x6e, 0x74, - 0x61, 0x72, 0x63, 0x74, 0x69, 0x63, 0x61, 0x2f, 0x44, 0x75, 0x6d, 0x6f, 0x6e, 0x74, 0x44, 0x55, - 0x72, 0x76, 0x69, 0x6c, 0x6c, 0x65, 0x0d, 0x0a, 0x41, 0x6e, 0x74, 0x61, 0x72, 0x63, 0x74, 0x69, - 0x63, 0x61, 0x2f, 0x4d, 0x61, 0x63, 0x71, 0x75, 0x61, 0x72, 0x69, 0x65, 0x0d, 0x0a, 0x41, 0x6e, - 0x74, 0x61, 0x72, 0x63, 0x74, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x61, 0x77, 0x73, 0x6f, 0x6e, 0x0d, - 0x0a, 0x41, 0x6e, 0x74, 0x61, 0x72, 0x63, 0x74, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x63, 0x4d, 0x75, - 0x72, 0x64, 0x6f, 0x0d, 0x0a, 0x41, 0x6e, 0x74, 0x61, 0x72, 0x63, 0x74, 0x69, 0x63, 0x61, 0x2f, - 0x50, 0x61, 0x6c, 0x6d, 0x65, 0x72, 0x0d, 0x0a, 0x41, 0x6e, 0x74, 0x61, 0x72, 0x63, 0x74, 0x69, - 0x63, 0x61, 0x2f, 0x52, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x61, 0x0d, 0x0a, 0x41, 0x6e, 0x74, 0x61, - 0x72, 0x63, 0x74, 0x69, 0x63, 0x61, 0x2f, 0x53, 0x6f, 0x75, 0x74, 0x68, 0x5f, 0x50, 0x6f, 0x6c, - 0x65, 0x0d, 0x0a, 0x41, 0x6e, 0x74, 0x61, 0x72, 0x63, 0x74, 0x69, 0x63, 0x61, 0x2f, 0x53, 0x79, - 0x6f, 0x77, 0x61, 0x0d, 0x0a, 0x41, 0x6e, 0x74, 0x61, 0x72, 0x63, 0x74, 0x69, 0x63, 0x61, 0x2f, - 0x54, 0x72, 0x6f, 0x6c, 0x6c, 0x0d, 0x0a, 0x41, 0x6e, 0x74, 0x61, 0x72, 0x63, 0x74, 0x69, 0x63, - 0x61, 0x2f, 0x56, 0x6f, 0x73, 0x74, 0x6f, 0x6b, 0x0d, 0x0a, 0x41, 0x72, 0x63, 0x74, 0x69, 0x63, - 0x2f, 0x4c, 0x6f, 0x6e, 0x67, 0x79, 0x65, 0x61, 0x72, 0x62, 0x79, 0x65, 0x6e, 0x0d, 0x0a, 0x41, - 0x73, 0x69, 0x61, 0x2f, 0x41, 0x64, 0x65, 0x6e, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x41, - 0x6c, 0x6d, 0x61, 0x74, 0x79, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x41, 0x6d, 0x6d, 0x61, - 0x6e, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x41, 0x6e, 0x61, 0x64, 0x79, 0x72, 0x0d, 0x0a, - 0x41, 0x73, 0x69, 0x61, 0x2f, 0x41, 0x71, 0x74, 0x61, 0x75, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, - 0x2f, 0x41, 0x71, 0x74, 0x6f, 0x62, 0x65, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x41, 0x73, - 0x68, 0x67, 0x61, 0x62, 0x61, 0x74, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x41, 0x73, 0x68, - 0x6b, 0x68, 0x61, 0x62, 0x61, 0x64, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x41, 0x74, 0x79, - 0x72, 0x61, 0x75, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x42, 0x61, 0x67, 0x68, 0x64, 0x61, - 0x64, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x42, 0x61, 0x68, 0x72, 0x61, 0x69, 0x6e, 0x0d, - 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x42, 0x61, 0x6b, 0x75, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, - 0x2f, 0x42, 0x61, 0x6e, 0x67, 0x6b, 0x6f, 0x6b, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x42, - 0x61, 0x72, 0x6e, 0x61, 0x75, 0x6c, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x42, 0x65, 0x69, - 0x72, 0x75, 0x74, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x42, 0x69, 0x73, 0x68, 0x6b, 0x65, - 0x6b, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x42, 0x72, 0x75, 0x6e, 0x65, 0x69, 0x0d, 0x0a, - 0x41, 0x73, 0x69, 0x61, 0x2f, 0x43, 0x61, 0x6c, 0x63, 0x75, 0x74, 0x74, 0x61, 0x0d, 0x0a, 0x41, - 0x73, 0x69, 0x61, 0x2f, 0x43, 0x68, 0x69, 0x74, 0x61, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, - 0x43, 0x68, 0x6f, 0x69, 0x62, 0x61, 0x6c, 0x73, 0x61, 0x6e, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, - 0x2f, 0x43, 0x68, 0x6f, 0x6e, 0x67, 0x71, 0x69, 0x6e, 0x67, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, - 0x2f, 0x43, 0x68, 0x75, 0x6e, 0x67, 0x6b, 0x69, 0x6e, 0x67, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, - 0x2f, 0x43, 0x6f, 0x6c, 0x6f, 0x6d, 0x62, 0x6f, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x44, - 0x61, 0x63, 0x63, 0x61, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x44, 0x61, 0x6d, 0x61, 0x73, - 0x63, 0x75, 0x73, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x44, 0x68, 0x61, 0x6b, 0x61, 0x0d, - 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x44, 0x69, 0x6c, 0x69, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, - 0x2f, 0x44, 0x75, 0x62, 0x61, 0x69, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x44, 0x75, 0x73, - 0x68, 0x61, 0x6e, 0x62, 0x65, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x46, 0x61, 0x6d, 0x61, - 0x67, 0x75, 0x73, 0x74, 0x61, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x47, 0x61, 0x7a, 0x61, - 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x48, 0x61, 0x72, 0x62, 0x69, 0x6e, 0x0d, 0x0a, 0x41, - 0x73, 0x69, 0x61, 0x2f, 0x48, 0x65, 0x62, 0x72, 0x6f, 0x6e, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, - 0x2f, 0x48, 0x6f, 0x6e, 0x67, 0x5f, 0x4b, 0x6f, 0x6e, 0x67, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, - 0x2f, 0x48, 0x6f, 0x76, 0x64, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x48, 0x6f, 0x5f, 0x43, - 0x68, 0x69, 0x5f, 0x4d, 0x69, 0x6e, 0x68, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x49, 0x72, - 0x6b, 0x75, 0x74, 0x73, 0x6b, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x49, 0x73, 0x74, 0x61, - 0x6e, 0x62, 0x75, 0x6c, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4a, 0x61, 0x6b, 0x61, 0x72, - 0x74, 0x61, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4a, 0x61, 0x79, 0x61, 0x70, 0x75, 0x72, - 0x61, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4a, 0x65, 0x72, 0x75, 0x73, 0x61, 0x6c, 0x65, - 0x6d, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4b, 0x61, 0x62, 0x75, 0x6c, 0x0d, 0x0a, 0x41, - 0x73, 0x69, 0x61, 0x2f, 0x4b, 0x61, 0x6d, 0x63, 0x68, 0x61, 0x74, 0x6b, 0x61, 0x0d, 0x0a, 0x41, - 0x73, 0x69, 0x61, 0x2f, 0x4b, 0x61, 0x72, 0x61, 0x63, 0x68, 0x69, 0x0d, 0x0a, 0x41, 0x73, 0x69, - 0x61, 0x2f, 0x4b, 0x61, 0x73, 0x68, 0x67, 0x61, 0x72, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, - 0x4b, 0x61, 0x74, 0x68, 0x6d, 0x61, 0x6e, 0x64, 0x75, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, - 0x4b, 0x61, 0x74, 0x6d, 0x61, 0x6e, 0x64, 0x75, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4b, - 0x68, 0x61, 0x6e, 0x64, 0x79, 0x67, 0x61, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4b, 0x6f, - 0x6c, 0x6b, 0x61, 0x74, 0x61, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4b, 0x72, 0x61, 0x73, - 0x6e, 0x6f, 0x79, 0x61, 0x72, 0x73, 0x6b, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4b, 0x75, - 0x61, 0x6c, 0x61, 0x5f, 0x4c, 0x75, 0x6d, 0x70, 0x75, 0x72, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, - 0x2f, 0x4b, 0x75, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4b, - 0x75, 0x77, 0x61, 0x69, 0x74, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4d, 0x61, 0x63, 0x61, - 0x6f, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4d, 0x61, 0x63, 0x61, 0x75, 0x0d, 0x0a, 0x41, - 0x73, 0x69, 0x61, 0x2f, 0x4d, 0x61, 0x67, 0x61, 0x64, 0x61, 0x6e, 0x0d, 0x0a, 0x41, 0x73, 0x69, - 0x61, 0x2f, 0x4d, 0x61, 0x6b, 0x61, 0x73, 0x73, 0x61, 0x72, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, - 0x2f, 0x4d, 0x61, 0x6e, 0x69, 0x6c, 0x61, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4d, 0x75, - 0x73, 0x63, 0x61, 0x74, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4e, 0x69, 0x63, 0x6f, 0x73, - 0x69, 0x61, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4e, 0x6f, 0x76, 0x6f, 0x6b, 0x75, 0x7a, - 0x6e, 0x65, 0x74, 0x73, 0x6b, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4e, 0x6f, 0x76, 0x6f, - 0x73, 0x69, 0x62, 0x69, 0x72, 0x73, 0x6b, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4f, 0x6d, - 0x73, 0x6b, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4f, 0x72, 0x61, 0x6c, 0x0d, 0x0a, 0x41, - 0x73, 0x69, 0x61, 0x2f, 0x50, 0x68, 0x6e, 0x6f, 0x6d, 0x5f, 0x50, 0x65, 0x6e, 0x68, 0x0d, 0x0a, - 0x41, 0x73, 0x69, 0x61, 0x2f, 0x50, 0x6f, 0x6e, 0x74, 0x69, 0x61, 0x6e, 0x61, 0x6b, 0x0d, 0x0a, - 0x41, 0x73, 0x69, 0x61, 0x2f, 0x50, 0x79, 0x6f, 0x6e, 0x67, 0x79, 0x61, 0x6e, 0x67, 0x0d, 0x0a, - 0x41, 0x73, 0x69, 0x61, 0x2f, 0x51, 0x61, 0x74, 0x61, 0x72, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, - 0x2f, 0x51, 0x79, 0x7a, 0x79, 0x6c, 0x6f, 0x72, 0x64, 0x61, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, - 0x2f, 0x52, 0x61, 0x6e, 0x67, 0x6f, 0x6f, 0x6e, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x52, - 0x69, 0x79, 0x61, 0x64, 0x68, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x53, 0x61, 0x69, 0x67, - 0x6f, 0x6e, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x53, 0x61, 0x6b, 0x68, 0x61, 0x6c, 0x69, - 0x6e, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x53, 0x61, 0x6d, 0x61, 0x72, 0x6b, 0x61, 0x6e, - 0x64, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x53, 0x65, 0x6f, 0x75, 0x6c, 0x0d, 0x0a, 0x41, - 0x73, 0x69, 0x61, 0x2f, 0x53, 0x68, 0x61, 0x6e, 0x67, 0x68, 0x61, 0x69, 0x0d, 0x0a, 0x41, 0x73, - 0x69, 0x61, 0x2f, 0x53, 0x69, 0x6e, 0x67, 0x61, 0x70, 0x6f, 0x72, 0x65, 0x0d, 0x0a, 0x41, 0x73, - 0x69, 0x61, 0x2f, 0x53, 0x72, 0x65, 0x64, 0x6e, 0x65, 0x6b, 0x6f, 0x6c, 0x79, 0x6d, 0x73, 0x6b, - 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x54, 0x61, 0x69, 0x70, 0x65, 0x69, 0x0d, 0x0a, 0x41, - 0x73, 0x69, 0x61, 0x2f, 0x54, 0x61, 0x73, 0x68, 0x6b, 0x65, 0x6e, 0x74, 0x0d, 0x0a, 0x41, 0x73, - 0x69, 0x61, 0x2f, 0x54, 0x62, 0x69, 0x6c, 0x69, 0x73, 0x69, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, - 0x2f, 0x54, 0x65, 0x68, 0x72, 0x61, 0x6e, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x54, 0x65, - 0x6c, 0x5f, 0x41, 0x76, 0x69, 0x76, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x54, 0x68, 0x69, - 0x6d, 0x62, 0x75, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x54, 0x68, 0x69, 0x6d, 0x70, 0x68, - 0x75, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x54, 0x6f, 0x6b, 0x79, 0x6f, 0x0d, 0x0a, 0x41, - 0x73, 0x69, 0x61, 0x2f, 0x54, 0x6f, 0x6d, 0x73, 0x6b, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, - 0x55, 0x6a, 0x75, 0x6e, 0x67, 0x5f, 0x50, 0x61, 0x6e, 0x64, 0x61, 0x6e, 0x67, 0x0d, 0x0a, 0x41, - 0x73, 0x69, 0x61, 0x2f, 0x55, 0x6c, 0x61, 0x61, 0x6e, 0x62, 0x61, 0x61, 0x74, 0x61, 0x72, 0x0d, - 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x55, 0x6c, 0x61, 0x6e, 0x5f, 0x42, 0x61, 0x74, 0x6f, 0x72, - 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x55, 0x72, 0x75, 0x6d, 0x71, 0x69, 0x0d, 0x0a, 0x41, - 0x73, 0x69, 0x61, 0x2f, 0x55, 0x73, 0x74, 0x2d, 0x4e, 0x65, 0x72, 0x61, 0x0d, 0x0a, 0x41, 0x73, - 0x69, 0x61, 0x2f, 0x56, 0x69, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6e, 0x65, 0x0d, 0x0a, 0x41, 0x73, - 0x69, 0x61, 0x2f, 0x56, 0x6c, 0x61, 0x64, 0x69, 0x76, 0x6f, 0x73, 0x74, 0x6f, 0x6b, 0x0d, 0x0a, - 0x41, 0x73, 0x69, 0x61, 0x2f, 0x59, 0x61, 0x6b, 0x75, 0x74, 0x73, 0x6b, 0x0d, 0x0a, 0x41, 0x73, - 0x69, 0x61, 0x2f, 0x59, 0x61, 0x6e, 0x67, 0x6f, 0x6e, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, - 0x59, 0x65, 0x6b, 0x61, 0x74, 0x65, 0x72, 0x69, 0x6e, 0x62, 0x75, 0x72, 0x67, 0x0d, 0x0a, 0x41, - 0x73, 0x69, 0x61, 0x2f, 0x59, 0x65, 0x72, 0x65, 0x76, 0x61, 0x6e, 0x0d, 0x0a, 0x41, 0x74, 0x6c, - 0x61, 0x6e, 0x74, 0x69, 0x63, 0x2f, 0x41, 0x7a, 0x6f, 0x72, 0x65, 0x73, 0x0d, 0x0a, 0x41, 0x74, - 0x6c, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x2f, 0x42, 0x65, 0x72, 0x6d, 0x75, 0x64, 0x61, 0x0d, 0x0a, - 0x41, 0x74, 0x6c, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x2f, 0x43, 0x61, 0x6e, 0x61, 0x72, 0x79, 0x0d, - 0x0a, 0x41, 0x74, 0x6c, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x2f, 0x43, 0x61, 0x70, 0x65, 0x5f, 0x56, - 0x65, 0x72, 0x64, 0x65, 0x0d, 0x0a, 0x41, 0x74, 0x6c, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x2f, 0x46, - 0x61, 0x65, 0x72, 0x6f, 0x65, 0x0d, 0x0a, 0x41, 0x74, 0x6c, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x2f, - 0x46, 0x61, 0x72, 0x6f, 0x65, 0x0d, 0x0a, 0x41, 0x74, 0x6c, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x2f, - 0x4a, 0x61, 0x6e, 0x5f, 0x4d, 0x61, 0x79, 0x65, 0x6e, 0x0d, 0x0a, 0x41, 0x74, 0x6c, 0x61, 0x6e, - 0x74, 0x69, 0x63, 0x2f, 0x4d, 0x61, 0x64, 0x65, 0x69, 0x72, 0x61, 0x0d, 0x0a, 0x41, 0x74, 0x6c, - 0x61, 0x6e, 0x74, 0x69, 0x63, 0x2f, 0x52, 0x65, 0x79, 0x6b, 0x6a, 0x61, 0x76, 0x69, 0x6b, 0x0d, - 0x0a, 0x41, 0x74, 0x6c, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x2f, 0x53, 0x6f, 0x75, 0x74, 0x68, 0x5f, - 0x47, 0x65, 0x6f, 0x72, 0x67, 0x69, 0x61, 0x0d, 0x0a, 0x41, 0x74, 0x6c, 0x61, 0x6e, 0x74, 0x69, - 0x63, 0x2f, 0x53, 0x74, 0x61, 0x6e, 0x6c, 0x65, 0x79, 0x0d, 0x0a, 0x41, 0x74, 0x6c, 0x61, 0x6e, - 0x74, 0x69, 0x63, 0x2f, 0x53, 0x74, 0x5f, 0x48, 0x65, 0x6c, 0x65, 0x6e, 0x61, 0x0d, 0x0a, 0x41, - 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x41, 0x43, 0x54, 0x0d, 0x0a, 0x41, 0x75, - 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x41, 0x64, 0x65, 0x6c, 0x61, 0x69, 0x64, 0x65, - 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x42, 0x72, 0x69, 0x73, - 0x62, 0x61, 0x6e, 0x65, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, - 0x42, 0x72, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x48, 0x69, 0x6c, 0x6c, 0x0d, 0x0a, 0x41, 0x75, 0x73, - 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x43, 0x61, 0x6e, 0x62, 0x65, 0x72, 0x72, 0x61, 0x0d, - 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x43, 0x75, 0x72, 0x72, 0x69, - 0x65, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x44, 0x61, 0x72, - 0x77, 0x69, 0x6e, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x45, - 0x75, 0x63, 0x6c, 0x61, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, - 0x48, 0x6f, 0x62, 0x61, 0x72, 0x74, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, - 0x61, 0x2f, 0x4c, 0x48, 0x49, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, - 0x2f, 0x4c, 0x69, 0x6e, 0x64, 0x65, 0x6d, 0x61, 0x6e, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, - 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x4c, 0x6f, 0x72, 0x64, 0x5f, 0x48, 0x6f, 0x77, 0x65, 0x0d, 0x0a, - 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x4d, 0x65, 0x6c, 0x62, 0x6f, 0x75, - 0x72, 0x6e, 0x65, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x4e, - 0x6f, 0x72, 0x74, 0x68, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, - 0x4e, 0x53, 0x57, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x50, - 0x65, 0x72, 0x74, 0x68, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, - 0x51, 0x75, 0x65, 0x65, 0x6e, 0x73, 0x6c, 0x61, 0x6e, 0x64, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, - 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x53, 0x6f, 0x75, 0x74, 0x68, 0x0d, 0x0a, 0x41, 0x75, 0x73, - 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x53, 0x79, 0x64, 0x6e, 0x65, 0x79, 0x0d, 0x0a, 0x41, - 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x54, 0x61, 0x73, 0x6d, 0x61, 0x6e, 0x69, - 0x61, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x56, 0x69, 0x63, - 0x74, 0x6f, 0x72, 0x69, 0x61, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, - 0x2f, 0x57, 0x65, 0x73, 0x74, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, - 0x2f, 0x59, 0x61, 0x6e, 0x63, 0x6f, 0x77, 0x69, 0x6e, 0x6e, 0x61, 0x0d, 0x0a, 0x42, 0x72, 0x61, - 0x7a, 0x69, 0x6c, 0x2f, 0x41, 0x63, 0x72, 0x65, 0x0d, 0x0a, 0x42, 0x72, 0x61, 0x7a, 0x69, 0x6c, - 0x2f, 0x44, 0x65, 0x4e, 0x6f, 0x72, 0x6f, 0x6e, 0x68, 0x61, 0x0d, 0x0a, 0x42, 0x72, 0x61, 0x7a, - 0x69, 0x6c, 0x2f, 0x45, 0x61, 0x73, 0x74, 0x0d, 0x0a, 0x42, 0x72, 0x61, 0x7a, 0x69, 0x6c, 0x2f, - 0x57, 0x65, 0x73, 0x74, 0x0d, 0x0a, 0x43, 0x61, 0x6e, 0x61, 0x64, 0x61, 0x2f, 0x41, 0x74, 0x6c, - 0x61, 0x6e, 0x74, 0x69, 0x63, 0x0d, 0x0a, 0x43, 0x61, 0x6e, 0x61, 0x64, 0x61, 0x2f, 0x43, 0x65, - 0x6e, 0x74, 0x72, 0x61, 0x6c, 0x0d, 0x0a, 0x43, 0x61, 0x6e, 0x61, 0x64, 0x61, 0x2f, 0x45, 0x61, - 0x73, 0x74, 0x2d, 0x53, 0x61, 0x73, 0x6b, 0x61, 0x74, 0x63, 0x68, 0x65, 0x77, 0x61, 0x6e, 0x0d, - 0x0a, 0x43, 0x61, 0x6e, 0x61, 0x64, 0x61, 0x2f, 0x45, 0x61, 0x73, 0x74, 0x65, 0x72, 0x6e, 0x0d, - 0x0a, 0x43, 0x61, 0x6e, 0x61, 0x64, 0x61, 0x2f, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x61, 0x69, 0x6e, - 0x0d, 0x0a, 0x43, 0x61, 0x6e, 0x61, 0x64, 0x61, 0x2f, 0x4e, 0x65, 0x77, 0x66, 0x6f, 0x75, 0x6e, - 0x64, 0x6c, 0x61, 0x6e, 0x64, 0x0d, 0x0a, 0x43, 0x61, 0x6e, 0x61, 0x64, 0x61, 0x2f, 0x50, 0x61, - 0x63, 0x69, 0x66, 0x69, 0x63, 0x0d, 0x0a, 0x43, 0x61, 0x6e, 0x61, 0x64, 0x61, 0x2f, 0x53, 0x61, - 0x73, 0x6b, 0x61, 0x74, 0x63, 0x68, 0x65, 0x77, 0x61, 0x6e, 0x0d, 0x0a, 0x43, 0x61, 0x6e, 0x61, - 0x64, 0x61, 0x2f, 0x59, 0x75, 0x6b, 0x6f, 0x6e, 0x0d, 0x0a, 0x43, 0x68, 0x69, 0x6c, 0x65, 0x2f, - 0x43, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x0d, 0x0a, 0x43, 0x68, 0x69, - 0x6c, 0x65, 0x2f, 0x45, 0x61, 0x73, 0x74, 0x65, 0x72, 0x49, 0x73, 0x6c, 0x61, 0x6e, 0x64, 0x0d, - 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, - 0x54, 0x2b, 0x30, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2b, 0x31, 0x0d, 0x0a, - 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2b, 0x31, 0x30, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, - 0x47, 0x4d, 0x54, 0x2b, 0x31, 0x31, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2b, - 0x31, 0x32, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2b, 0x32, 0x0d, 0x0a, 0x45, - 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2b, 0x33, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, - 0x54, 0x2b, 0x34, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2b, 0x35, 0x0d, 0x0a, - 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2b, 0x36, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, - 0x4d, 0x54, 0x2b, 0x37, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2b, 0x38, 0x0d, - 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2b, 0x39, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, - 0x47, 0x4d, 0x54, 0x2d, 0x30, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2d, 0x31, - 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2d, 0x31, 0x30, 0x0d, 0x0a, 0x45, 0x74, - 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2d, 0x31, 0x31, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, - 0x54, 0x2d, 0x31, 0x32, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2d, 0x31, 0x33, - 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2d, 0x31, 0x34, 0x0d, 0x0a, 0x45, 0x74, - 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2d, 0x32, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, - 0x2d, 0x33, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2d, 0x34, 0x0d, 0x0a, 0x45, - 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2d, 0x35, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, - 0x54, 0x2d, 0x36, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2d, 0x37, 0x0d, 0x0a, - 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2d, 0x38, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, - 0x4d, 0x54, 0x2d, 0x39, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x30, 0x0d, 0x0a, - 0x45, 0x74, 0x63, 0x2f, 0x47, 0x72, 0x65, 0x65, 0x6e, 0x77, 0x69, 0x63, 0x68, 0x0d, 0x0a, 0x45, - 0x74, 0x63, 0x2f, 0x55, 0x43, 0x54, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x55, 0x6e, 0x69, 0x76, - 0x65, 0x72, 0x73, 0x61, 0x6c, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x55, 0x54, 0x43, 0x0d, 0x0a, - 0x45, 0x74, 0x63, 0x2f, 0x5a, 0x75, 0x6c, 0x75, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, - 0x2f, 0x41, 0x6d, 0x73, 0x74, 0x65, 0x72, 0x64, 0x61, 0x6d, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, - 0x70, 0x65, 0x2f, 0x41, 0x6e, 0x64, 0x6f, 0x72, 0x72, 0x61, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, - 0x70, 0x65, 0x2f, 0x41, 0x73, 0x74, 0x72, 0x61, 0x6b, 0x68, 0x61, 0x6e, 0x0d, 0x0a, 0x45, 0x75, - 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x41, 0x74, 0x68, 0x65, 0x6e, 0x73, 0x0d, 0x0a, 0x45, 0x75, 0x72, - 0x6f, 0x70, 0x65, 0x2f, 0x42, 0x65, 0x6c, 0x66, 0x61, 0x73, 0x74, 0x0d, 0x0a, 0x45, 0x75, 0x72, - 0x6f, 0x70, 0x65, 0x2f, 0x42, 0x65, 0x6c, 0x67, 0x72, 0x61, 0x64, 0x65, 0x0d, 0x0a, 0x45, 0x75, - 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x42, 0x65, 0x72, 0x6c, 0x69, 0x6e, 0x0d, 0x0a, 0x45, 0x75, 0x72, - 0x6f, 0x70, 0x65, 0x2f, 0x42, 0x72, 0x61, 0x74, 0x69, 0x73, 0x6c, 0x61, 0x76, 0x61, 0x0d, 0x0a, - 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x42, 0x72, 0x75, 0x73, 0x73, 0x65, 0x6c, 0x73, 0x0d, - 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x42, 0x75, 0x63, 0x68, 0x61, 0x72, 0x65, 0x73, - 0x74, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x42, 0x75, 0x64, 0x61, 0x70, 0x65, - 0x73, 0x74, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x42, 0x75, 0x73, 0x69, 0x6e, - 0x67, 0x65, 0x6e, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x43, 0x68, 0x69, 0x73, - 0x69, 0x6e, 0x61, 0x75, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x43, 0x6f, 0x70, - 0x65, 0x6e, 0x68, 0x61, 0x67, 0x65, 0x6e, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, - 0x44, 0x75, 0x62, 0x6c, 0x69, 0x6e, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x47, - 0x69, 0x62, 0x72, 0x61, 0x6c, 0x74, 0x61, 0x72, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, - 0x2f, 0x47, 0x75, 0x65, 0x72, 0x6e, 0x73, 0x65, 0x79, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, - 0x65, 0x2f, 0x48, 0x65, 0x6c, 0x73, 0x69, 0x6e, 0x6b, 0x69, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, - 0x70, 0x65, 0x2f, 0x49, 0x73, 0x6c, 0x65, 0x5f, 0x6f, 0x66, 0x5f, 0x4d, 0x61, 0x6e, 0x0d, 0x0a, - 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x49, 0x73, 0x74, 0x61, 0x6e, 0x62, 0x75, 0x6c, 0x0d, - 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4a, 0x65, 0x72, 0x73, 0x65, 0x79, 0x0d, 0x0a, - 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4b, 0x61, 0x6c, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x72, - 0x61, 0x64, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4b, 0x69, 0x65, 0x76, 0x0d, - 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4b, 0x69, 0x72, 0x6f, 0x76, 0x0d, 0x0a, 0x45, - 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4c, 0x69, 0x73, 0x62, 0x6f, 0x6e, 0x0d, 0x0a, 0x45, 0x75, - 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4c, 0x6a, 0x75, 0x62, 0x6c, 0x6a, 0x61, 0x6e, 0x61, 0x0d, 0x0a, - 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4c, 0x6f, 0x6e, 0x64, 0x6f, 0x6e, 0x0d, 0x0a, 0x45, - 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4c, 0x75, 0x78, 0x65, 0x6d, 0x62, 0x6f, 0x75, 0x72, 0x67, - 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4d, 0x61, 0x64, 0x72, 0x69, 0x64, 0x0d, - 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4d, 0x61, 0x6c, 0x74, 0x61, 0x0d, 0x0a, 0x45, - 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4d, 0x61, 0x72, 0x69, 0x65, 0x68, 0x61, 0x6d, 0x6e, 0x0d, - 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4d, 0x69, 0x6e, 0x73, 0x6b, 0x0d, 0x0a, 0x45, - 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4d, 0x6f, 0x6e, 0x61, 0x63, 0x6f, 0x0d, 0x0a, 0x45, 0x75, - 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4d, 0x6f, 0x73, 0x63, 0x6f, 0x77, 0x0d, 0x0a, 0x45, 0x75, 0x72, - 0x6f, 0x70, 0x65, 0x2f, 0x4e, 0x69, 0x63, 0x6f, 0x73, 0x69, 0x61, 0x0d, 0x0a, 0x45, 0x75, 0x72, - 0x6f, 0x70, 0x65, 0x2f, 0x4f, 0x73, 0x6c, 0x6f, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, - 0x2f, 0x50, 0x61, 0x72, 0x69, 0x73, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x50, - 0x6f, 0x64, 0x67, 0x6f, 0x72, 0x69, 0x63, 0x61, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, - 0x2f, 0x50, 0x72, 0x61, 0x67, 0x75, 0x65, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, - 0x52, 0x69, 0x67, 0x61, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x52, 0x6f, 0x6d, - 0x65, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x53, 0x61, 0x6d, 0x61, 0x72, 0x61, - 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x53, 0x61, 0x6e, 0x5f, 0x4d, 0x61, 0x72, - 0x69, 0x6e, 0x6f, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x53, 0x61, 0x72, 0x61, - 0x6a, 0x65, 0x76, 0x6f, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x53, 0x61, 0x72, - 0x61, 0x74, 0x6f, 0x76, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x53, 0x69, 0x6d, - 0x66, 0x65, 0x72, 0x6f, 0x70, 0x6f, 0x6c, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, - 0x53, 0x6b, 0x6f, 0x70, 0x6a, 0x65, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x53, - 0x6f, 0x66, 0x69, 0x61, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x53, 0x74, 0x6f, - 0x63, 0x6b, 0x68, 0x6f, 0x6c, 0x6d, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x54, - 0x61, 0x6c, 0x6c, 0x69, 0x6e, 0x6e, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x54, - 0x69, 0x72, 0x61, 0x6e, 0x65, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x54, 0x69, - 0x72, 0x61, 0x73, 0x70, 0x6f, 0x6c, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x55, - 0x6c, 0x79, 0x61, 0x6e, 0x6f, 0x76, 0x73, 0x6b, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, - 0x2f, 0x55, 0x7a, 0x68, 0x67, 0x6f, 0x72, 0x6f, 0x64, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, - 0x65, 0x2f, 0x56, 0x61, 0x64, 0x75, 0x7a, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, - 0x56, 0x61, 0x74, 0x69, 0x63, 0x61, 0x6e, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, - 0x56, 0x69, 0x65, 0x6e, 0x6e, 0x61, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x56, - 0x69, 0x6c, 0x6e, 0x69, 0x75, 0x73, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x56, - 0x6f, 0x6c, 0x67, 0x6f, 0x67, 0x72, 0x61, 0x64, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, - 0x2f, 0x57, 0x61, 0x72, 0x73, 0x61, 0x77, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, - 0x5a, 0x61, 0x67, 0x72, 0x65, 0x62, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x5a, - 0x61, 0x70, 0x6f, 0x72, 0x6f, 0x7a, 0x68, 0x79, 0x65, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, - 0x65, 0x2f, 0x5a, 0x75, 0x72, 0x69, 0x63, 0x68, 0x0d, 0x0a, 0x49, 0x6e, 0x64, 0x69, 0x61, 0x6e, - 0x2f, 0x41, 0x6e, 0x74, 0x61, 0x6e, 0x61, 0x6e, 0x61, 0x72, 0x69, 0x76, 0x6f, 0x0d, 0x0a, 0x49, - 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x2f, 0x43, 0x68, 0x61, 0x67, 0x6f, 0x73, 0x0d, 0x0a, 0x49, 0x6e, - 0x64, 0x69, 0x61, 0x6e, 0x2f, 0x43, 0x68, 0x72, 0x69, 0x73, 0x74, 0x6d, 0x61, 0x73, 0x0d, 0x0a, - 0x49, 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x2f, 0x43, 0x6f, 0x63, 0x6f, 0x73, 0x0d, 0x0a, 0x49, 0x6e, - 0x64, 0x69, 0x61, 0x6e, 0x2f, 0x43, 0x6f, 0x6d, 0x6f, 0x72, 0x6f, 0x0d, 0x0a, 0x49, 0x6e, 0x64, - 0x69, 0x61, 0x6e, 0x2f, 0x4b, 0x65, 0x72, 0x67, 0x75, 0x65, 0x6c, 0x65, 0x6e, 0x0d, 0x0a, 0x49, - 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x2f, 0x4d, 0x61, 0x68, 0x65, 0x0d, 0x0a, 0x49, 0x6e, 0x64, 0x69, - 0x61, 0x6e, 0x2f, 0x4d, 0x61, 0x6c, 0x64, 0x69, 0x76, 0x65, 0x73, 0x0d, 0x0a, 0x49, 0x6e, 0x64, - 0x69, 0x61, 0x6e, 0x2f, 0x4d, 0x61, 0x75, 0x72, 0x69, 0x74, 0x69, 0x75, 0x73, 0x0d, 0x0a, 0x49, - 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x2f, 0x4d, 0x61, 0x79, 0x6f, 0x74, 0x74, 0x65, 0x0d, 0x0a, 0x49, - 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x2f, 0x52, 0x65, 0x75, 0x6e, 0x69, 0x6f, 0x6e, 0x0d, 0x0a, 0x4d, - 0x65, 0x78, 0x69, 0x63, 0x6f, 0x2f, 0x42, 0x61, 0x6a, 0x61, 0x4e, 0x6f, 0x72, 0x74, 0x65, 0x0d, - 0x0a, 0x4d, 0x65, 0x78, 0x69, 0x63, 0x6f, 0x2f, 0x42, 0x61, 0x6a, 0x61, 0x53, 0x75, 0x72, 0x0d, - 0x0a, 0x4d, 0x65, 0x78, 0x69, 0x63, 0x6f, 0x2f, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x0d, - 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x41, 0x70, 0x69, 0x61, 0x0d, 0x0a, 0x50, - 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x41, 0x75, 0x63, 0x6b, 0x6c, 0x61, 0x6e, 0x64, 0x0d, - 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x42, 0x6f, 0x75, 0x67, 0x61, 0x69, 0x6e, - 0x76, 0x69, 0x6c, 0x6c, 0x65, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x43, - 0x68, 0x61, 0x74, 0x68, 0x61, 0x6d, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, - 0x43, 0x68, 0x75, 0x75, 0x6b, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x45, - 0x61, 0x73, 0x74, 0x65, 0x72, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x45, - 0x66, 0x61, 0x74, 0x65, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x45, 0x6e, - 0x64, 0x65, 0x72, 0x62, 0x75, 0x72, 0x79, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, - 0x2f, 0x46, 0x61, 0x6b, 0x61, 0x6f, 0x66, 0x6f, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, - 0x63, 0x2f, 0x46, 0x69, 0x6a, 0x69, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, - 0x46, 0x75, 0x6e, 0x61, 0x66, 0x75, 0x74, 0x69, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, - 0x63, 0x2f, 0x47, 0x61, 0x6c, 0x61, 0x70, 0x61, 0x67, 0x6f, 0x73, 0x0d, 0x0a, 0x50, 0x61, 0x63, - 0x69, 0x66, 0x69, 0x63, 0x2f, 0x47, 0x61, 0x6d, 0x62, 0x69, 0x65, 0x72, 0x0d, 0x0a, 0x50, 0x61, - 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x47, 0x75, 0x61, 0x64, 0x61, 0x6c, 0x63, 0x61, 0x6e, 0x61, - 0x6c, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x47, 0x75, 0x61, 0x6d, 0x0d, - 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x48, 0x6f, 0x6e, 0x6f, 0x6c, 0x75, 0x6c, - 0x75, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x4a, 0x6f, 0x68, 0x6e, 0x73, - 0x74, 0x6f, 0x6e, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x4b, 0x69, 0x72, - 0x69, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x69, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, - 0x2f, 0x4b, 0x6f, 0x73, 0x72, 0x61, 0x65, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, - 0x2f, 0x4b, 0x77, 0x61, 0x6a, 0x61, 0x6c, 0x65, 0x69, 0x6e, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, - 0x66, 0x69, 0x63, 0x2f, 0x4d, 0x61, 0x6a, 0x75, 0x72, 0x6f, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, - 0x66, 0x69, 0x63, 0x2f, 0x4d, 0x61, 0x72, 0x71, 0x75, 0x65, 0x73, 0x61, 0x73, 0x0d, 0x0a, 0x50, - 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x4d, 0x69, 0x64, 0x77, 0x61, 0x79, 0x0d, 0x0a, 0x50, - 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x4e, 0x61, 0x75, 0x72, 0x75, 0x0d, 0x0a, 0x50, 0x61, - 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x4e, 0x69, 0x75, 0x65, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, - 0x66, 0x69, 0x63, 0x2f, 0x4e, 0x6f, 0x72, 0x66, 0x6f, 0x6c, 0x6b, 0x0d, 0x0a, 0x50, 0x61, 0x63, - 0x69, 0x66, 0x69, 0x63, 0x2f, 0x4e, 0x6f, 0x75, 0x6d, 0x65, 0x61, 0x0d, 0x0a, 0x50, 0x61, 0x63, - 0x69, 0x66, 0x69, 0x63, 0x2f, 0x50, 0x61, 0x67, 0x6f, 0x5f, 0x50, 0x61, 0x67, 0x6f, 0x0d, 0x0a, - 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x50, 0x61, 0x6c, 0x61, 0x75, 0x0d, 0x0a, 0x50, - 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x50, 0x69, 0x74, 0x63, 0x61, 0x69, 0x72, 0x6e, 0x0d, - 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x50, 0x6f, 0x68, 0x6e, 0x70, 0x65, 0x69, - 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x50, 0x6f, 0x6e, 0x61, 0x70, 0x65, - 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x50, 0x6f, 0x72, 0x74, 0x5f, 0x4d, - 0x6f, 0x72, 0x65, 0x73, 0x62, 0x79, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, - 0x52, 0x61, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x67, 0x61, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, - 0x69, 0x63, 0x2f, 0x53, 0x61, 0x69, 0x70, 0x61, 0x6e, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, - 0x69, 0x63, 0x2f, 0x53, 0x61, 0x6d, 0x6f, 0x61, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, - 0x63, 0x2f, 0x54, 0x61, 0x68, 0x69, 0x74, 0x69, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, - 0x63, 0x2f, 0x54, 0x61, 0x72, 0x61, 0x77, 0x61, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, - 0x63, 0x2f, 0x54, 0x6f, 0x6e, 0x67, 0x61, 0x74, 0x61, 0x70, 0x75, 0x0d, 0x0a, 0x50, 0x61, 0x63, - 0x69, 0x66, 0x69, 0x63, 0x2f, 0x54, 0x72, 0x75, 0x6b, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, - 0x69, 0x63, 0x2f, 0x57, 0x61, 0x6b, 0x65, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, - 0x2f, 0x57, 0x61, 0x6c, 0x6c, 0x69, 0x73, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, - 0x2f, 0x59, 0x61, 0x70, 0x0d, 0x0a, 0x55, 0x53, 0x2f, 0x41, 0x6c, 0x61, 0x73, 0x6b, 0x61, 0x0d, - 0x0a, 0x55, 0x53, 0x2f, 0x41, 0x6c, 0x65, 0x75, 0x74, 0x69, 0x61, 0x6e, 0x0d, 0x0a, 0x55, 0x53, - 0x2f, 0x41, 0x72, 0x69, 0x7a, 0x6f, 0x6e, 0x61, 0x0d, 0x0a, 0x55, 0x53, 0x2f, 0x43, 0x65, 0x6e, - 0x74, 0x72, 0x61, 0x6c, 0x0d, 0x0a, 0x55, 0x53, 0x2f, 0x45, 0x61, 0x73, 0x74, 0x2d, 0x49, 0x6e, - 0x64, 0x69, 0x61, 0x6e, 0x61, 0x0d, 0x0a, 0x55, 0x53, 0x2f, 0x45, 0x61, 0x73, 0x74, 0x65, 0x72, - 0x6e, 0x0d, 0x0a, 0x55, 0x53, 0x2f, 0x48, 0x61, 0x77, 0x61, 0x69, 0x69, 0x0d, 0x0a, 0x55, 0x53, - 0x2f, 0x49, 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x61, 0x2d, 0x53, 0x74, 0x61, 0x72, 0x6b, 0x65, 0x0d, - 0x0a, 0x55, 0x53, 0x2f, 0x4d, 0x69, 0x63, 0x68, 0x69, 0x67, 0x61, 0x6e, 0x0d, 0x0a, 0x55, 0x53, - 0x2f, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x0d, 0x0a, 0x55, 0x53, 0x2f, 0x50, 0x61, - 0x63, 0x69, 0x66, 0x69, 0x63, 0x0d, 0x0a, 0x55, 0x53, 0x2f, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, - 0x63, 0x2d, 0x4e, 0x65, 0x77, 0x0d, 0x0a, 0x55, 0x53, 0x2f, 0x53, 0x61, 0x6d, 0x6f, 0x61, 0x0d, - 0x0a}; +#include "nx_tzdb.h" -static VirtualFile GenerateDefaultTimeZoneFile() { - struct TimeZoneInfo { - s64_be at; - std::array<u8, 7> padding1; - std::array<char, 4> time_zone_chars; - std::array<u8, 2> padding2; - std::array<char, 6> time_zone_name; - }; +namespace FileSys::SystemArchive { - VirtualFile file{std::make_shared<VectorVfsFile>( - std::vector<u8>(sizeof(Service::Time::TimeZone::TzifHeader) + sizeof(TimeZoneInfo)), - "GMT")}; +const static std::map<std::string, const std::map<const char*, const std::vector<u8>>&> + tzdb_zoneinfo_dirs = {{"Africa", NxTzdb::africa}, + {"America", NxTzdb::america}, + {"Antarctica", NxTzdb::antarctica}, + {"Arctic", NxTzdb::arctic}, + {"Asia", NxTzdb::asia}, + {"Atlantic", NxTzdb::atlantic}, + {"Australia", NxTzdb::australia}, + {"Brazil", NxTzdb::brazil}, + {"Canada", NxTzdb::canada}, + {"Chile", NxTzdb::chile}, + {"Etc", NxTzdb::etc}, + {"Europe", NxTzdb::europe}, + {"Indian", NxTzdb::indian}, + {"Mexico", NxTzdb::mexico}, + {"Pacific", NxTzdb::pacific}, + {"US", NxTzdb::us}}; - const Service::Time::TimeZone::TzifHeader header{ - .magic = 0x545a6966, - .version = 0x32, - .ttis_gmt_count = 1, - .ttis_std_count = 1, - .time_count = 1, - .type_count = 1, - .char_count = 4, - }; - file->WriteObject(header, 0); +const static std::map<std::string, const std::map<const char*, const std::vector<u8>>&> + tzdb_america_dirs = {{"Argentina", NxTzdb::america_argentina}, + {"Indiana", NxTzdb::america_indiana}, + {"Kentucky", NxTzdb::america_kentucky}, + {"North_Dakota", NxTzdb::america_north_dakota}}; - const TimeZoneInfo time_zone_info{ - .at = 0xf8, - .padding1 = {}, - .time_zone_chars = {'G', 'M', 'T', '\0'}, - .padding2 = {}, - .time_zone_name = {'\n', 'G', 'M', 'T', '0', '\n'}, - }; - file->WriteObject(time_zone_info, sizeof(Service::Time::TimeZone::TzifHeader)); +static void GenerateFiles(std::vector<VirtualFile>& directory, + const std::map<const char*, const std::vector<u8>>& files) { + for (const auto& [filename, data] : files) { + const auto data_copy{data}; + const std::string filename_copy{filename}; + VirtualFile file{ + std::make_shared<VectorVfsFile>(std::move(data_copy), std::move(filename_copy))}; + directory.push_back(file); + } +} - return file; +static std::vector<VirtualFile> GenerateZoneinfoFiles() { + std::vector<VirtualFile> zoneinfo_files; + GenerateFiles(zoneinfo_files, NxTzdb::zoneinfo); + return zoneinfo_files; } VirtualDir TimeZoneBinary() { - std::vector<VirtualDir> root_dirs{std::make_shared<VectorVfsDirectory>( - std::vector<VirtualFile>{GenerateDefaultTimeZoneFile()}, std::vector<VirtualDir>{}, - "zoneinfo")}; - std::vector<VirtualFile> root_files{MakeArrayFile(LOCATION_NAMES, "binaryList.txt")}; + std::vector<VirtualDir> america_sub_dirs; + for (const auto& [dir_name, files] : tzdb_america_dirs) { + std::vector<VirtualFile> vfs_files; + GenerateFiles(vfs_files, files); + america_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>( + std::move(vfs_files), std::vector<VirtualDir>{}, dir_name)); + } + + std::vector<VirtualDir> zoneinfo_sub_dirs; + for (const auto& [dir_name, files] : tzdb_zoneinfo_dirs) { + std::vector<VirtualFile> vfs_files; + GenerateFiles(vfs_files, files); + if (dir_name == "America") { + zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>( + std::move(vfs_files), std::move(america_sub_dirs), dir_name)); + } else { + zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>( + std::move(vfs_files), std::vector<VirtualDir>{}, dir_name)); + } + } + + std::vector<VirtualDir> zoneinfo_dir{std::make_shared<VectorVfsDirectory>( + GenerateZoneinfoFiles(), std::move(zoneinfo_sub_dirs), "zoneinfo")}; + std::vector<VirtualFile> root_files; + GenerateFiles(root_files, NxTzdb::base); - return std::make_shared<VectorVfsDirectory>(std::move(root_files), std::move(root_dirs), + return std::make_shared<VectorVfsDirectory>(std::move(root_files), std::move(zoneinfo_dir), "data"); } diff --git a/src/core/file_sys/vfs_concat.cpp b/src/core/file_sys/vfs_concat.cpp index 853b893a1f..311a59e5fe 100644 --- a/src/core/file_sys/vfs_concat.cpp +++ b/src/core/file_sys/vfs_concat.cpp @@ -150,23 +150,29 @@ std::size_t ConcatenatedVfsFile::Read(u8* data, std::size_t length, std::size_t while (cur_length > 0 && it != concatenation_map.end()) { // Check if we can read the file at this position. const auto& file = it->file; - const u64 file_offset = it->offset; + const u64 map_offset = it->offset; const u64 file_size = file->GetSize(); - if (cur_offset >= file_offset + file_size) { + if (cur_offset > map_offset + file_size) { // Entirely out of bounds read. break; } // Read the file at this position. - const u64 intended_read_size = std::min<u64>(cur_length, file_size); + const u64 file_seek = cur_offset - map_offset; + const u64 intended_read_size = std::min<u64>(cur_length, file_size - file_seek); const u64 actual_read_size = - file->Read(data + (cur_offset - offset), intended_read_size, cur_offset - file_offset); + file->Read(data + (cur_offset - offset), intended_read_size, file_seek); // Update tracking. cur_offset += actual_read_size; cur_length -= actual_read_size; it++; + + // If we encountered a short read, we're done. + if (actual_read_size < intended_read_size) { + break; + } } return cur_offset - offset; diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp index cc00762385..b0515ec058 100644 --- a/src/core/file_sys/vfs_real.cpp +++ b/src/core/file_sys/vfs_real.cpp @@ -10,6 +10,7 @@ #include "common/fs/fs.h" #include "common/fs/path_util.h" #include "common/logging/log.h" +#include "core/file_sys/vfs.h" #include "core/file_sys/vfs_real.h" // For FileTimeStampRaw @@ -25,6 +26,8 @@ namespace FS = Common::FS; namespace { +constexpr size_t MaxOpenFiles = 512; + constexpr FS::FileAccessMode ModeFlagsToFileAccessMode(Mode mode) { switch (mode) { case Mode::Read: @@ -70,31 +73,42 @@ VfsEntryType RealVfsFilesystem::GetEntryType(std::string_view path_) const { return VfsEntryType::File; } -VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) { +VirtualFile RealVfsFilesystem::OpenFileFromEntry(std::string_view path_, std::optional<u64> size, + Mode perms) { const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); + std::scoped_lock lk{list_lock}; - if (const auto weak_iter = cache.find(path); weak_iter != cache.cend()) { - const auto& weak = weak_iter->second; - - if (!weak.expired()) { - return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, weak.lock(), path, perms)); + if (auto it = cache.find(path); it != cache.end()) { + if (auto file = it->second.lock(); file) { + return file; } } - auto backing = FS::FileOpen(path, ModeFlagsToFileAccessMode(perms), FS::FileType::BinaryFile); - - if (!backing) { + if (!size && !FS::IsFile(path)) { return nullptr; } - cache.insert_or_assign(path, std::move(backing)); + auto reference = std::make_unique<FileReference>(); + this->InsertReferenceIntoListLocked(*reference); - // Cannot use make_shared as RealVfsFile constructor is private - return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, backing, path, perms)); + auto file = std::shared_ptr<RealVfsFile>( + new RealVfsFile(*this, std::move(reference), path, perms, size)); + cache[path] = file; + + return file; +} + +VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) { + return OpenFileFromEntry(path_, {}, perms); } VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, Mode perms) { const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); + { + std::scoped_lock lk{list_lock}; + cache.erase(path); + } + // Current usages of CreateFile expect to delete the contents of an existing file. if (FS::IsFile(path)) { FS::IOFile temp{path, FS::FileAccessMode::Write, FS::FileType::BinaryFile}; @@ -123,51 +137,28 @@ VirtualFile RealVfsFilesystem::CopyFile(std::string_view old_path_, std::string_ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) { const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault); const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault); - const auto cached_file_iter = cache.find(old_path); - - if (cached_file_iter != cache.cend()) { - auto file = cached_file_iter->second.lock(); - - if (!cached_file_iter->second.expired()) { - file->Close(); - } - - if (!FS::RenameFile(old_path, new_path)) { - return nullptr; - } - + { + std::scoped_lock lk{list_lock}; cache.erase(old_path); - file->Open(new_path, FS::FileAccessMode::Read, FS::FileType::BinaryFile); - if (file->IsOpen()) { - cache.insert_or_assign(new_path, std::move(file)); - } else { - LOG_ERROR(Service_FS, "Failed to open path {} in order to re-cache it", new_path); - } - } else { - ASSERT(false); + cache.erase(new_path); + } + if (!FS::RenameFile(old_path, new_path)) { return nullptr; } - return OpenFile(new_path, Mode::ReadWrite); } bool RealVfsFilesystem::DeleteFile(std::string_view path_) { const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); - const auto cached_iter = cache.find(path); - - if (cached_iter != cache.cend()) { - if (!cached_iter->second.expired()) { - cached_iter->second.lock()->Close(); - } + { + std::scoped_lock lk{list_lock}; cache.erase(path); } - return FS::RemoveFile(path); } VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) { const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); - // Cannot use make_shared as RealVfsDirectory constructor is private return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms)); } @@ -176,7 +167,6 @@ VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, Mode perms if (!FS::CreateDirs(path)) { return nullptr; } - // Cannot use make_shared as RealVfsDirectory constructor is private return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms)); } @@ -194,73 +184,112 @@ VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_, if (!FS::RenameDir(old_path, new_path)) { return nullptr; } + return OpenDirectory(new_path, Mode::ReadWrite); +} - for (auto& kv : cache) { - // If the path in the cache doesn't start with old_path, then bail on this file. - if (kv.first.rfind(old_path, 0) != 0) { - continue; - } +bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) { + const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); + return FS::RemoveDirRecursively(path); +} - const auto file_old_path = - FS::SanitizePath(kv.first, FS::DirectorySeparator::PlatformDefault); - auto file_new_path = FS::SanitizePath(new_path + '/' + kv.first.substr(old_path.size()), - FS::DirectorySeparator::PlatformDefault); - const auto& cached = cache[file_old_path]; +std::unique_lock<std::mutex> RealVfsFilesystem::RefreshReference(const std::string& path, + Mode perms, + FileReference& reference) { + std::unique_lock lk{list_lock}; - if (cached.expired()) { - continue; - } + // Temporarily remove from list. + this->RemoveReferenceFromListLocked(reference); + + // Restore file if needed. + if (!reference.file) { + this->EvictSingleReferenceLocked(); - auto file = cached.lock(); - cache.erase(file_old_path); - file->Open(file_new_path, FS::FileAccessMode::Read, FS::FileType::BinaryFile); - if (file->IsOpen()) { - cache.insert_or_assign(std::move(file_new_path), std::move(file)); - } else { - LOG_ERROR(Service_FS, "Failed to open path {} in order to re-cache it", file_new_path); + reference.file = + FS::FileOpen(path, ModeFlagsToFileAccessMode(perms), FS::FileType::BinaryFile); + if (reference.file) { + num_open_files++; } } - return OpenDirectory(new_path, Mode::ReadWrite); + // Reinsert into list. + this->InsertReferenceIntoListLocked(reference); + + return lk; } -bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) { - const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); +void RealVfsFilesystem::DropReference(std::unique_ptr<FileReference>&& reference) { + std::scoped_lock lk{list_lock}; - for (auto& kv : cache) { - // If the path in the cache doesn't start with path, then bail on this file. - if (kv.first.rfind(path, 0) != 0) { - continue; - } + // Remove from list. + this->RemoveReferenceFromListLocked(*reference); - const auto& entry = cache[kv.first]; - if (!entry.expired()) { - entry.lock()->Close(); - } + // Close the file. + if (reference->file) { + reference->file.reset(); + num_open_files--; + } +} - cache.erase(kv.first); +void RealVfsFilesystem::EvictSingleReferenceLocked() { + if (num_open_files < MaxOpenFiles || open_references.empty()) { + return; } - return FS::RemoveDirRecursively(path); + // Get and remove from list. + auto& reference = open_references.back(); + this->RemoveReferenceFromListLocked(reference); + + // Close the file. + if (reference.file) { + reference.file.reset(); + num_open_files--; + } + + // Reinsert into closed list. + this->InsertReferenceIntoListLocked(reference); +} + +void RealVfsFilesystem::InsertReferenceIntoListLocked(FileReference& reference) { + if (reference.file) { + open_references.push_front(reference); + } else { + closed_references.push_front(reference); + } } -RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::shared_ptr<FS::IOFile> backing_, - const std::string& path_, Mode perms_) - : base(base_), backing(std::move(backing_)), path(path_), parent_path(FS::GetParentPath(path_)), - path_components(FS::SplitPathComponents(path_)), perms(perms_) {} +void RealVfsFilesystem::RemoveReferenceFromListLocked(FileReference& reference) { + if (reference.file) { + open_references.erase(open_references.iterator_to(reference)); + } else { + closed_references.erase(closed_references.iterator_to(reference)); + } +} -RealVfsFile::~RealVfsFile() = default; +RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::unique_ptr<FileReference> reference_, + const std::string& path_, Mode perms_, std::optional<u64> size_) + : base(base_), reference(std::move(reference_)), path(path_), + parent_path(FS::GetParentPath(path_)), path_components(FS::SplitPathComponents(path_)), + size(size_), perms(perms_) {} + +RealVfsFile::~RealVfsFile() { + base.DropReference(std::move(reference)); +} std::string RealVfsFile::GetName() const { return path_components.back(); } std::size_t RealVfsFile::GetSize() const { - return backing->GetSize(); + if (size) { + return *size; + } + return FS::GetSize(path); } bool RealVfsFile::Resize(std::size_t new_size) { - return backing->SetSize(new_size); + size.reset(); + auto lk = base.RefreshReference(path, perms, *reference); + return reference->file ? reference->file->SetSize(new_size) : false; } VirtualDir RealVfsFile::GetContainingDirectory() const { @@ -276,27 +305,26 @@ bool RealVfsFile::IsReadable() const { } std::size_t RealVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const { - if (!backing->Seek(static_cast<s64>(offset))) { + auto lk = base.RefreshReference(path, perms, *reference); + if (!reference->file || !reference->file->Seek(static_cast<s64>(offset))) { return 0; } - return backing->ReadSpan(std::span{data, length}); + return reference->file->ReadSpan(std::span{data, length}); } std::size_t RealVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) { - if (!backing->Seek(static_cast<s64>(offset))) { + size.reset(); + auto lk = base.RefreshReference(path, perms, *reference); + if (!reference->file || !reference->file->Seek(static_cast<s64>(offset))) { return 0; } - return backing->WriteSpan(std::span{data, length}); + return reference->file->WriteSpan(std::span{data, length}); } bool RealVfsFile::Rename(std::string_view name) { return base.MoveFile(path, parent_path + '/' + std::string(name)) != nullptr; } -void RealVfsFile::Close() { - backing->Close(); -} - // TODO(DarkLordZach): MSVC would not let me combine the following two functions using 'if // constexpr' because there is a compile error in the branch not used. @@ -308,10 +336,11 @@ std::vector<VirtualFile> RealVfsDirectory::IterateEntries<RealVfsFile, VfsFile>( std::vector<VirtualFile> out; - const FS::DirEntryCallable callback = [this, &out](const std::filesystem::path& full_path) { - const auto full_path_string = FS::PathToUTF8String(full_path); + const FS::DirEntryCallable callback = [this, + &out](const std::filesystem::directory_entry& entry) { + const auto full_path_string = FS::PathToUTF8String(entry.path()); - out.emplace_back(base.OpenFile(full_path_string, perms)); + out.emplace_back(base.OpenFileFromEntry(full_path_string, entry.file_size(), perms)); return true; }; @@ -329,8 +358,9 @@ std::vector<VirtualDir> RealVfsDirectory::IterateEntries<RealVfsDirectory, VfsDi std::vector<VirtualDir> out; - const FS::DirEntryCallable callback = [this, &out](const std::filesystem::path& full_path) { - const auto full_path_string = FS::PathToUTF8String(full_path); + const FS::DirEntryCallable callback = [this, + &out](const std::filesystem::directory_entry& entry) { + const auto full_path_string = FS::PathToUTF8String(entry.path()); out.emplace_back(base.OpenDirectory(full_path_string, perms)); @@ -482,12 +512,10 @@ std::map<std::string, VfsEntryType, std::less<>> RealVfsDirectory::GetEntries() std::map<std::string, VfsEntryType, std::less<>> out; - const FS::DirEntryCallable callback = [&out](const std::filesystem::path& full_path) { - const auto filename = FS::PathToUTF8String(full_path.filename()); - + const FS::DirEntryCallable callback = [&out](const std::filesystem::directory_entry& entry) { + const auto filename = FS::PathToUTF8String(entry.path().filename()); out.insert_or_assign(filename, - FS::IsDir(full_path) ? VfsEntryType::Directory : VfsEntryType::File); - + entry.is_directory() ? VfsEntryType::Directory : VfsEntryType::File); return true; }; diff --git a/src/core/file_sys/vfs_real.h b/src/core/file_sys/vfs_real.h index b92c84316e..26ea7df621 100644 --- a/src/core/file_sys/vfs_real.h +++ b/src/core/file_sys/vfs_real.h @@ -3,8 +3,11 @@ #pragma once +#include <map> +#include <mutex> +#include <optional> #include <string_view> -#include <boost/container/flat_map.hpp> +#include "common/intrusive_list.h" #include "core/file_sys/mode.h" #include "core/file_sys/vfs.h" @@ -14,6 +17,13 @@ class IOFile; namespace FileSys { +struct FileReference : public Common::IntrusiveListBaseNode<FileReference> { + std::shared_ptr<Common::FS::IOFile> file{}; +}; + +class RealVfsFile; +class RealVfsDirectory; + class RealVfsFilesystem : public VfsFilesystem { public: RealVfsFilesystem(); @@ -35,7 +45,28 @@ public: bool DeleteDirectory(std::string_view path) override; private: - boost::container::flat_map<std::string, std::weak_ptr<Common::FS::IOFile>> cache; + using ReferenceListType = Common::IntrusiveListBaseTraits<FileReference>::ListType; + std::map<std::string, std::weak_ptr<VfsFile>, std::less<>> cache; + ReferenceListType open_references; + ReferenceListType closed_references; + std::mutex list_lock; + size_t num_open_files{}; + +private: + friend class RealVfsFile; + std::unique_lock<std::mutex> RefreshReference(const std::string& path, Mode perms, + FileReference& reference); + void DropReference(std::unique_ptr<FileReference>&& reference); + +private: + friend class RealVfsDirectory; + VirtualFile OpenFileFromEntry(std::string_view path, std::optional<u64> size, + Mode perms = Mode::Read); + +private: + void EvictSingleReferenceLocked(); + void InsertReferenceIntoListLocked(FileReference& reference); + void RemoveReferenceFromListLocked(FileReference& reference); }; // An implementation of VfsFile that represents a file on the user's computer. @@ -57,16 +88,15 @@ public: bool Rename(std::string_view name) override; private: - RealVfsFile(RealVfsFilesystem& base, std::shared_ptr<Common::FS::IOFile> backing, - const std::string& path, Mode perms = Mode::Read); - - void Close(); + RealVfsFile(RealVfsFilesystem& base, std::unique_ptr<FileReference> reference, + const std::string& path, Mode perms = Mode::Read, std::optional<u64> size = {}); RealVfsFilesystem& base; - std::shared_ptr<Common::FS::IOFile> backing; + std::unique_ptr<FileReference> reference; std::string path; std::string parent_path; std::vector<std::string> path_components; + std::optional<u64> size; Mode perms; }; diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp index 0a77777322..1ebc32c1e7 100644 --- a/src/core/hid/emulated_controller.cpp +++ b/src/core/hid/emulated_controller.cpp @@ -149,12 +149,16 @@ void EmulatedController::LoadDevices() { camera_params[0] = right_joycon; camera_params[0].Set("camera", true); - camera_params[1] = Common::ParamPackage{"engine:camera,camera:1"}; - ring_params[1] = Common::ParamPackage{"engine:joycon,axis_x:100,axis_y:101"}; - nfc_params[0] = Common::ParamPackage{"engine:virtual_amiibo,nfc:1"}; nfc_params[1] = right_joycon; nfc_params[1].Set("nfc", true); + // Only map virtual devices to the first controller + if (npad_id_type == NpadIdType::Player1 || npad_id_type == NpadIdType::Handheld) { + camera_params[1] = Common::ParamPackage{"engine:camera,camera:1"}; + ring_params[1] = Common::ParamPackage{"engine:joycon,axis_x:100,axis_y:101"}; + nfc_params[0] = Common::ParamPackage{"engine:virtual_amiibo,nfc:1"}; + } + output_params[LeftIndex] = left_joycon; output_params[RightIndex] = right_joycon; output_params[2] = camera_params[1]; @@ -1176,10 +1180,7 @@ void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) { return; } - controller.nfc_state = { - controller.nfc_values.state, - controller.nfc_values.data, - }; + controller.nfc_state = controller.nfc_values; } bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) { @@ -1249,6 +1250,11 @@ Common::Input::DriverResult EmulatedController::SetPollingMode( const auto virtual_nfc_result = nfc_output_device->SetPollingMode(polling_mode); const auto mapped_nfc_result = right_output_device->SetPollingMode(polling_mode); + // Restore previous state + if (mapped_nfc_result != Common::Input::DriverResult::Success) { + right_output_device->SetPollingMode(Common::Input::PollingMode::Active); + } + if (virtual_nfc_result == Common::Input::DriverResult::Success) { return virtual_nfc_result; } @@ -1308,6 +1314,79 @@ bool EmulatedController::HasNfc() const { return is_connected && (has_virtual_nfc && is_virtual_nfc_supported); } +bool EmulatedController::AddNfcHandle() { + nfc_handles++; + return SetPollingMode(EmulatedDeviceIndex::RightIndex, Common::Input::PollingMode::NFC) == + Common::Input::DriverResult::Success; +} + +bool EmulatedController::RemoveNfcHandle() { + nfc_handles--; + if (nfc_handles <= 0) { + return SetPollingMode(EmulatedDeviceIndex::RightIndex, + Common::Input::PollingMode::Active) == + Common::Input::DriverResult::Success; + } + return true; +} + +bool EmulatedController::StartNfcPolling() { + auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; + auto& nfc_virtual_output_device = output_devices[3]; + + const auto device_result = nfc_output_device->StartNfcPolling(); + const auto virtual_device_result = nfc_virtual_output_device->StartNfcPolling(); + + return device_result == Common::Input::NfcState::Success || + virtual_device_result == Common::Input::NfcState::Success; +} + +bool EmulatedController::StopNfcPolling() { + auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; + auto& nfc_virtual_output_device = output_devices[3]; + + const auto device_result = nfc_output_device->StopNfcPolling(); + const auto virtual_device_result = nfc_virtual_output_device->StopNfcPolling(); + + return device_result == Common::Input::NfcState::Success || + virtual_device_result == Common::Input::NfcState::Success; +} + +bool EmulatedController::ReadAmiiboData(std::vector<u8>& data) { + auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; + auto& nfc_virtual_output_device = output_devices[3]; + + if (nfc_output_device->ReadAmiiboData(data) == Common::Input::NfcState::Success) { + return true; + } + + return nfc_virtual_output_device->ReadAmiiboData(data) == Common::Input::NfcState::Success; +} + +bool EmulatedController::ReadMifareData(const Common::Input::MifareRequest& request, + Common::Input::MifareRequest& out_data) { + auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; + auto& nfc_virtual_output_device = output_devices[3]; + + if (nfc_output_device->ReadMifareData(request, out_data) == Common::Input::NfcState::Success) { + return true; + } + + return nfc_virtual_output_device->ReadMifareData(request, out_data) == + Common::Input::NfcState::Success; +} + +bool EmulatedController::WriteMifareData(const Common::Input::MifareRequest& request) { + auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; + auto& nfc_virtual_output_device = output_devices[3]; + + if (nfc_output_device->WriteMifareData(request) == Common::Input::NfcState::Success) { + return true; + } + + return nfc_virtual_output_device->WriteMifareData(request) == Common::Input::NfcState::Success; +} + bool EmulatedController::WriteNfc(const std::vector<u8>& data) { auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; auto& nfc_virtual_output_device = output_devices[3]; diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h index 09fe1a0abd..d511e5facb 100644 --- a/src/core/hid/emulated_controller.h +++ b/src/core/hid/emulated_controller.h @@ -97,10 +97,7 @@ struct RingSensorForce { f32 force; }; -struct NfcState { - Common::Input::NfcState state{}; - std::vector<u8> data{}; -}; +using NfcState = Common::Input::NfcStatus; struct ControllerMotion { Common::Vec3f accel{}; @@ -393,9 +390,31 @@ public: /// Returns true if the device has nfc support bool HasNfc() const; + /// Sets the joycon in nfc mode and increments the handle count + bool AddNfcHandle(); + + /// Decrements the handle count if zero sets the joycon in active mode + bool RemoveNfcHandle(); + + /// Start searching for nfc tags + bool StartNfcPolling(); + + /// Stop searching for nfc tags + bool StopNfcPolling(); + + /// Returns true if the nfc tag was readable + bool ReadAmiiboData(std::vector<u8>& data); + /// Returns true if the nfc tag was written bool WriteNfc(const std::vector<u8>& data); + /// Returns true if the nfc tag was readable + bool ReadMifareData(const Common::Input::MifareRequest& request, + Common::Input::MifareRequest& out_data); + + /// Returns true if the nfc tag was written + bool WriteMifareData(const Common::Input::MifareRequest& request); + /// Returns the led pattern corresponding to this emulated controller LedPattern GetLedPattern() const; @@ -532,6 +551,7 @@ private: bool system_buttons_enabled{true}; f32 motion_sensitivity{Core::HID::MotionInput::IsAtRestStandard}; u32 turbo_button_state{0}; + std::size_t nfc_handles{0}; // Temporary values to avoid doing changes while the controller is in configuring mode NpadStyleIndex tmp_npad_type{NpadStyleIndex::None}; diff --git a/src/core/hid/input_converter.cpp b/src/core/hid/input_converter.cpp index 4ccb1c5966..a05716fd88 100644 --- a/src/core/hid/input_converter.cpp +++ b/src/core/hid/input_converter.cpp @@ -299,11 +299,7 @@ Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& cal Common::Input::NfcStatus nfc{}; switch (callback.type) { case Common::Input::InputType::Nfc: - nfc = { - .state = callback.nfc_status, - .data = callback.raw_data, - }; - break; + return callback.nfc_status; default: LOG_ERROR(Input, "Conversion from type {} to NFC not implemented", callback.type); break; diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp index faa12b4f05..75ce5a23c0 100644 --- a/src/core/hle/kernel/k_scheduler.cpp +++ b/src/core/hle/kernel/k_scheduler.cpp @@ -184,7 +184,8 @@ u64 KScheduler::UpdateHighestPriorityThread(KThread* highest_thread) { prev_highest_thread != highest_thread) [[likely]] { if (prev_highest_thread != nullptr) [[likely]] { IncrementScheduledCount(prev_highest_thread); - prev_highest_thread->SetLastScheduledTick(m_kernel.System().CoreTiming().GetCPUTicks()); + prev_highest_thread->SetLastScheduledTick( + m_kernel.System().CoreTiming().GetClockTicks()); } if (m_state.should_count_idle) { if (highest_thread != nullptr) [[likely]] { @@ -351,7 +352,7 @@ void KScheduler::SwitchThread(KThread* next_thread) { // Update the CPU time tracking variables. const s64 prev_tick = m_last_context_switch_time; - const s64 cur_tick = m_kernel.System().CoreTiming().GetCPUTicks(); + const s64 cur_tick = m_kernel.System().CoreTiming().GetClockTicks(); const s64 tick_diff = cur_tick - prev_tick; cur_thread->AddCpuTime(m_core_id, tick_diff); if (cur_process != nullptr) { diff --git a/src/core/hle/kernel/k_synchronization_object.cpp b/src/core/hle/kernel/k_synchronization_object.cpp index b7da3eee77..3e5b735b1e 100644 --- a/src/core/hle/kernel/k_synchronization_object.cpp +++ b/src/core/hle/kernel/k_synchronization_object.cpp @@ -3,6 +3,7 @@ #include "common/assert.h" #include "common/common_types.h" +#include "common/scratch_buffer.h" #include "core/hle/kernel/k_scheduler.h" #include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" #include "core/hle/kernel/k_synchronization_object.h" @@ -75,7 +76,7 @@ Result KSynchronizationObject::Wait(KernelCore& kernel, s32* out_index, KSynchronizationObject** objects, const s32 num_objects, s64 timeout) { // Allocate space on stack for thread nodes. - std::vector<ThreadListNode> thread_nodes(num_objects); + std::array<ThreadListNode, Svc::ArgumentHandleCountMax> thread_nodes; // Prepare for wait. KThread* thread = GetCurrentThreadPointer(kernel); diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp index 70480b7258..adb6ec5814 100644 --- a/src/core/hle/kernel/k_thread.cpp +++ b/src/core/hle/kernel/k_thread.cpp @@ -4,6 +4,8 @@ #include <algorithm> #include <atomic> #include <cinttypes> +#include <condition_variable> +#include <mutex> #include <optional> #include <vector> @@ -907,7 +909,7 @@ Result KThread::SetActivity(Svc::ThreadActivity activity) { R_SUCCEED(); } -Result KThread::GetThreadContext3(std::vector<u8>& out) { +Result KThread::GetThreadContext3(Common::ScratchBuffer<u8>& out) { // Lock ourselves. KScopedLightLock lk{m_activity_pause_lock}; @@ -925,15 +927,13 @@ Result KThread::GetThreadContext3(std::vector<u8>& out) { // Mask away mode bits, interrupt bits, IL bit, and other reserved bits. auto context = GetContext64(); context.pstate &= 0xFF0FFE20; - - out.resize(sizeof(context)); + out.resize_destructive(sizeof(context)); std::memcpy(out.data(), std::addressof(context), sizeof(context)); } else { // Mask away mode bits, interrupt bits, IL bit, and other reserved bits. auto context = GetContext32(); context.cpsr &= 0xFF0FFE20; - - out.resize(sizeof(context)); + out.resize_destructive(sizeof(context)); std::memcpy(out.data(), std::addressof(context), sizeof(context)); } } @@ -1313,7 +1313,8 @@ void KThread::RequestDummyThreadWait() { ASSERT(this->IsDummyThread()); // We will block when the scheduler lock is released. - m_dummy_thread_runnable.store(false); + std::scoped_lock lock{m_dummy_thread_mutex}; + m_dummy_thread_runnable = false; } void KThread::DummyThreadBeginWait() { @@ -1323,7 +1324,8 @@ void KThread::DummyThreadBeginWait() { } // Block until runnable is no longer false. - m_dummy_thread_runnable.wait(false); + std::unique_lock lock{m_dummy_thread_mutex}; + m_dummy_thread_cv.wait(lock, [this] { return m_dummy_thread_runnable; }); } void KThread::DummyThreadEndWait() { @@ -1331,8 +1333,11 @@ void KThread::DummyThreadEndWait() { ASSERT(this->IsDummyThread()); // Wake up the waiting thread. - m_dummy_thread_runnable.store(true); - m_dummy_thread_runnable.notify_one(); + { + std::scoped_lock lock{m_dummy_thread_mutex}; + m_dummy_thread_runnable = true; + } + m_dummy_thread_cv.notify_one(); } void KThread::BeginWait(KThreadQueue* queue) { diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index f9814ac8f6..dd662b3f8f 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h @@ -15,6 +15,7 @@ #include "common/intrusive_list.h" #include "common/intrusive_red_black_tree.h" +#include "common/scratch_buffer.h" #include "common/spin_lock.h" #include "core/arm/arm_interface.h" #include "core/hle/kernel/k_affinity_mask.h" @@ -567,7 +568,7 @@ public: void RemoveWaiter(KThread* thread); - Result GetThreadContext3(std::vector<u8>& out); + Result GetThreadContext3(Common::ScratchBuffer<u8>& out); KThread* RemoveUserWaiterByKey(bool* out_has_waiters, KProcessAddress key) { return this->RemoveWaiterByKey(out_has_waiters, key, false); @@ -892,7 +893,9 @@ private: std::shared_ptr<Common::Fiber> m_host_context{}; ThreadType m_thread_type{}; StepState m_step_state{}; - std::atomic<bool> m_dummy_thread_runnable{true}; + bool m_dummy_thread_runnable{true}; + std::mutex m_dummy_thread_mutex{}; + std::condition_variable m_dummy_thread_cv{}; // For debugging std::vector<KSynchronizationObject*> m_wait_objects_for_debugging{}; diff --git a/src/core/hle/kernel/svc/svc_info.cpp b/src/core/hle/kernel/svc/svc_info.cpp index 2b2c878b5a..445cdd87b3 100644 --- a/src/core/hle/kernel/svc/svc_info.cpp +++ b/src/core/hle/kernel/svc/svc_info.cpp @@ -199,9 +199,9 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) { const u64 thread_ticks = current_thread->GetCpuTime(); - out_ticks = thread_ticks + (core_timing.GetCPUTicks() - prev_ctx_ticks); + out_ticks = thread_ticks + (core_timing.GetClockTicks() - prev_ctx_ticks); } else if (same_thread && info_sub_id == system.Kernel().CurrentPhysicalCoreIndex()) { - out_ticks = core_timing.GetCPUTicks() - prev_ctx_ticks; + out_ticks = core_timing.GetClockTicks() - prev_ctx_ticks; } *result = out_ticks; diff --git a/src/core/hle/kernel/svc/svc_ipc.cpp b/src/core/hle/kernel/svc/svc_ipc.cpp index ea03068aa1..60247df2eb 100644 --- a/src/core/hle/kernel/svc/svc_ipc.cpp +++ b/src/core/hle/kernel/svc/svc_ipc.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "common/scope_exit.h" +#include "common/scratch_buffer.h" #include "core/core.h" #include "core/hle/kernel/k_client_session.h" #include "core/hle/kernel/k_process.h" @@ -45,11 +46,11 @@ Result ReplyAndReceive(Core::System& system, s32* out_index, uint64_t handles_ad handles_addr, static_cast<u64>(sizeof(Handle) * num_handles)), ResultInvalidPointer); - std::vector<Handle> handles(num_handles); + std::array<Handle, Svc::ArgumentHandleCountMax> handles; GetCurrentMemory(kernel).ReadBlock(handles_addr, handles.data(), sizeof(Handle) * num_handles); // Convert handle list to object table. - std::vector<KSynchronizationObject*> objs(num_handles); + std::array<KSynchronizationObject*, Svc::ArgumentHandleCountMax> objs; R_UNLESS(handle_table.GetMultipleObjects<KSynchronizationObject>(objs.data(), handles.data(), num_handles), ResultInvalidHandle); @@ -80,7 +81,7 @@ Result ReplyAndReceive(Core::System& system, s32* out_index, uint64_t handles_ad // Wait for an object. s32 index; Result result = KSynchronizationObject::Wait(kernel, std::addressof(index), objs.data(), - static_cast<s32>(objs.size()), timeout_ns); + num_handles, timeout_ns); if (result == ResultTimedOut) { R_RETURN(result); } diff --git a/src/core/hle/kernel/svc/svc_synchronization.cpp b/src/core/hle/kernel/svc/svc_synchronization.cpp index 04d65f0bd6..53df5bcd8d 100644 --- a/src/core/hle/kernel/svc/svc_synchronization.cpp +++ b/src/core/hle/kernel/svc/svc_synchronization.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "common/scope_exit.h" +#include "common/scratch_buffer.h" #include "core/core.h" #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_readable_event.h" @@ -54,7 +55,7 @@ static Result WaitSynchronization(Core::System& system, int32_t* out_index, cons // Get the synchronization context. auto& kernel = system.Kernel(); auto& handle_table = GetCurrentProcess(kernel).GetHandleTable(); - std::vector<KSynchronizationObject*> objs(num_handles); + std::array<KSynchronizationObject*, Svc::ArgumentHandleCountMax> objs; // Copy user handles. if (num_handles > 0) { @@ -72,8 +73,8 @@ static Result WaitSynchronization(Core::System& system, int32_t* out_index, cons }); // Wait on the objects. - Result res = KSynchronizationObject::Wait(kernel, out_index, objs.data(), - static_cast<s32>(objs.size()), timeout_ns); + Result res = + KSynchronizationObject::Wait(kernel, out_index, objs.data(), num_handles, timeout_ns); R_SUCCEED_IF(res == ResultSessionClosed); R_RETURN(res); @@ -87,8 +88,7 @@ Result WaitSynchronization(Core::System& system, int32_t* out_index, u64 user_ha // Ensure number of handles is valid. R_UNLESS(0 <= num_handles && num_handles <= Svc::ArgumentHandleCountMax, ResultOutOfRange); - - std::vector<Handle> handles(num_handles); + std::array<Handle, Svc::ArgumentHandleCountMax> handles; if (num_handles > 0) { GetCurrentMemory(system.Kernel()) .ReadBlock(user_handles, handles.data(), num_handles * sizeof(Handle)); diff --git a/src/core/hle/kernel/svc/svc_thread.cpp b/src/core/hle/kernel/svc/svc_thread.cpp index 37b54079cf..36b94e6bfe 100644 --- a/src/core/hle/kernel/svc/svc_thread.cpp +++ b/src/core/hle/kernel/svc/svc_thread.cpp @@ -174,7 +174,7 @@ Result GetThreadContext3(Core::System& system, u64 out_context, Handle thread_ha } // Get the thread context. - std::vector<u8> context; + static thread_local Common::ScratchBuffer<u8> context; R_TRY(thread->GetThreadContext3(context)); // Copy the thread context to user space. diff --git a/src/core/hle/kernel/svc/svc_tick.cpp b/src/core/hle/kernel/svc/svc_tick.cpp index 5613364821..7dd7c6e518 100644 --- a/src/core/hle/kernel/svc/svc_tick.cpp +++ b/src/core/hle/kernel/svc/svc_tick.cpp @@ -12,16 +12,8 @@ namespace Kernel::Svc { int64_t GetSystemTick(Core::System& system) { LOG_TRACE(Kernel_SVC, "called"); - auto& core_timing = system.CoreTiming(); - // Returns the value of cntpct_el0 (https://switchbrew.org/wiki/SVC#svcGetSystemTick) - const u64 result{core_timing.GetClockTicks()}; - - if (!system.Kernel().IsMulticore()) { - core_timing.AddTicks(400U); - } - - return static_cast<int64_t>(result); + return static_cast<int64_t>(system.CoreTiming().GetClockTicks()); } int64_t GetSystemTick64(Core::System& system) { diff --git a/src/core/hle/service/am/applets/applet_cabinet.cpp b/src/core/hle/service/am/applets/applet_cabinet.cpp index 8b754e9d49..19ed184e85 100644 --- a/src/core/hle/service/am/applets/applet_cabinet.cpp +++ b/src/core/hle/service/am/applets/applet_cabinet.cpp @@ -141,7 +141,7 @@ void Cabinet::DisplayCompleted(bool apply_changes, std::string_view amiibo_name) applet_output.device_handle = applet_input_common.device_handle; applet_output.result = CabinetResult::Cancel; const auto reg_result = nfp_device->GetRegisterInfo(applet_output.register_info); - const auto tag_result = nfp_device->GetTagInfo(applet_output.tag_info, false); + const auto tag_result = nfp_device->GetTagInfo(applet_output.tag_info); nfp_device->Finalize(); if (reg_result.IsSuccess()) { diff --git a/src/core/hle/service/audio/audin_u.cpp b/src/core/hle/service/audio/audin_u.cpp index f0640c64f4..c8d574993b 100644 --- a/src/core/hle/service/audio/audin_u.cpp +++ b/src/core/hle/service/audio/audin_u.cpp @@ -5,6 +5,7 @@ #include "audio_core/renderer/audio_device.h" #include "common/common_funcs.h" #include "common/logging/log.h" +#include "common/settings.h" #include "common/string_util.h" #include "core/core.h" #include "core/hle/kernel/k_event.h" @@ -123,19 +124,13 @@ private: void GetReleasedAudioInBuffer(HLERequestContext& ctx) { const auto write_buffer_size = ctx.GetWriteBufferNumElements<u64>(); - std::vector<u64> released_buffers(write_buffer_size); + tmp_buffer.resize_destructive(write_buffer_size); + tmp_buffer[0] = 0; - const auto count = impl->GetReleasedBuffers(released_buffers); + const auto count = impl->GetReleasedBuffers(tmp_buffer); - [[maybe_unused]] std::string tags{}; - for (u32 i = 0; i < count; i++) { - tags += fmt::format("{:08X}, ", released_buffers[i]); - } - [[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()}; - LOG_TRACE(Service_Audio, "called. Session {} released {} buffers: {}", sessionid, count, - tags); + ctx.WriteBuffer(tmp_buffer); - ctx.WriteBuffer(released_buffers); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); rb.Push(count); @@ -200,6 +195,7 @@ private: KernelHelpers::ServiceContext service_context; Kernel::KEvent* event; std::shared_ptr<AudioCore::AudioIn::In> impl; + Common::ScratchBuffer<u64> tmp_buffer; }; AudInU::AudInU(Core::System& system_) diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index 3e62fa4fca..032c8c11f5 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp @@ -123,19 +123,13 @@ private: void GetReleasedAudioOutBuffers(HLERequestContext& ctx) { const auto write_buffer_size = ctx.GetWriteBufferNumElements<u64>(); - std::vector<u64> released_buffers(write_buffer_size); + tmp_buffer.resize_destructive(write_buffer_size); + tmp_buffer[0] = 0; - const auto count = impl->GetReleasedBuffers(released_buffers); + const auto count = impl->GetReleasedBuffers(tmp_buffer); - [[maybe_unused]] std::string tags{}; - for (u32 i = 0; i < count; i++) { - tags += fmt::format("{:08X}, ", released_buffers[i]); - } - [[maybe_unused]] const auto sessionid{impl->GetSystem().GetSessionId()}; - LOG_TRACE(Service_Audio, "called. Session {} released {} buffers: {}", sessionid, count, - tags); + ctx.WriteBuffer(tmp_buffer); - ctx.WriteBuffer(released_buffers); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); rb.Push(count); @@ -211,6 +205,7 @@ private: KernelHelpers::ServiceContext service_context; Kernel::KEvent* event; std::shared_ptr<AudioCore::AudioOut::Out> impl; + Common::ScratchBuffer<u64> tmp_buffer; }; AudOutU::AudOutU(Core::System& system_) diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index 7086d47508..12845c23a3 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp @@ -116,28 +116,26 @@ private: // These buffers are written manually to avoid an issue with WriteBuffer throwing errors for // checking size 0. Performance size is 0 for most games. - std::vector<u8> output{}; - std::vector<u8> performance{}; auto is_buffer_b{ctx.BufferDescriptorB()[0].Size() != 0}; if (is_buffer_b) { const auto buffersB{ctx.BufferDescriptorB()}; - output.resize(buffersB[0].Size(), 0); - performance.resize(buffersB[1].Size(), 0); + tmp_output.resize_destructive(buffersB[0].Size()); + tmp_performance.resize_destructive(buffersB[1].Size()); } else { const auto buffersC{ctx.BufferDescriptorC()}; - output.resize(buffersC[0].Size(), 0); - performance.resize(buffersC[1].Size(), 0); + tmp_output.resize_destructive(buffersC[0].Size()); + tmp_performance.resize_destructive(buffersC[1].Size()); } - auto result = impl->RequestUpdate(input, performance, output); + auto result = impl->RequestUpdate(input, tmp_performance, tmp_output); if (result.IsSuccess()) { if (is_buffer_b) { - ctx.WriteBufferB(output.data(), output.size(), 0); - ctx.WriteBufferB(performance.data(), performance.size(), 1); + ctx.WriteBufferB(tmp_output.data(), tmp_output.size(), 0); + ctx.WriteBufferB(tmp_performance.data(), tmp_performance.size(), 1); } else { - ctx.WriteBufferC(output.data(), output.size(), 0); - ctx.WriteBufferC(performance.data(), performance.size(), 1); + ctx.WriteBufferC(tmp_output.data(), tmp_output.size(), 0); + ctx.WriteBufferC(tmp_performance.data(), tmp_performance.size(), 1); } } else { LOG_ERROR(Service_Audio, "RequestUpdate failed error 0x{:02X}!", result.description); @@ -235,6 +233,8 @@ private: Kernel::KEvent* rendered_event; Manager& manager; std::unique_ptr<Renderer> impl; + Common::ScratchBuffer<u8> tmp_output; + Common::ScratchBuffer<u8> tmp_performance; }; class IAudioDevice final : public ServiceFramework<IAudioDevice> { diff --git a/src/core/hle/service/audio/audren_u.h b/src/core/hle/service/audio/audren_u.h index 24ce37e871..d8e9c87195 100644 --- a/src/core/hle/service/audio/audren_u.h +++ b/src/core/hle/service/audio/audren_u.h @@ -4,6 +4,7 @@ #pragma once #include "audio_core/audio_render_manager.h" +#include "common/scratch_buffer.h" #include "core/hle/service/kernel_helpers.h" #include "core/hle/service/service.h" diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp index 451ac224a4..c835f6cb76 100644 --- a/src/core/hle/service/audio/hwopus.cpp +++ b/src/core/hle/service/audio/hwopus.cpp @@ -68,13 +68,13 @@ private: ExtraBehavior extra_behavior) { u32 consumed = 0; u32 sample_count = 0; - std::vector<opus_int16> samples(ctx.GetWriteBufferNumElements<opus_int16>()); + tmp_samples.resize_destructive(ctx.GetWriteBufferNumElements<opus_int16>()); if (extra_behavior == ExtraBehavior::ResetContext) { ResetDecoderContext(); } - if (!DecodeOpusData(consumed, sample_count, ctx.ReadBuffer(), samples, performance)) { + if (!DecodeOpusData(consumed, sample_count, ctx.ReadBuffer(), tmp_samples, performance)) { LOG_ERROR(Audio, "Failed to decode opus data"); IPC::ResponseBuilder rb{ctx, 2}; // TODO(ogniK): Use correct error code @@ -90,11 +90,11 @@ private: if (performance) { rb.Push<u64>(*performance); } - ctx.WriteBuffer(samples); + ctx.WriteBuffer(tmp_samples); } bool DecodeOpusData(u32& consumed, u32& sample_count, std::span<const u8> input, - std::vector<opus_int16>& output, u64* out_performance_time) const { + std::span<opus_int16> output, u64* out_performance_time) const { const auto start_time = std::chrono::steady_clock::now(); const std::size_t raw_output_sz = output.size() * sizeof(opus_int16); if (sizeof(OpusPacketHeader) > input.size()) { @@ -154,6 +154,7 @@ private: OpusDecoderPtr decoder; u32 sample_rate; u32 channel_count; + Common::ScratchBuffer<opus_int16> tmp_samples; }; class IHardwareOpusDecoderManager final : public ServiceFramework<IHardwareOpusDecoderManager> { diff --git a/src/core/hle/service/hid/hidbus.cpp b/src/core/hle/service/hid/hidbus.cpp index 5604a6fdad..80aac221b8 100644 --- a/src/core/hle/service/hid/hidbus.cpp +++ b/src/core/hle/service/hid/hidbus.cpp @@ -5,7 +5,6 @@ #include "common/settings.h" #include "core/core.h" #include "core/core_timing.h" -#include "core/core_timing_util.h" #include "core/hid/hid_types.h" #include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_readable_event.h" diff --git a/src/core/hle/service/nfc/common/amiibo_crypto.cpp b/src/core/hle/service/nfc/common/amiibo_crypto.cpp index b2bcb68c37..bc232c334d 100644 --- a/src/core/hle/service/nfc/common/amiibo_crypto.cpp +++ b/src/core/hle/service/nfc/common/amiibo_crypto.cpp @@ -36,12 +36,12 @@ bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) { // Validate UUID constexpr u8 CT = 0x88; // As defined in `ISO / IEC 14443 - 3` - if ((CT ^ ntag_file.uuid.uid[0] ^ ntag_file.uuid.uid[1] ^ ntag_file.uuid.uid[2]) != - ntag_file.uuid.uid[3]) { + if ((CT ^ ntag_file.uuid.part1[0] ^ ntag_file.uuid.part1[1] ^ ntag_file.uuid.part1[2]) != + ntag_file.uuid.crc_check1) { return false; } - if ((ntag_file.uuid.uid[4] ^ ntag_file.uuid.uid[5] ^ ntag_file.uuid.uid[6] ^ - ntag_file.uuid.nintendo_id) != ntag_file.uuid.lock_bytes[0]) { + if ((ntag_file.uuid.part2[0] ^ ntag_file.uuid.part2[1] ^ ntag_file.uuid.part2[2] ^ + ntag_file.uuid.nintendo_id) != ntag_file.uuid_crc_check2) { return false; } @@ -74,8 +74,9 @@ bool IsAmiiboValid(const NTAG215File& ntag_file) { NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) { NTAG215File encoded_data{}; - encoded_data.uid = nfc_data.uuid.uid; - encoded_data.nintendo_id = nfc_data.uuid.nintendo_id; + encoded_data.uid = nfc_data.uuid; + encoded_data.uid_crc_check2 = nfc_data.uuid_crc_check2; + encoded_data.internal_number = nfc_data.internal_number; encoded_data.static_lock = nfc_data.static_lock; encoded_data.compability_container = nfc_data.compability_container; encoded_data.hmac_data = nfc_data.user_memory.hmac_data; @@ -94,7 +95,6 @@ NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) { encoded_data.register_info_crc = nfc_data.user_memory.register_info_crc; encoded_data.application_area = nfc_data.user_memory.application_area; encoded_data.hmac_tag = nfc_data.user_memory.hmac_tag; - encoded_data.lock_bytes = nfc_data.uuid.lock_bytes; encoded_data.model_info = nfc_data.user_memory.model_info; encoded_data.keygen_salt = nfc_data.user_memory.keygen_salt; encoded_data.dynamic_lock = nfc_data.dynamic_lock; @@ -108,9 +108,9 @@ NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) { EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data) { EncryptedNTAG215File nfc_data{}; - nfc_data.uuid.uid = encoded_data.uid; - nfc_data.uuid.nintendo_id = encoded_data.nintendo_id; - nfc_data.uuid.lock_bytes = encoded_data.lock_bytes; + nfc_data.uuid = encoded_data.uid; + nfc_data.uuid_crc_check2 = encoded_data.uid_crc_check2; + nfc_data.internal_number = encoded_data.internal_number; nfc_data.static_lock = encoded_data.static_lock; nfc_data.compability_container = encoded_data.compability_container; nfc_data.user_memory.hmac_data = encoded_data.hmac_data; @@ -139,23 +139,12 @@ EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data) { return nfc_data; } -u32 GetTagPassword(const TagUuid& uuid) { - // Verify that the generated password is correct - u32 password = 0xAA ^ (uuid.uid[1] ^ uuid.uid[3]); - password &= (0x55 ^ (uuid.uid[2] ^ uuid.uid[4])) << 8; - password &= (0xAA ^ (uuid.uid[3] ^ uuid.uid[5])) << 16; - password &= (0x55 ^ (uuid.uid[4] ^ uuid.uid[6])) << 24; - return password; -} - HashSeed GetSeed(const NTAG215File& data) { HashSeed seed{ .magic = data.write_counter, .padding = {}, .uid_1 = data.uid, - .nintendo_id_1 = data.nintendo_id, .uid_2 = data.uid, - .nintendo_id_2 = data.nintendo_id, .keygen_salt = data.keygen_salt, }; @@ -177,10 +166,11 @@ std::vector<u8> GenerateInternalKey(const InternalKey& key, const HashSeed& seed output.insert(output.end(), key.magic_bytes.begin(), key.magic_bytes.begin() + key.magic_length); - output.insert(output.end(), seed.uid_1.begin(), seed.uid_1.end()); - output.emplace_back(seed.nintendo_id_1); - output.insert(output.end(), seed.uid_2.begin(), seed.uid_2.end()); - output.emplace_back(seed.nintendo_id_2); + std::array<u8, sizeof(NFP::TagUuid)> seed_uuid{}; + memcpy(seed_uuid.data(), &seed.uid_1, sizeof(NFP::TagUuid)); + output.insert(output.end(), seed_uuid.begin(), seed_uuid.end()); + memcpy(seed_uuid.data(), &seed.uid_2, sizeof(NFP::TagUuid)); + output.insert(output.end(), seed_uuid.begin(), seed_uuid.end()); for (std::size_t i = 0; i < sizeof(seed.keygen_salt); i++) { output.emplace_back(static_cast<u8>(seed.keygen_salt[i] ^ key.xor_pad[i])); @@ -264,8 +254,8 @@ void Cipher(const DerivedKeys& keys, const NTAG215File& in_data, NTAG215File& ou // Copy the rest of the data directly out_data.uid = in_data.uid; - out_data.nintendo_id = in_data.nintendo_id; - out_data.lock_bytes = in_data.lock_bytes; + out_data.uid_crc_check2 = in_data.uid_crc_check2; + out_data.internal_number = in_data.internal_number; out_data.static_lock = in_data.static_lock; out_data.compability_container = in_data.compability_container; diff --git a/src/core/hle/service/nfc/common/amiibo_crypto.h b/src/core/hle/service/nfc/common/amiibo_crypto.h index bf3044ed96..6a3e0841eb 100644 --- a/src/core/hle/service/nfc/common/amiibo_crypto.h +++ b/src/core/hle/service/nfc/common/amiibo_crypto.h @@ -24,10 +24,8 @@ using DrgbOutput = std::array<u8, 0x20>; struct HashSeed { u16_be magic; std::array<u8, 0xE> padding; - NFC::UniqueSerialNumber uid_1; - u8 nintendo_id_1; - NFC::UniqueSerialNumber uid_2; - u8 nintendo_id_2; + TagUuid uid_1; + TagUuid uid_2; std::array<u8, 0x20> keygen_salt; }; static_assert(sizeof(HashSeed) == 0x40, "HashSeed is an invalid size"); @@ -69,9 +67,6 @@ NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data); /// Converts from encoded file format to encrypted file format EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data); -/// Returns password needed to allow write access to protected memory -u32 GetTagPassword(const TagUuid& uuid); - // Generates Seed needed for key derivation HashSeed GetSeed(const NTAG215File& data); diff --git a/src/core/hle/service/nfc/common/device.cpp b/src/core/hle/service/nfc/common/device.cpp index b14f682b50..5bf2898183 100644 --- a/src/core/hle/service/nfc/common/device.cpp +++ b/src/core/hle/service/nfc/common/device.cpp @@ -93,7 +93,8 @@ void NfcDevice::NpadUpdate(Core::HID::ControllerTriggerType type) { const auto nfc_status = npad_device->GetNfc(); switch (nfc_status.state) { case Common::Input::NfcState::NewAmiibo: - LoadNfcTag(nfc_status.data); + LoadNfcTag(nfc_status.protocol, nfc_status.tag_type, nfc_status.uuid_length, + nfc_status.uuid); break; case Common::Input::NfcState::AmiiboRemoved: if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) { @@ -108,28 +109,46 @@ void NfcDevice::NpadUpdate(Core::HID::ControllerTriggerType type) { } } -bool NfcDevice::LoadNfcTag(std::span<const u8> data) { +bool NfcDevice::LoadNfcTag(u8 protocol, u8 tag_type, u8 uuid_length, UniqueSerialNumber uuid) { if (device_state != DeviceState::SearchingForTag) { LOG_ERROR(Service_NFC, "Game is not looking for nfc tag, current state {}", device_state); return false; } + if ((protocol & static_cast<u8>(allowed_protocols)) == 0) { + LOG_ERROR(Service_NFC, "Protocol not supported {}", protocol); + return false; + } + + real_tag_info = { + .uuid = uuid, + .uuid_length = uuid_length, + .protocol = static_cast<NfcProtocol>(protocol), + .tag_type = static_cast<TagType>(tag_type), + }; + + device_state = DeviceState::TagFound; + deactivate_event->GetReadableEvent().Clear(); + activate_event->Signal(); + return true; +} + +bool NfcDevice::LoadAmiiboData() { + std::vector<u8> data{}; + + if (!npad_device->ReadAmiiboData(data)) { + return false; + } + if (data.size() < sizeof(NFP::EncryptedNTAG215File)) { LOG_ERROR(Service_NFC, "Not an amiibo, size={}", data.size()); return false; } - mifare_data.resize(data.size()); - memcpy(mifare_data.data(), data.data(), data.size()); - memcpy(&tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File)); is_plain_amiibo = NFP::AmiiboCrypto::IsAmiiboValid(tag_data); is_write_protected = false; - device_state = DeviceState::TagFound; - deactivate_event->GetReadableEvent().Clear(); - activate_event->Signal(); - // Fallback for plain amiibos if (is_plain_amiibo) { LOG_INFO(Service_NFP, "Using plain amiibo"); @@ -147,6 +166,7 @@ bool NfcDevice::LoadNfcTag(std::span<const u8> data) { return true; } + LOG_INFO(Service_NFP, "Using encrypted amiibo"); tag_data = {}; memcpy(&encrypted_tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File)); return true; @@ -162,7 +182,6 @@ void NfcDevice::CloseNfcTag() { device_state = DeviceState::TagRemoved; encrypted_tag_data = {}; tag_data = {}; - mifare_data = {}; activate_event->GetReadableEvent().Clear(); deactivate_event->Signal(); } @@ -179,8 +198,12 @@ void NfcDevice::Initialize() { device_state = npad_device->HasNfc() ? DeviceState::Initialized : DeviceState::Unavailable; encrypted_tag_data = {}; tag_data = {}; - mifare_data = {}; - is_initalized = true; + + if (device_state != DeviceState::Initialized) { + return; + } + + is_initalized = npad_device->AddNfcHandle(); } void NfcDevice::Finalize() { @@ -190,6 +213,11 @@ void NfcDevice::Finalize() { if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) { StopDetection(); } + + if (device_state != DeviceState::Unavailable) { + npad_device->RemoveNfcHandle(); + } + device_state = DeviceState::Unavailable; is_initalized = false; } @@ -200,10 +228,8 @@ Result NfcDevice::StartDetection(NfcProtocol allowed_protocol) { return ResultWrongDeviceState; } - if (npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, - Common::Input::PollingMode::NFC) != - Common::Input::DriverResult::Success) { - LOG_ERROR(Service_NFC, "Nfc not supported"); + if (!npad_device->StartNfcPolling()) { + LOG_ERROR(Service_NFC, "Nfc polling not supported"); return ResultNfcDisabled; } @@ -213,9 +239,6 @@ Result NfcDevice::StartDetection(NfcProtocol allowed_protocol) { } Result NfcDevice::StopDetection() { - npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, - Common::Input::PollingMode::Active); - if (device_state == DeviceState::Initialized) { return ResultSuccess; } @@ -225,6 +248,7 @@ Result NfcDevice::StopDetection() { } if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) { + npad_device->StopNfcPolling(); device_state = DeviceState::Initialized; return ResultSuccess; } @@ -233,7 +257,7 @@ Result NfcDevice::StopDetection() { return ResultWrongDeviceState; } -Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info, bool is_mifare) const { +Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info) const { if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) { LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); if (device_state == DeviceState::TagRemoved) { @@ -242,72 +266,78 @@ Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info, bool is_mifare) const { return ResultWrongDeviceState; } - UniqueSerialNumber uuid = encrypted_tag_data.uuid.uid; + tag_info = real_tag_info; // Generate random UUID to bypass amiibo load limits - if (Settings::values.random_amiibo_id) { + if (real_tag_info.tag_type == TagType::Type2 && Settings::values.random_amiibo_id) { Common::TinyMT rng{}; rng.Initialize(static_cast<u32>(GetCurrentPosixTime())); - rng.GenerateRandomBytes(uuid.data(), sizeof(UniqueSerialNumber)); - uuid[3] = 0x88 ^ uuid[0] ^ uuid[1] ^ uuid[2]; + rng.GenerateRandomBytes(tag_info.uuid.data(), tag_info.uuid_length); } - if (is_mifare) { - tag_info = { - .uuid = uuid, - .uuid_extension = {}, - .uuid_length = static_cast<u8>(uuid.size()), - .protocol = NfcProtocol::TypeA, - .tag_type = TagType::Type4, - }; - return ResultSuccess; - } - - // Protocol and tag type may change here - tag_info = { - .uuid = uuid, - .uuid_extension = {}, - .uuid_length = static_cast<u8>(uuid.size()), - .protocol = NfcProtocol::TypeA, - .tag_type = TagType::Type2, - }; - return ResultSuccess; } Result NfcDevice::ReadMifare(std::span<const MifareReadBlockParameter> parameters, std::span<MifareReadBlockData> read_block_data) const { + if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return ResultTagRemoved; + } + return ResultWrongDeviceState; + } + Result result = ResultSuccess; - for (std::size_t i = 0; i < parameters.size(); i++) { - result = ReadMifare(parameters[i], read_block_data[i]); - if (result.IsError()) { - break; - } + TagInfo tag_info{}; + result = GetTagInfo(tag_info); + + if (result.IsError()) { + return result; } - return result; -} + if (tag_info.protocol != NfcProtocol::TypeA || tag_info.tag_type != TagType::Mifare) { + return ResultInvalidTagType; + } -Result NfcDevice::ReadMifare(const MifareReadBlockParameter& parameter, - MifareReadBlockData& read_block_data) const { - const std::size_t sector_index = parameter.sector_number * sizeof(DataBlock); - read_block_data.sector_number = parameter.sector_number; + if (parameters.size() == 0) { + return ResultInvalidArgument; + } - if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); - if (device_state == DeviceState::TagRemoved) { - return ResultTagRemoved; + Common::Input::MifareRequest request{}; + Common::Input::MifareRequest out_data{}; + const auto unknown = parameters[0].sector_key.unknown; + for (std::size_t i = 0; i < parameters.size(); i++) { + if (unknown != parameters[i].sector_key.unknown) { + return ResultInvalidArgument; } - return ResultWrongDeviceState; } - if (mifare_data.size() < sector_index + sizeof(DataBlock)) { - return Mifare::ResultReadError; + for (std::size_t i = 0; i < parameters.size(); i++) { + if (parameters[i].sector_key.command == MifareCmd::None) { + continue; + } + request.data[i].command = static_cast<u8>(parameters[i].sector_key.command); + request.data[i].sector = parameters[i].sector_number; + memcpy(request.data[i].key.data(), parameters[i].sector_key.sector_key.data(), + sizeof(KeyData)); } - // TODO: Use parameter.sector_key to read encrypted data - memcpy(read_block_data.data.data(), mifare_data.data() + sector_index, sizeof(DataBlock)); + if (!npad_device->ReadMifareData(request, out_data)) { + return ResultMifareError288; + } + + for (std::size_t i = 0; i < read_block_data.size(); i++) { + if (static_cast<MifareCmd>(out_data.data[i].command) == MifareCmd::None) { + continue; + } + + read_block_data[i] = { + .data = out_data.data[i].data, + .sector_number = out_data.data[i].sector, + }; + } return ResultSuccess; } @@ -315,40 +345,45 @@ Result NfcDevice::ReadMifare(const MifareReadBlockParameter& parameter, Result NfcDevice::WriteMifare(std::span<const MifareWriteBlockParameter> parameters) { Result result = ResultSuccess; - for (std::size_t i = 0; i < parameters.size(); i++) { - result = WriteMifare(parameters[i]); - if (result.IsError()) { - break; - } - } + TagInfo tag_info{}; + result = GetTagInfo(tag_info); - if (!npad_device->WriteNfc(mifare_data)) { - LOG_ERROR(Service_NFP, "Error writing to file"); - return Mifare::ResultReadError; + if (result.IsError()) { + return result; } - return result; -} + if (tag_info.protocol != NfcProtocol::TypeA || tag_info.tag_type != TagType::Mifare) { + return ResultInvalidTagType; + } -Result NfcDevice::WriteMifare(const MifareWriteBlockParameter& parameter) { - const std::size_t sector_index = parameter.sector_number * sizeof(DataBlock); + if (parameters.size() == 0) { + return ResultInvalidArgument; + } - if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); - if (device_state == DeviceState::TagRemoved) { - return ResultTagRemoved; + const auto unknown = parameters[0].sector_key.unknown; + for (std::size_t i = 0; i < parameters.size(); i++) { + if (unknown != parameters[i].sector_key.unknown) { + return ResultInvalidArgument; } - return ResultWrongDeviceState; } - if (mifare_data.size() < sector_index + sizeof(DataBlock)) { - return Mifare::ResultReadError; + Common::Input::MifareRequest request{}; + for (std::size_t i = 0; i < parameters.size(); i++) { + if (parameters[i].sector_key.command == MifareCmd::None) { + continue; + } + request.data[i].command = static_cast<u8>(parameters[i].sector_key.command); + request.data[i].sector = parameters[i].sector_number; + memcpy(request.data[i].key.data(), parameters[i].sector_key.sector_key.data(), + sizeof(KeyData)); + memcpy(request.data[i].data.data(), parameters[i].data.data(), sizeof(KeyData)); } - // TODO: Use parameter.sector_key to encrypt the data - memcpy(mifare_data.data() + sector_index, parameter.data.data(), sizeof(DataBlock)); + if (!npad_device->WriteMifareData(request)) { + return ResultMifareError288; + } - return ResultSuccess; + return result; } Result NfcDevice::SendCommandByPassThrough(const Time::Clock::TimeSpanType& timeout, @@ -364,9 +399,14 @@ Result NfcDevice::Mount(NFP::ModelType model_type, NFP::MountTarget mount_target return ResultWrongDeviceState; } + if (!LoadAmiiboData()) { + LOG_ERROR(Service_NFP, "Not an amiibo"); + return ResultInvalidTagType; + } + if (!NFP::AmiiboCrypto::IsAmiiboValid(encrypted_tag_data)) { LOG_ERROR(Service_NFP, "Not an amiibo"); - return ResultNotAnAmiibo; + return ResultInvalidTagType; } // The loaded amiibo is not encrypted @@ -381,14 +421,14 @@ Result NfcDevice::Mount(NFP::ModelType model_type, NFP::MountTarget mount_target } if (!NFP::AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) { - bool has_backup = HasBackup(encrypted_tag_data.uuid.uid).IsSuccess(); + bool has_backup = HasBackup(encrypted_tag_data.uuid).IsSuccess(); LOG_ERROR(Service_NFP, "Can't decode amiibo, has_backup= {}", has_backup); return has_backup ? ResultCorruptedDataWithBackup : ResultCorruptedData; } std::vector<u8> data(sizeof(NFP::EncryptedNTAG215File)); memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data)); - WriteBackupData(encrypted_tag_data.uuid.uid, data); + WriteBackupData(encrypted_tag_data.uuid, data); device_state = DeviceState::TagMounted; mount_target = mount_target_; @@ -492,7 +532,7 @@ Result NfcDevice::FlushWithBreak(NFP::BreakType break_type) { } memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data)); - WriteBackupData(encrypted_tag_data.uuid.uid, data); + WriteBackupData(encrypted_tag_data.uuid, data); } if (!npad_device->WriteNfc(data)) { @@ -514,13 +554,13 @@ Result NfcDevice::Restore() { NFC::TagInfo tag_info{}; std::array<u8, sizeof(NFP::EncryptedNTAG215File)> data{}; - Result result = GetTagInfo(tag_info, false); + Result result = GetTagInfo(tag_info); if (result.IsError()) { return result; } - result = ReadBackupData(tag_info.uuid, data); + result = ReadBackupData(tag_info.uuid, tag_info.uuid_length, data); if (result.IsError()) { return result; @@ -548,7 +588,7 @@ Result NfcDevice::Restore() { } if (!NFP::AmiiboCrypto::IsAmiiboValid(temporary_encrypted_tag_data)) { - return ResultNotAnAmiibo; + return ResultInvalidTagType; } if (!is_plain_amiibo) { @@ -587,7 +627,7 @@ Result NfcDevice::GetCommonInfo(NFP::CommonInfo& common_info) const { // TODO: Validate this data common_info = { .last_write_date = settings.write_date.GetWriteDate(), - .write_counter = tag_data.write_counter, + .write_counter = tag_data.application_write_counter, .version = tag_data.amiibo_version, .application_area_size = sizeof(NFP::ApplicationArea), }; @@ -1194,10 +1234,12 @@ Result NfcDevice::BreakTag(NFP::BreakType break_type) { return FlushWithBreak(break_type); } -Result NfcDevice::HasBackup(const NFC::UniqueSerialNumber& uid) const { +Result NfcDevice::HasBackup(const UniqueSerialNumber& uid, std::size_t uuid_size) const { + ASSERT_MSG(uuid_size < sizeof(UniqueSerialNumber), "Invalid UUID size"); constexpr auto backup_dir = "backup"; const auto yuzu_amiibo_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::AmiiboDir); - const auto file_name = fmt::format("{0:02x}.bin", fmt::join(uid, "")); + const auto file_name = + fmt::format("{0:02x}.bin", fmt::join(uid.begin(), uid.begin() + uuid_size, "")); if (!Common::FS::Exists(yuzu_amiibo_dir / backup_dir / file_name)) { return ResultUnableToAccessBackupFile; @@ -1206,10 +1248,19 @@ Result NfcDevice::HasBackup(const NFC::UniqueSerialNumber& uid) const { return ResultSuccess; } -Result NfcDevice::ReadBackupData(const NFC::UniqueSerialNumber& uid, std::span<u8> data) const { +Result NfcDevice::HasBackup(const NFP::TagUuid& tag_uid) const { + UniqueSerialNumber uuid{}; + memcpy(uuid.data(), &tag_uid, sizeof(NFP::TagUuid)); + return HasBackup(uuid, sizeof(NFP::TagUuid)); +} + +Result NfcDevice::ReadBackupData(const UniqueSerialNumber& uid, std::size_t uuid_size, + std::span<u8> data) const { + ASSERT_MSG(uuid_size < sizeof(UniqueSerialNumber), "Invalid UUID size"); constexpr auto backup_dir = "backup"; const auto yuzu_amiibo_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::AmiiboDir); - const auto file_name = fmt::format("{0:02x}.bin", fmt::join(uid, "")); + const auto file_name = + fmt::format("{0:02x}.bin", fmt::join(uid.begin(), uid.begin() + uuid_size, "")); const Common::FS::IOFile keys_file{yuzu_amiibo_dir / backup_dir / file_name, Common::FS::FileAccessMode::Read, @@ -1228,12 +1279,21 @@ Result NfcDevice::ReadBackupData(const NFC::UniqueSerialNumber& uid, std::span<u return ResultSuccess; } -Result NfcDevice::WriteBackupData(const NFC::UniqueSerialNumber& uid, std::span<const u8> data) { +Result NfcDevice::ReadBackupData(const NFP::TagUuid& tag_uid, std::span<u8> data) const { + UniqueSerialNumber uuid{}; + memcpy(uuid.data(), &tag_uid, sizeof(NFP::TagUuid)); + return ReadBackupData(uuid, sizeof(NFP::TagUuid), data); +} + +Result NfcDevice::WriteBackupData(const UniqueSerialNumber& uid, std::size_t uuid_size, + std::span<const u8> data) { + ASSERT_MSG(uuid_size < sizeof(UniqueSerialNumber), "Invalid UUID size"); constexpr auto backup_dir = "backup"; const auto yuzu_amiibo_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::AmiiboDir); - const auto file_name = fmt::format("{0:02x}.bin", fmt::join(uid, "")); + const auto file_name = + fmt::format("{0:02x}.bin", fmt::join(uid.begin(), uid.begin() + uuid_size, "")); - if (HasBackup(uid).IsError()) { + if (HasBackup(uid, uuid_size).IsError()) { if (!Common::FS::CreateDir(yuzu_amiibo_dir / backup_dir)) { return ResultBackupPathAlreadyExist; } @@ -1260,6 +1320,12 @@ Result NfcDevice::WriteBackupData(const NFC::UniqueSerialNumber& uid, std::span< return ResultSuccess; } +Result NfcDevice::WriteBackupData(const NFP::TagUuid& tag_uid, std::span<const u8> data) { + UniqueSerialNumber uuid{}; + memcpy(uuid.data(), &tag_uid, sizeof(NFP::TagUuid)); + return WriteBackupData(uuid, sizeof(NFP::TagUuid), data); +} + Result NfcDevice::WriteNtf(std::span<const u8> data) { if (device_state != DeviceState::TagMounted) { LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); diff --git a/src/core/hle/service/nfc/common/device.h b/src/core/hle/service/nfc/common/device.h index 6f049b687e..0ed1ff34ca 100644 --- a/src/core/hle/service/nfc/common/device.h +++ b/src/core/hle/service/nfc/common/device.h @@ -42,15 +42,12 @@ public: Result StartDetection(NfcProtocol allowed_protocol); Result StopDetection(); - Result GetTagInfo(TagInfo& tag_info, bool is_mifare) const; + Result GetTagInfo(TagInfo& tag_info) const; Result ReadMifare(std::span<const MifareReadBlockParameter> parameters, std::span<MifareReadBlockData> read_block_data) const; - Result ReadMifare(const MifareReadBlockParameter& parameter, - MifareReadBlockData& read_block_data) const; Result WriteMifare(std::span<const MifareWriteBlockParameter> parameters); - Result WriteMifare(const MifareWriteBlockParameter& parameter); Result SendCommandByPassThrough(const Time::Clock::TimeSpanType& timeout, std::span<const u8> command_data, std::span<u8> out_data); @@ -86,9 +83,14 @@ public: Result GetAll(NFP::NfpData& data) const; Result SetAll(const NFP::NfpData& data); Result BreakTag(NFP::BreakType break_type); - Result HasBackup(const NFC::UniqueSerialNumber& uid) const; - Result ReadBackupData(const NFC::UniqueSerialNumber& uid, std::span<u8> data) const; - Result WriteBackupData(const NFC::UniqueSerialNumber& uid, std::span<const u8> data); + Result HasBackup(const UniqueSerialNumber& uid, std::size_t uuid_size) const; + Result HasBackup(const NFP::TagUuid& tag_uid) const; + Result ReadBackupData(const UniqueSerialNumber& uid, std::size_t uuid_size, + std::span<u8> data) const; + Result ReadBackupData(const NFP::TagUuid& tag_uid, std::span<u8> data) const; + Result WriteBackupData(const UniqueSerialNumber& uid, std::size_t uuid_size, + std::span<const u8> data); + Result WriteBackupData(const NFP::TagUuid& tag_uid, std::span<const u8> data); Result WriteNtf(std::span<const u8> data); u64 GetHandle() const; @@ -100,7 +102,8 @@ public: private: void NpadUpdate(Core::HID::ControllerTriggerType type); - bool LoadNfcTag(std::span<const u8> data); + bool LoadNfcTag(u8 protocol, u8 tag_type, u8 uuid_length, UniqueSerialNumber uuid); + bool LoadAmiiboData(); void CloseNfcTag(); NFP::AmiiboName GetAmiiboName(const NFP::AmiiboSettings& settings) const; @@ -135,8 +138,8 @@ private: bool is_write_protected{}; NFP::MountTarget mount_target{NFP::MountTarget::None}; + TagInfo real_tag_info{}; NFP::NTAG215File tag_data{}; - std::vector<u8> mifare_data{}; NFP::EncryptedNTAG215File encrypted_tag_data{}; }; diff --git a/src/core/hle/service/nfc/common/device_manager.cpp b/src/core/hle/service/nfc/common/device_manager.cpp index cffd602df0..562f3a28ea 100644 --- a/src/core/hle/service/nfc/common/device_manager.cpp +++ b/src/core/hle/service/nfc/common/device_manager.cpp @@ -29,6 +29,9 @@ DeviceManager::DeviceManager(Core::System& system_, KernelHelpers::ServiceContex } DeviceManager ::~DeviceManager() { + if (is_initialized) { + Finalize(); + } service_context.CloseEvent(availability_change_event); } @@ -125,14 +128,14 @@ Result DeviceManager::StopDetection(u64 device_handle) { return result; } -Result DeviceManager::GetTagInfo(u64 device_handle, TagInfo& tag_info, bool is_mifare) const { +Result DeviceManager::GetTagInfo(u64 device_handle, TagInfo& tag_info) const { std::scoped_lock lock{mutex}; std::shared_ptr<NfcDevice> device = nullptr; auto result = GetDeviceHandle(device_handle, device); if (result.IsSuccess()) { - result = device->GetTagInfo(tag_info, is_mifare); + result = device->GetTagInfo(tag_info); result = VerifyDeviceResult(device, result); } @@ -546,11 +549,11 @@ Result DeviceManager::ReadBackupData(u64 device_handle, std::span<u8> data) cons NFC::TagInfo tag_info{}; if (result.IsSuccess()) { - result = device->GetTagInfo(tag_info, false); + result = device->GetTagInfo(tag_info); } if (result.IsSuccess()) { - result = device->ReadBackupData(tag_info.uuid, data); + result = device->ReadBackupData(tag_info.uuid, tag_info.uuid_length, data); result = VerifyDeviceResult(device, result); } @@ -565,11 +568,11 @@ Result DeviceManager::WriteBackupData(u64 device_handle, std::span<const u8> dat NFC::TagInfo tag_info{}; if (result.IsSuccess()) { - result = device->GetTagInfo(tag_info, false); + result = device->GetTagInfo(tag_info); } if (result.IsSuccess()) { - result = device->WriteBackupData(tag_info.uuid, data); + result = device->WriteBackupData(tag_info.uuid, tag_info.uuid_length, data); result = VerifyDeviceResult(device, result); } diff --git a/src/core/hle/service/nfc/common/device_manager.h b/src/core/hle/service/nfc/common/device_manager.h index 2971e280f3..c61ba0cf32 100644 --- a/src/core/hle/service/nfc/common/device_manager.h +++ b/src/core/hle/service/nfc/common/device_manager.h @@ -33,7 +33,7 @@ public: Kernel::KReadableEvent& AttachAvailabilityChangeEvent() const; Result StartDetection(u64 device_handle, NfcProtocol tag_protocol); Result StopDetection(u64 device_handle); - Result GetTagInfo(u64 device_handle, NFP::TagInfo& tag_info, bool is_mifare) const; + Result GetTagInfo(u64 device_handle, NFP::TagInfo& tag_info) const; Kernel::KReadableEvent& AttachActivateEvent(u64 device_handle) const; Kernel::KReadableEvent& AttachDeactivateEvent(u64 device_handle) const; Result ReadMifare(u64 device_handle, diff --git a/src/core/hle/service/nfc/mifare_result.h b/src/core/hle/service/nfc/mifare_result.h index 4b60048a56..16a9171e60 100644 --- a/src/core/hle/service/nfc/mifare_result.h +++ b/src/core/hle/service/nfc/mifare_result.h @@ -12,6 +12,6 @@ constexpr Result ResultInvalidArgument(ErrorModule::NFCMifare, 65); constexpr Result ResultWrongDeviceState(ErrorModule::NFCMifare, 73); constexpr Result ResultNfcDisabled(ErrorModule::NFCMifare, 80); constexpr Result ResultTagRemoved(ErrorModule::NFCMifare, 97); -constexpr Result ResultReadError(ErrorModule::NFCMifare, 288); +constexpr Result ResultNotAMifare(ErrorModule::NFCMifare, 288); } // namespace Service::NFC::Mifare diff --git a/src/core/hle/service/nfc/mifare_types.h b/src/core/hle/service/nfc/mifare_types.h index 75b59f0211..4679373994 100644 --- a/src/core/hle/service/nfc/mifare_types.h +++ b/src/core/hle/service/nfc/mifare_types.h @@ -11,9 +11,10 @@ namespace Service::NFC { enum class MifareCmd : u8 { + None = 0x00, + Read = 0x30, AuthA = 0x60, AuthB = 0x61, - Read = 0x30, Write = 0xA0, Transfer = 0xB0, Decrement = 0xC0, @@ -35,17 +36,17 @@ static_assert(sizeof(SectorKey) == 0x10, "SectorKey is an invalid size"); // This is nn::nfc::MifareReadBlockParameter struct MifareReadBlockParameter { - u8 sector_number; + u8 sector_number{}; INSERT_PADDING_BYTES(0x7); - SectorKey sector_key; + SectorKey sector_key{}; }; static_assert(sizeof(MifareReadBlockParameter) == 0x18, "MifareReadBlockParameter is an invalid size"); // This is nn::nfc::MifareReadBlockData struct MifareReadBlockData { - DataBlock data; - u8 sector_number; + DataBlock data{}; + u8 sector_number{}; INSERT_PADDING_BYTES(0x7); }; static_assert(sizeof(MifareReadBlockData) == 0x18, "MifareReadBlockData is an invalid size"); diff --git a/src/core/hle/service/nfc/nfc_interface.cpp b/src/core/hle/service/nfc/nfc_interface.cpp index 198d0f2b9f..e7ca7582ed 100644 --- a/src/core/hle/service/nfc/nfc_interface.cpp +++ b/src/core/hle/service/nfc/nfc_interface.cpp @@ -142,9 +142,13 @@ void NfcInterface::AttachAvailabilityChangeEvent(HLERequestContext& ctx) { void NfcInterface::StartDetection(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto device_handle{rp.Pop<u64>()}; - const auto tag_protocol{rp.PopEnum<NfcProtocol>()}; - LOG_INFO(Service_NFC, "called, device_handle={}, nfp_protocol={}", device_handle, tag_protocol); + auto tag_protocol{NfcProtocol::All}; + + if (backend_type == BackendType::Nfc) { + tag_protocol = rp.PopEnum<NfcProtocol>(); + } + LOG_INFO(Service_NFC, "called, device_handle={}, nfp_protocol={}", device_handle, tag_protocol); auto result = GetManager()->StartDetection(device_handle, tag_protocol); result = TranslateResultToServiceError(result); @@ -170,8 +174,7 @@ void NfcInterface::GetTagInfo(HLERequestContext& ctx) { LOG_INFO(Service_NFC, "called, device_handle={}", device_handle); TagInfo tag_info{}; - auto result = - GetManager()->GetTagInfo(device_handle, tag_info, backend_type == BackendType::Mifare); + auto result = GetManager()->GetTagInfo(device_handle, tag_info); result = TranslateResultToServiceError(result); if (result.IsSuccess()) { @@ -212,8 +215,8 @@ void NfcInterface::ReadMifare(HLERequestContext& ctx) { memcpy(read_commands.data(), buffer.data(), number_of_commands * sizeof(MifareReadBlockParameter)); - LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, read_commands_size={}", - device_handle, number_of_commands); + LOG_INFO(Service_NFC, "called, device_handle={}, read_commands_size={}", device_handle, + number_of_commands); std::vector<MifareReadBlockData> out_data(number_of_commands); auto result = GetManager()->ReadMifare(device_handle, read_commands, out_data); @@ -355,7 +358,7 @@ Result NfcInterface::TranslateResultToNfp(Result result) const { if (result == ResultApplicationAreaExist) { return NFP::ResultApplicationAreaExist; } - if (result == ResultNotAnAmiibo) { + if (result == ResultInvalidTagType) { return NFP::ResultNotAnAmiibo; } if (result == ResultUnableToAccessBackupFile) { @@ -381,6 +384,9 @@ Result NfcInterface::TranslateResultToMifare(Result result) const { if (result == ResultTagRemoved) { return Mifare::ResultTagRemoved; } + if (result == ResultInvalidTagType) { + return Mifare::ResultNotAMifare; + } LOG_WARNING(Service_NFC, "Result conversion not handled"); return result; } diff --git a/src/core/hle/service/nfc/nfc_result.h b/src/core/hle/service/nfc/nfc_result.h index 59a8087402..715c0e80c6 100644 --- a/src/core/hle/service/nfc/nfc_result.h +++ b/src/core/hle/service/nfc/nfc_result.h @@ -24,7 +24,8 @@ constexpr Result ResultCorruptedDataWithBackup(ErrorModule::NFC, 136); constexpr Result ResultCorruptedData(ErrorModule::NFC, 144); constexpr Result ResultWrongApplicationAreaId(ErrorModule::NFC, 152); constexpr Result ResultApplicationAreaExist(ErrorModule::NFC, 168); -constexpr Result ResultNotAnAmiibo(ErrorModule::NFC, 178); +constexpr Result ResultInvalidTagType(ErrorModule::NFC, 178); constexpr Result ResultBackupPathAlreadyExist(ErrorModule::NFC, 216); +constexpr Result ResultMifareError288(ErrorModule::NFC, 288); } // namespace Service::NFC diff --git a/src/core/hle/service/nfc/nfc_types.h b/src/core/hle/service/nfc/nfc_types.h index c7ebd1fdbb..68e724442f 100644 --- a/src/core/hle/service/nfc/nfc_types.h +++ b/src/core/hle/service/nfc/nfc_types.h @@ -35,32 +35,35 @@ enum class State : u32 { // This is nn::nfc::TagType enum class TagType : u32 { - None, - Type1, // ISO14443A RW 96-2k bytes 106kbit/s - Type2, // ISO14443A RW/RO 540 bytes 106kbit/s - Type3, // Sony FeliCa RW/RO 2k bytes 212kbit/s - Type4, // ISO14443A RW/RO 4k-32k bytes 424kbit/s - Type5, // ISO15693 RW/RO 540 bytes 106kbit/s + None = 0, + Type1 = 1U << 0, // ISO14443A RW. Topaz + Type2 = 1U << 1, // ISO14443A RW. Ultralight, NTAGX, ST25TN + Type3 = 1U << 2, // ISO14443A RW/RO. Sony FeliCa + Type4A = 1U << 3, // ISO14443A RW/RO. DESFire + Type4B = 1U << 4, // ISO14443B RW/RO. DESFire + Type5 = 1U << 5, // ISO15693 RW/RO. SLI, SLIX, ST25TV + Mifare = 1U << 6, // Mifare classic. Skylanders + All = 0xFFFFFFFF, }; enum class PackedTagType : u8 { - None, - Type1, // ISO14443A RW 96-2k bytes 106kbit/s - Type2, // ISO14443A RW/RO 540 bytes 106kbit/s - Type3, // Sony FeliCa RW/RO 2k bytes 212kbit/s - Type4, // ISO14443A RW/RO 4k-32k bytes 424kbit/s - Type5, // ISO15693 RW/RO 540 bytes 106kbit/s + None = 0, + Type1 = 1U << 0, // ISO14443A RW. Topaz + Type2 = 1U << 1, // ISO14443A RW. Ultralight, NTAGX, ST25TN + Type3 = 1U << 2, // ISO14443A RW/RO. Sony FeliCa + Type4A = 1U << 3, // ISO14443A RW/RO. DESFire + Type4B = 1U << 4, // ISO14443B RW/RO. DESFire + Type5 = 1U << 5, // ISO15693 RW/RO. SLI, SLIX, ST25TV + Mifare = 1U << 6, // Mifare classic. Skylanders + All = 0xFF, }; // This is nn::nfc::NfcProtocol -// Verify this enum. It might be completely wrong default protocol is 0x48 enum class NfcProtocol : u32 { None, TypeA = 1U << 0, // ISO14443A TypeB = 1U << 1, // ISO14443B TypeF = 1U << 2, // Sony FeliCa - Unknown1 = 1U << 3, - Unknown2 = 1U << 5, All = 0xFFFFFFFFU, }; @@ -69,8 +72,7 @@ enum class TestWaveType : u32 { Unknown, }; -using UniqueSerialNumber = std::array<u8, 7>; -using UniqueSerialNumberExtension = std::array<u8, 3>; +using UniqueSerialNumber = std::array<u8, 10>; // This is nn::nfc::DeviceHandle using DeviceHandle = u64; @@ -78,7 +80,6 @@ using DeviceHandle = u64; // This is nn::nfc::TagInfo struct TagInfo { UniqueSerialNumber uuid; - UniqueSerialNumberExtension uuid_extension; u8 uuid_length; INSERT_PADDING_BYTES(0x15); NfcProtocol protocol; diff --git a/src/core/hle/service/nfp/nfp_types.h b/src/core/hle/service/nfp/nfp_types.h index 7d36d5ee69..aed12a7f8e 100644 --- a/src/core/hle/service/nfp/nfp_types.h +++ b/src/core/hle/service/nfp/nfp_types.h @@ -85,7 +85,7 @@ enum class CabinetMode : u8 { StartFormatter, }; -using LockBytes = std::array<u8, 2>; +using UuidPart = std::array<u8, 3>; using HashData = std::array<u8, 0x20>; using ApplicationArea = std::array<u8, 0xD8>; using AmiiboName = std::array<char, (amiibo_name_length * 4) + 1>; @@ -93,12 +93,20 @@ using AmiiboName = std::array<char, (amiibo_name_length * 4) + 1>; // This is nn::nfp::TagInfo using TagInfo = NFC::TagInfo; +struct NtagTagUuid { + UuidPart part1; + UuidPart part2; + u8 nintendo_id; +}; +static_assert(sizeof(NtagTagUuid) == 7, "NtagTagUuid is an invalid size"); + struct TagUuid { - NFC::UniqueSerialNumber uid; + UuidPart part1; + u8 crc_check1; + UuidPart part2; u8 nintendo_id; - LockBytes lock_bytes; }; -static_assert(sizeof(TagUuid) == 10, "TagUuid is an invalid size"); +static_assert(sizeof(TagUuid) == 8, "TagUuid is an invalid size"); struct WriteDate { u16 year; @@ -231,7 +239,8 @@ struct EncryptedAmiiboFile { static_assert(sizeof(EncryptedAmiiboFile) == 0x1F8, "AmiiboFile is an invalid size"); struct NTAG215File { - LockBytes lock_bytes; // Tag UUID + u8 uid_crc_check2; + u8 internal_number; u16 static_lock; // Set defined pages as read only u32 compability_container; // Defines available memory HashData hmac_data; // Hash @@ -250,8 +259,7 @@ struct NTAG215File { u32_be register_info_crc; ApplicationArea application_area; // Encrypted Game data HashData hmac_tag; // Hash - NFC::UniqueSerialNumber uid; // Unique serial number - u8 nintendo_id; // Tag UUID + TagUuid uid; AmiiboModelInfo model_info; HashData keygen_salt; // Salt u32 dynamic_lock; // Dynamic lock @@ -264,7 +272,9 @@ static_assert(std::is_trivially_copyable_v<NTAG215File>, "NTAG215File must be tr #pragma pack() struct EncryptedNTAG215File { - TagUuid uuid; // Unique serial number + TagUuid uuid; + u8 uuid_crc_check2; + u8 internal_number; u16 static_lock; // Set defined pages as read only u32 compability_container; // Defines available memory EncryptedAmiiboFile user_memory; // Writable data diff --git a/src/core/hle/service/nvdrv/devices/nvdevice.h b/src/core/hle/service/nvdrv/devices/nvdevice.h index ab1f30f9e6..a04538d5d0 100644 --- a/src/core/hle/service/nvdrv/devices/nvdevice.h +++ b/src/core/hle/service/nvdrv/devices/nvdevice.h @@ -34,7 +34,7 @@ public: * @returns The result code of the ioctl. */ virtual NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::vector<u8>& output) = 0; + std::span<u8> output) = 0; /** * Handles an ioctl2 request. @@ -45,7 +45,7 @@ public: * @returns The result code of the ioctl. */ virtual NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::span<const u8> inline_input, std::vector<u8>& output) = 0; + std::span<const u8> inline_input, std::span<u8> output) = 0; /** * Handles an ioctl3 request. @@ -56,7 +56,7 @@ public: * @returns The result code of the ioctl. */ virtual NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::vector<u8>& output, std::vector<u8>& inline_output) = 0; + std::span<u8> output, std::span<u8> inline_output) = 0; /** * Called once a device is opened diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp index 5a5b2e3055..05a43d8dc4 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp @@ -18,19 +18,19 @@ nvdisp_disp0::nvdisp_disp0(Core::System& system_, NvCore::Container& core) nvdisp_disp0::~nvdisp_disp0() = default; NvResult nvdisp_disp0::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::vector<u8>& output) { + std::span<u8> output) { UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); return NvResult::NotImplemented; } NvResult nvdisp_disp0::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::span<const u8> inline_input, std::vector<u8>& output) { + std::span<const u8> inline_input, std::span<u8> output) { UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); return NvResult::NotImplemented; } NvResult nvdisp_disp0::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::vector<u8>& output, std::vector<u8>& inline_output) { + std::span<u8> output, std::span<u8> inline_output) { UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); return NvResult::NotImplemented; } @@ -51,8 +51,8 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, android::PixelFormat form stride, format, transform, crop_rect}; system.GPU().RequestSwapBuffers(&framebuffer, fences, num_fences); - system.GetPerfStats().EndSystemFrame(); system.SpeedLimiter().DoSpeedLimiting(system.CoreTiming().GetGlobalTimeUs()); + system.GetPerfStats().EndSystemFrame(); system.GetPerfStats().BeginSystemFrame(); } diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h index bcd0e3ed52..daee05fe81 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h @@ -26,11 +26,11 @@ public: ~nvdisp_disp0() override; NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::vector<u8>& output) override; + std::span<u8> output) override; NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::span<const u8> inline_input, std::vector<u8>& output) override; - NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output, - std::vector<u8>& inline_output) override; + std::span<const u8> inline_input, std::span<u8> output) override; + NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, + std::span<u8> inline_output) override; void OnOpen(DeviceFD fd) override; void OnClose(DeviceFD fd) override; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp index 681bd08673..07e570a9f0 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp @@ -28,7 +28,7 @@ nvhost_as_gpu::nvhost_as_gpu(Core::System& system_, Module& module_, NvCore::Con nvhost_as_gpu::~nvhost_as_gpu() = default; NvResult nvhost_as_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::vector<u8>& output) { + std::span<u8> output) { switch (command.group) { case 'A': switch (command.cmd) { @@ -61,13 +61,13 @@ NvResult nvhost_as_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> i } NvResult nvhost_as_gpu::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::span<const u8> inline_input, std::vector<u8>& output) { + std::span<const u8> inline_input, std::span<u8> output) { UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); return NvResult::NotImplemented; } NvResult nvhost_as_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::vector<u8>& output, std::vector<u8>& inline_output) { + std::span<u8> output, std::span<u8> inline_output) { switch (command.group) { case 'A': switch (command.cmd) { @@ -87,7 +87,7 @@ NvResult nvhost_as_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> i void nvhost_as_gpu::OnOpen(DeviceFD fd) {} void nvhost_as_gpu::OnClose(DeviceFD fd) {} -NvResult nvhost_as_gpu::AllocAsEx(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_as_gpu::AllocAsEx(std::span<const u8> input, std::span<u8> output) { IoctlAllocAsEx params{}; std::memcpy(¶ms, input.data(), input.size()); @@ -141,7 +141,7 @@ NvResult nvhost_as_gpu::AllocAsEx(std::span<const u8> input, std::vector<u8>& ou return NvResult::Success; } -NvResult nvhost_as_gpu::AllocateSpace(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_as_gpu::AllocateSpace(std::span<const u8> input, std::span<u8> output) { IoctlAllocSpace params{}; std::memcpy(¶ms, input.data(), input.size()); @@ -220,7 +220,7 @@ void nvhost_as_gpu::FreeMappingLocked(u64 offset) { mapping_map.erase(offset); } -NvResult nvhost_as_gpu::FreeSpace(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_as_gpu::FreeSpace(std::span<const u8> input, std::span<u8> output) { IoctlFreeSpace params{}; std::memcpy(¶ms, input.data(), input.size()); @@ -266,15 +266,14 @@ NvResult nvhost_as_gpu::FreeSpace(std::span<const u8> input, std::vector<u8>& ou return NvResult::Success; } -NvResult nvhost_as_gpu::Remap(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_as_gpu::Remap(std::span<const u8> input, std::span<u8> output) { const auto num_entries = input.size() / sizeof(IoctlRemapEntry); LOG_DEBUG(Service_NVDRV, "called, num_entries=0x{:X}", num_entries); - std::vector<IoctlRemapEntry> entries(num_entries); - std::memcpy(entries.data(), input.data(), input.size()); - std::scoped_lock lock(mutex); + entries.resize_destructive(num_entries); + std::memcpy(entries.data(), input.data(), input.size()); if (!vm.initialised) { return NvResult::BadValue; @@ -320,7 +319,7 @@ NvResult nvhost_as_gpu::Remap(std::span<const u8> input, std::vector<u8>& output return NvResult::Success; } -NvResult nvhost_as_gpu::MapBufferEx(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_as_gpu::MapBufferEx(std::span<const u8> input, std::span<u8> output) { IoctlMapBufferEx params{}; std::memcpy(¶ms, input.data(), input.size()); @@ -424,7 +423,7 @@ NvResult nvhost_as_gpu::MapBufferEx(std::span<const u8> input, std::vector<u8>& return NvResult::Success; } -NvResult nvhost_as_gpu::UnmapBuffer(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_as_gpu::UnmapBuffer(std::span<const u8> input, std::span<u8> output) { IoctlUnmapBuffer params{}; std::memcpy(¶ms, input.data(), input.size()); @@ -463,7 +462,7 @@ NvResult nvhost_as_gpu::UnmapBuffer(std::span<const u8> input, std::vector<u8>& return NvResult::Success; } -NvResult nvhost_as_gpu::BindChannel(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_as_gpu::BindChannel(std::span<const u8> input, std::span<u8> output) { IoctlBindChannel params{}; std::memcpy(¶ms, input.data(), input.size()); LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd); @@ -492,7 +491,7 @@ void nvhost_as_gpu::GetVARegionsImpl(IoctlGetVaRegions& params) { }; } -NvResult nvhost_as_gpu::GetVARegions(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_as_gpu::GetVARegions(std::span<const u8> input, std::span<u8> output) { IoctlGetVaRegions params{}; std::memcpy(¶ms, input.data(), input.size()); @@ -511,8 +510,8 @@ NvResult nvhost_as_gpu::GetVARegions(std::span<const u8> input, std::vector<u8>& return NvResult::Success; } -NvResult nvhost_as_gpu::GetVARegions(std::span<const u8> input, std::vector<u8>& output, - std::vector<u8>& inline_output) { +NvResult nvhost_as_gpu::GetVARegions(std::span<const u8> input, std::span<u8> output, + std::span<u8> inline_output) { IoctlGetVaRegions params{}; std::memcpy(¶ms, input.data(), input.size()); diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h index 1aba8d5799..2af3e1260a 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h @@ -15,6 +15,7 @@ #include "common/address_space.h" #include "common/common_funcs.h" #include "common/common_types.h" +#include "common/scratch_buffer.h" #include "common/swap.h" #include "core/hle/service/nvdrv/core/nvmap.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" @@ -48,11 +49,11 @@ public: ~nvhost_as_gpu() override; NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::vector<u8>& output) override; + std::span<u8> output) override; NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::span<const u8> inline_input, std::vector<u8>& output) override; - NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output, - std::vector<u8>& inline_output) override; + std::span<const u8> inline_input, std::span<u8> output) override; + NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, + std::span<u8> inline_output) override; void OnOpen(DeviceFD fd) override; void OnClose(DeviceFD fd) override; @@ -138,18 +139,18 @@ private: static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(VaRegion) * 2, "IoctlGetVaRegions is incorrect size"); - NvResult AllocAsEx(std::span<const u8> input, std::vector<u8>& output); - NvResult AllocateSpace(std::span<const u8> input, std::vector<u8>& output); - NvResult Remap(std::span<const u8> input, std::vector<u8>& output); - NvResult MapBufferEx(std::span<const u8> input, std::vector<u8>& output); - NvResult UnmapBuffer(std::span<const u8> input, std::vector<u8>& output); - NvResult FreeSpace(std::span<const u8> input, std::vector<u8>& output); - NvResult BindChannel(std::span<const u8> input, std::vector<u8>& output); + NvResult AllocAsEx(std::span<const u8> input, std::span<u8> output); + NvResult AllocateSpace(std::span<const u8> input, std::span<u8> output); + NvResult Remap(std::span<const u8> input, std::span<u8> output); + NvResult MapBufferEx(std::span<const u8> input, std::span<u8> output); + NvResult UnmapBuffer(std::span<const u8> input, std::span<u8> output); + NvResult FreeSpace(std::span<const u8> input, std::span<u8> output); + NvResult BindChannel(std::span<const u8> input, std::span<u8> output); void GetVARegionsImpl(IoctlGetVaRegions& params); - NvResult GetVARegions(std::span<const u8> input, std::vector<u8>& output); - NvResult GetVARegions(std::span<const u8> input, std::vector<u8>& output, - std::vector<u8>& inline_output); + NvResult GetVARegions(std::span<const u8> input, std::span<u8> output); + NvResult GetVARegions(std::span<const u8> input, std::span<u8> output, + std::span<u8> inline_output); void FreeMappingLocked(u64 offset); @@ -212,6 +213,7 @@ private: bool initialised{}; } vm; std::shared_ptr<Tegra::MemoryManager> gmmu; + Common::ScratchBuffer<IoctlRemapEntry> entries; // s32 channel{}; // u32 big_page_size{VM::DEFAULT_BIG_PAGE_SIZE}; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index e120255600..4d55554b4e 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp @@ -35,7 +35,7 @@ nvhost_ctrl::~nvhost_ctrl() { } NvResult nvhost_ctrl::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::vector<u8>& output) { + std::span<u8> output) { switch (command.group) { case 0x0: switch (command.cmd) { @@ -64,13 +64,13 @@ NvResult nvhost_ctrl::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inp } NvResult nvhost_ctrl::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::span<const u8> inline_input, std::vector<u8>& output) { + std::span<const u8> inline_input, std::span<u8> output) { UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); return NvResult::NotImplemented; } NvResult nvhost_ctrl::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::vector<u8>& output, std::vector<u8>& inline_outpu) { + std::span<u8> output, std::span<u8> inline_outpu) { UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); return NvResult::NotImplemented; } @@ -79,7 +79,7 @@ void nvhost_ctrl::OnOpen(DeviceFD fd) {} void nvhost_ctrl::OnClose(DeviceFD fd) {} -NvResult nvhost_ctrl::NvOsGetConfigU32(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_ctrl::NvOsGetConfigU32(std::span<const u8> input, std::span<u8> output) { IocGetConfigParams params{}; std::memcpy(¶ms, input.data(), sizeof(params)); LOG_TRACE(Service_NVDRV, "called, setting={}!{}", params.domain_str.data(), @@ -87,7 +87,7 @@ NvResult nvhost_ctrl::NvOsGetConfigU32(std::span<const u8> input, std::vector<u8 return NvResult::ConfigVarNotFound; // Returns error on production mode } -NvResult nvhost_ctrl::IocCtrlEventWait(std::span<const u8> input, std::vector<u8>& output, +NvResult nvhost_ctrl::IocCtrlEventWait(std::span<const u8> input, std::span<u8> output, bool is_allocation) { IocCtrlEventWaitParams params{}; std::memcpy(¶ms, input.data(), sizeof(params)); @@ -231,7 +231,7 @@ NvResult nvhost_ctrl::FreeEvent(u32 slot) { return NvResult::Success; } -NvResult nvhost_ctrl::IocCtrlEventRegister(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_ctrl::IocCtrlEventRegister(std::span<const u8> input, std::span<u8> output) { IocCtrlEventRegisterParams params{}; std::memcpy(¶ms, input.data(), sizeof(params)); const u32 event_id = params.user_event_id; @@ -252,7 +252,7 @@ NvResult nvhost_ctrl::IocCtrlEventRegister(std::span<const u8> input, std::vecto return NvResult::Success; } -NvResult nvhost_ctrl::IocCtrlEventUnregister(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_ctrl::IocCtrlEventUnregister(std::span<const u8> input, std::span<u8> output) { IocCtrlEventUnregisterParams params{}; std::memcpy(¶ms, input.data(), sizeof(params)); const u32 event_id = params.user_event_id & 0x00FF; @@ -262,8 +262,7 @@ NvResult nvhost_ctrl::IocCtrlEventUnregister(std::span<const u8> input, std::vec return FreeEvent(event_id); } -NvResult nvhost_ctrl::IocCtrlEventUnregisterBatch(std::span<const u8> input, - std::vector<u8>& output) { +NvResult nvhost_ctrl::IocCtrlEventUnregisterBatch(std::span<const u8> input, std::span<u8> output) { IocCtrlEventUnregisterBatchParams params{}; std::memcpy(¶ms, input.data(), sizeof(params)); u64 event_mask = params.user_events; @@ -281,7 +280,7 @@ NvResult nvhost_ctrl::IocCtrlEventUnregisterBatch(std::span<const u8> input, return NvResult::Success; } -NvResult nvhost_ctrl::IocCtrlClearEventWait(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_ctrl::IocCtrlClearEventWait(std::span<const u8> input, std::span<u8> output) { IocCtrlEventClearParams params{}; std::memcpy(¶ms, input.data(), sizeof(params)); diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h index dd2e7888af..2efed4862d 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h @@ -26,11 +26,11 @@ public: ~nvhost_ctrl() override; NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::vector<u8>& output) override; + std::span<u8> output) override; NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::span<const u8> inline_input, std::vector<u8>& output) override; - NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output, - std::vector<u8>& inline_output) override; + std::span<const u8> inline_input, std::span<u8> output) override; + NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, + std::span<u8> inline_output) override; void OnOpen(DeviceFD fd) override; void OnClose(DeviceFD fd) override; @@ -186,13 +186,12 @@ private: static_assert(sizeof(IocCtrlEventUnregisterBatchParams) == 8, "IocCtrlEventKill is incorrect size"); - NvResult NvOsGetConfigU32(std::span<const u8> input, std::vector<u8>& output); - NvResult IocCtrlEventWait(std::span<const u8> input, std::vector<u8>& output, - bool is_allocation); - NvResult IocCtrlEventRegister(std::span<const u8> input, std::vector<u8>& output); - NvResult IocCtrlEventUnregister(std::span<const u8> input, std::vector<u8>& output); - NvResult IocCtrlEventUnregisterBatch(std::span<const u8> input, std::vector<u8>& output); - NvResult IocCtrlClearEventWait(std::span<const u8> input, std::vector<u8>& output); + NvResult NvOsGetConfigU32(std::span<const u8> input, std::span<u8> output); + NvResult IocCtrlEventWait(std::span<const u8> input, std::span<u8> output, bool is_allocation); + NvResult IocCtrlEventRegister(std::span<const u8> input, std::span<u8> output); + NvResult IocCtrlEventUnregister(std::span<const u8> input, std::span<u8> output); + NvResult IocCtrlEventUnregisterBatch(std::span<const u8> input, std::span<u8> output); + NvResult IocCtrlClearEventWait(std::span<const u8> input, std::span<u8> output); NvResult FreeEvent(u32 slot); diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp index be3c083dbf..6081d92e97 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp @@ -22,7 +22,7 @@ nvhost_ctrl_gpu::~nvhost_ctrl_gpu() { } NvResult nvhost_ctrl_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::vector<u8>& output) { + std::span<u8> output) { switch (command.group) { case 'G': switch (command.cmd) { @@ -54,13 +54,13 @@ NvResult nvhost_ctrl_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> } NvResult nvhost_ctrl_gpu::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::span<const u8> inline_input, std::vector<u8>& output) { + std::span<const u8> inline_input, std::span<u8> output) { UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); return NvResult::NotImplemented; } NvResult nvhost_ctrl_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::vector<u8>& output, std::vector<u8>& inline_output) { + std::span<u8> output, std::span<u8> inline_output) { switch (command.group) { case 'G': switch (command.cmd) { @@ -82,7 +82,7 @@ NvResult nvhost_ctrl_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> void nvhost_ctrl_gpu::OnOpen(DeviceFD fd) {} void nvhost_ctrl_gpu::OnClose(DeviceFD fd) {} -NvResult nvhost_ctrl_gpu::GetCharacteristics(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_ctrl_gpu::GetCharacteristics(std::span<const u8> input, std::span<u8> output) { LOG_DEBUG(Service_NVDRV, "called"); IoctlCharacteristics params{}; std::memcpy(¶ms, input.data(), input.size()); @@ -127,8 +127,8 @@ NvResult nvhost_ctrl_gpu::GetCharacteristics(std::span<const u8> input, std::vec return NvResult::Success; } -NvResult nvhost_ctrl_gpu::GetCharacteristics(std::span<const u8> input, std::vector<u8>& output, - std::vector<u8>& inline_output) { +NvResult nvhost_ctrl_gpu::GetCharacteristics(std::span<const u8> input, std::span<u8> output, + std::span<u8> inline_output) { LOG_DEBUG(Service_NVDRV, "called"); IoctlCharacteristics params{}; std::memcpy(¶ms, input.data(), input.size()); @@ -175,7 +175,7 @@ NvResult nvhost_ctrl_gpu::GetCharacteristics(std::span<const u8> input, std::vec return NvResult::Success; } -NvResult nvhost_ctrl_gpu::GetTPCMasks(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_ctrl_gpu::GetTPCMasks(std::span<const u8> input, std::span<u8> output) { IoctlGpuGetTpcMasksArgs params{}; std::memcpy(¶ms, input.data(), input.size()); LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size); @@ -186,8 +186,8 @@ NvResult nvhost_ctrl_gpu::GetTPCMasks(std::span<const u8> input, std::vector<u8> return NvResult::Success; } -NvResult nvhost_ctrl_gpu::GetTPCMasks(std::span<const u8> input, std::vector<u8>& output, - std::vector<u8>& inline_output) { +NvResult nvhost_ctrl_gpu::GetTPCMasks(std::span<const u8> input, std::span<u8> output, + std::span<u8> inline_output) { IoctlGpuGetTpcMasksArgs params{}; std::memcpy(¶ms, input.data(), input.size()); LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size); @@ -199,7 +199,7 @@ NvResult nvhost_ctrl_gpu::GetTPCMasks(std::span<const u8> input, std::vector<u8> return NvResult::Success; } -NvResult nvhost_ctrl_gpu::GetActiveSlotMask(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_ctrl_gpu::GetActiveSlotMask(std::span<const u8> input, std::span<u8> output) { LOG_DEBUG(Service_NVDRV, "called"); IoctlActiveSlotMask params{}; @@ -212,7 +212,7 @@ NvResult nvhost_ctrl_gpu::GetActiveSlotMask(std::span<const u8> input, std::vect return NvResult::Success; } -NvResult nvhost_ctrl_gpu::ZCullGetCtxSize(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_ctrl_gpu::ZCullGetCtxSize(std::span<const u8> input, std::span<u8> output) { LOG_DEBUG(Service_NVDRV, "called"); IoctlZcullGetCtxSize params{}; @@ -224,7 +224,7 @@ NvResult nvhost_ctrl_gpu::ZCullGetCtxSize(std::span<const u8> input, std::vector return NvResult::Success; } -NvResult nvhost_ctrl_gpu::ZCullGetInfo(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_ctrl_gpu::ZCullGetInfo(std::span<const u8> input, std::span<u8> output) { LOG_DEBUG(Service_NVDRV, "called"); IoctlNvgpuGpuZcullGetInfoArgs params{}; @@ -247,7 +247,7 @@ NvResult nvhost_ctrl_gpu::ZCullGetInfo(std::span<const u8> input, std::vector<u8 return NvResult::Success; } -NvResult nvhost_ctrl_gpu::ZBCSetTable(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_ctrl_gpu::ZBCSetTable(std::span<const u8> input, std::span<u8> output) { LOG_WARNING(Service_NVDRV, "(STUBBED) called"); IoctlZbcSetTable params{}; @@ -263,7 +263,7 @@ NvResult nvhost_ctrl_gpu::ZBCSetTable(std::span<const u8> input, std::vector<u8> return NvResult::Success; } -NvResult nvhost_ctrl_gpu::ZBCQueryTable(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_ctrl_gpu::ZBCQueryTable(std::span<const u8> input, std::span<u8> output) { LOG_WARNING(Service_NVDRV, "(STUBBED) called"); IoctlZbcQueryTable params{}; @@ -273,7 +273,7 @@ NvResult nvhost_ctrl_gpu::ZBCQueryTable(std::span<const u8> input, std::vector<u return NvResult::Success; } -NvResult nvhost_ctrl_gpu::FlushL2(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_ctrl_gpu::FlushL2(std::span<const u8> input, std::span<u8> output) { LOG_WARNING(Service_NVDRV, "(STUBBED) called"); IoctlFlushL2 params{}; @@ -283,7 +283,7 @@ NvResult nvhost_ctrl_gpu::FlushL2(std::span<const u8> input, std::vector<u8>& ou return NvResult::Success; } -NvResult nvhost_ctrl_gpu::GetGpuTime(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_ctrl_gpu::GetGpuTime(std::span<const u8> input, std::span<u8> output) { LOG_DEBUG(Service_NVDRV, "called"); IoctlGetGpuTime params{}; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h index b9333d9d39..97995551cc 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h @@ -22,11 +22,11 @@ public: ~nvhost_ctrl_gpu() override; NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::vector<u8>& output) override; + std::span<u8> output) override; NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::span<const u8> inline_input, std::vector<u8>& output) override; - NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output, - std::vector<u8>& inline_output) override; + std::span<const u8> inline_input, std::span<u8> output) override; + NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, + std::span<u8> inline_output) override; void OnOpen(DeviceFD fd) override; void OnClose(DeviceFD fd) override; @@ -151,21 +151,21 @@ private: }; static_assert(sizeof(IoctlGetGpuTime) == 0x10, "IoctlGetGpuTime is incorrect size"); - NvResult GetCharacteristics(std::span<const u8> input, std::vector<u8>& output); - NvResult GetCharacteristics(std::span<const u8> input, std::vector<u8>& output, - std::vector<u8>& inline_output); - - NvResult GetTPCMasks(std::span<const u8> input, std::vector<u8>& output); - NvResult GetTPCMasks(std::span<const u8> input, std::vector<u8>& output, - std::vector<u8>& inline_output); - - NvResult GetActiveSlotMask(std::span<const u8> input, std::vector<u8>& output); - NvResult ZCullGetCtxSize(std::span<const u8> input, std::vector<u8>& output); - NvResult ZCullGetInfo(std::span<const u8> input, std::vector<u8>& output); - NvResult ZBCSetTable(std::span<const u8> input, std::vector<u8>& output); - NvResult ZBCQueryTable(std::span<const u8> input, std::vector<u8>& output); - NvResult FlushL2(std::span<const u8> input, std::vector<u8>& output); - NvResult GetGpuTime(std::span<const u8> input, std::vector<u8>& output); + NvResult GetCharacteristics(std::span<const u8> input, std::span<u8> output); + NvResult GetCharacteristics(std::span<const u8> input, std::span<u8> output, + std::span<u8> inline_output); + + NvResult GetTPCMasks(std::span<const u8> input, std::span<u8> output); + NvResult GetTPCMasks(std::span<const u8> input, std::span<u8> output, + std::span<u8> inline_output); + + NvResult GetActiveSlotMask(std::span<const u8> input, std::span<u8> output); + NvResult ZCullGetCtxSize(std::span<const u8> input, std::span<u8> output); + NvResult ZCullGetInfo(std::span<const u8> input, std::span<u8> output); + NvResult ZBCSetTable(std::span<const u8> input, std::span<u8> output); + NvResult ZBCQueryTable(std::span<const u8> input, std::span<u8> output); + NvResult FlushL2(std::span<const u8> input, std::span<u8> output); + NvResult GetGpuTime(std::span<const u8> input, std::span<u8> output); EventInterface& events_interface; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index 453a965dcb..46a25fcaba 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp @@ -47,7 +47,7 @@ nvhost_gpu::~nvhost_gpu() { } NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::vector<u8>& output) { + std::span<u8> output) { switch (command.group) { case 0x0: switch (command.cmd) { @@ -99,7 +99,7 @@ NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inpu }; NvResult nvhost_gpu::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::span<const u8> inline_input, std::vector<u8>& output) { + std::span<const u8> inline_input, std::span<u8> output) { switch (command.group) { case 'H': switch (command.cmd) { @@ -113,7 +113,7 @@ NvResult nvhost_gpu::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> inpu } NvResult nvhost_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::vector<u8>& output, std::vector<u8>& inline_output) { + std::span<u8> output, std::span<u8> inline_output) { UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); return NvResult::NotImplemented; } @@ -121,7 +121,7 @@ NvResult nvhost_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> inpu void nvhost_gpu::OnOpen(DeviceFD fd) {} void nvhost_gpu::OnClose(DeviceFD fd) {} -NvResult nvhost_gpu::SetNVMAPfd(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_gpu::SetNVMAPfd(std::span<const u8> input, std::span<u8> output) { IoctlSetNvmapFD params{}; std::memcpy(¶ms, input.data(), input.size()); LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); @@ -130,7 +130,7 @@ NvResult nvhost_gpu::SetNVMAPfd(std::span<const u8> input, std::vector<u8>& outp return NvResult::Success; } -NvResult nvhost_gpu::SetClientData(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_gpu::SetClientData(std::span<const u8> input, std::span<u8> output) { LOG_DEBUG(Service_NVDRV, "called"); IoctlClientData params{}; @@ -139,7 +139,7 @@ NvResult nvhost_gpu::SetClientData(std::span<const u8> input, std::vector<u8>& o return NvResult::Success; } -NvResult nvhost_gpu::GetClientData(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_gpu::GetClientData(std::span<const u8> input, std::span<u8> output) { LOG_DEBUG(Service_NVDRV, "called"); IoctlClientData params{}; @@ -149,7 +149,7 @@ NvResult nvhost_gpu::GetClientData(std::span<const u8> input, std::vector<u8>& o return NvResult::Success; } -NvResult nvhost_gpu::ZCullBind(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_gpu::ZCullBind(std::span<const u8> input, std::span<u8> output) { std::memcpy(&zcull_params, input.data(), input.size()); LOG_DEBUG(Service_NVDRV, "called, gpu_va={:X}, mode={:X}", zcull_params.gpu_va, zcull_params.mode); @@ -158,7 +158,7 @@ NvResult nvhost_gpu::ZCullBind(std::span<const u8> input, std::vector<u8>& outpu return NvResult::Success; } -NvResult nvhost_gpu::SetErrorNotifier(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_gpu::SetErrorNotifier(std::span<const u8> input, std::span<u8> output) { IoctlSetErrorNotifier params{}; std::memcpy(¶ms, input.data(), input.size()); LOG_WARNING(Service_NVDRV, "(STUBBED) called, offset={:X}, size={:X}, mem={:X}", params.offset, @@ -168,14 +168,14 @@ NvResult nvhost_gpu::SetErrorNotifier(std::span<const u8> input, std::vector<u8> return NvResult::Success; } -NvResult nvhost_gpu::SetChannelPriority(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_gpu::SetChannelPriority(std::span<const u8> input, std::span<u8> output) { std::memcpy(&channel_priority, input.data(), input.size()); LOG_DEBUG(Service_NVDRV, "(STUBBED) called, priority={:X}", channel_priority); return NvResult::Success; } -NvResult nvhost_gpu::AllocGPFIFOEx2(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_gpu::AllocGPFIFOEx2(std::span<const u8> input, std::span<u8> output) { IoctlAllocGpfifoEx2 params{}; std::memcpy(¶ms, input.data(), input.size()); LOG_WARNING(Service_NVDRV, @@ -197,7 +197,7 @@ NvResult nvhost_gpu::AllocGPFIFOEx2(std::span<const u8> input, std::vector<u8>& return NvResult::Success; } -NvResult nvhost_gpu::AllocateObjectContext(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_gpu::AllocateObjectContext(std::span<const u8> input, std::span<u8> output) { IoctlAllocObjCtx params{}; std::memcpy(¶ms, input.data(), input.size()); LOG_WARNING(Service_NVDRV, "(STUBBED) called, class_num={:X}, flags={:X}", params.class_num, @@ -208,7 +208,8 @@ NvResult nvhost_gpu::AllocateObjectContext(std::span<const u8> input, std::vecto return NvResult::Success; } -static std::vector<Tegra::CommandHeader> BuildWaitCommandList(NvFence fence) { +static boost::container::small_vector<Tegra::CommandHeader, 512> BuildWaitCommandList( + NvFence fence) { return { Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointPayload, 1, Tegra::SubmissionMode::Increasing), @@ -219,35 +220,35 @@ static std::vector<Tegra::CommandHeader> BuildWaitCommandList(NvFence fence) { }; } -static std::vector<Tegra::CommandHeader> BuildIncrementCommandList(NvFence fence) { - std::vector<Tegra::CommandHeader> result{ +static boost::container::small_vector<Tegra::CommandHeader, 512> BuildIncrementCommandList( + NvFence fence) { + boost::container::small_vector<Tegra::CommandHeader, 512> result{ Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointPayload, 1, Tegra::SubmissionMode::Increasing), {}}; for (u32 count = 0; count < 2; ++count) { - result.emplace_back(Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointOperation, 1, - Tegra::SubmissionMode::Increasing)); - result.emplace_back( + result.push_back(Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointOperation, 1, + Tegra::SubmissionMode::Increasing)); + result.push_back( BuildFenceAction(Tegra::Engines::Puller::FenceOperation::Increment, fence.id)); } return result; } -static std::vector<Tegra::CommandHeader> BuildIncrementWithWfiCommandList(NvFence fence) { - std::vector<Tegra::CommandHeader> result{ +static boost::container::small_vector<Tegra::CommandHeader, 512> BuildIncrementWithWfiCommandList( + NvFence fence) { + boost::container::small_vector<Tegra::CommandHeader, 512> result{ Tegra::BuildCommandHeader(Tegra::BufferMethods::WaitForIdle, 1, Tegra::SubmissionMode::Increasing), {}}; - const std::vector<Tegra::CommandHeader> increment{BuildIncrementCommandList(fence)}; - - result.insert(result.end(), increment.begin(), increment.end()); - + auto increment_list{BuildIncrementCommandList(fence)}; + result.insert(result.end(), increment_list.begin(), increment_list.end()); return result; } -NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& output, +NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::span<u8> output, Tegra::CommandList&& entries) { LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address, params.num_entries, params.flags.raw); @@ -293,7 +294,7 @@ NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8> return NvResult::Success; } -NvResult nvhost_gpu::SubmitGPFIFOBase(std::span<const u8> input, std::vector<u8>& output, +NvResult nvhost_gpu::SubmitGPFIFOBase(std::span<const u8> input, std::span<u8> output, bool kickoff) { if (input.size() < sizeof(IoctlSubmitGpfifo)) { UNIMPLEMENTED(); @@ -315,7 +316,7 @@ NvResult nvhost_gpu::SubmitGPFIFOBase(std::span<const u8> input, std::vector<u8> } NvResult nvhost_gpu::SubmitGPFIFOBase(std::span<const u8> input, std::span<const u8> input_inline, - std::vector<u8>& output) { + std::span<u8> output) { if (input.size() < sizeof(IoctlSubmitGpfifo)) { UNIMPLEMENTED(); return NvResult::InvalidSize; @@ -327,7 +328,7 @@ NvResult nvhost_gpu::SubmitGPFIFOBase(std::span<const u8> input, std::span<const return SubmitGPFIFOImpl(params, output, std::move(entries)); } -NvResult nvhost_gpu::GetWaitbase(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_gpu::GetWaitbase(std::span<const u8> input, std::span<u8> output) { IoctlGetWaitbase params{}; std::memcpy(¶ms, input.data(), sizeof(IoctlGetWaitbase)); LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown); @@ -337,7 +338,7 @@ NvResult nvhost_gpu::GetWaitbase(std::span<const u8> input, std::vector<u8>& out return NvResult::Success; } -NvResult nvhost_gpu::ChannelSetTimeout(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_gpu::ChannelSetTimeout(std::span<const u8> input, std::span<u8> output) { IoctlChannelSetTimeout params{}; std::memcpy(¶ms, input.data(), sizeof(IoctlChannelSetTimeout)); LOG_INFO(Service_NVDRV, "called, timeout=0x{:X}", params.timeout); @@ -345,7 +346,7 @@ NvResult nvhost_gpu::ChannelSetTimeout(std::span<const u8> input, std::vector<u8 return NvResult::Success; } -NvResult nvhost_gpu::ChannelSetTimeslice(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_gpu::ChannelSetTimeslice(std::span<const u8> input, std::span<u8> output) { IoctlSetTimeslice params{}; std::memcpy(¶ms, input.data(), sizeof(IoctlSetTimeslice)); LOG_INFO(Service_NVDRV, "called, timeslice=0x{:X}", params.timeslice); diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h index 3ca58202d3..529c20526a 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h @@ -41,11 +41,11 @@ public: ~nvhost_gpu() override; NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::vector<u8>& output) override; + std::span<u8> output) override; NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::span<const u8> inline_input, std::vector<u8>& output) override; - NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output, - std::vector<u8>& inline_output) override; + std::span<const u8> inline_input, std::span<u8> output) override; + NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, + std::span<u8> inline_output) override; void OnOpen(DeviceFD fd) override; void OnClose(DeviceFD fd) override; @@ -186,23 +186,23 @@ private: u32_le channel_priority{}; u32_le channel_timeslice{}; - NvResult SetNVMAPfd(std::span<const u8> input, std::vector<u8>& output); - NvResult SetClientData(std::span<const u8> input, std::vector<u8>& output); - NvResult GetClientData(std::span<const u8> input, std::vector<u8>& output); - NvResult ZCullBind(std::span<const u8> input, std::vector<u8>& output); - NvResult SetErrorNotifier(std::span<const u8> input, std::vector<u8>& output); - NvResult SetChannelPriority(std::span<const u8> input, std::vector<u8>& output); - NvResult AllocGPFIFOEx2(std::span<const u8> input, std::vector<u8>& output); - NvResult AllocateObjectContext(std::span<const u8> input, std::vector<u8>& output); - NvResult SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& output, + NvResult SetNVMAPfd(std::span<const u8> input, std::span<u8> output); + NvResult SetClientData(std::span<const u8> input, std::span<u8> output); + NvResult GetClientData(std::span<const u8> input, std::span<u8> output); + NvResult ZCullBind(std::span<const u8> input, std::span<u8> output); + NvResult SetErrorNotifier(std::span<const u8> input, std::span<u8> output); + NvResult SetChannelPriority(std::span<const u8> input, std::span<u8> output); + NvResult AllocGPFIFOEx2(std::span<const u8> input, std::span<u8> output); + NvResult AllocateObjectContext(std::span<const u8> input, std::span<u8> output); + NvResult SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::span<u8> output, Tegra::CommandList&& entries); - NvResult SubmitGPFIFOBase(std::span<const u8> input, std::vector<u8>& output, + NvResult SubmitGPFIFOBase(std::span<const u8> input, std::span<u8> output, bool kickoff = false); NvResult SubmitGPFIFOBase(std::span<const u8> input, std::span<const u8> input_inline, - std::vector<u8>& output); - NvResult GetWaitbase(std::span<const u8> input, std::vector<u8>& output); - NvResult ChannelSetTimeout(std::span<const u8> input, std::vector<u8>& output); - NvResult ChannelSetTimeslice(std::span<const u8> input, std::vector<u8>& output); + std::span<u8> output); + NvResult GetWaitbase(std::span<const u8> input, std::span<u8> output); + NvResult ChannelSetTimeout(std::span<const u8> input, std::span<u8> output); + NvResult ChannelSetTimeslice(std::span<const u8> input, std::span<u8> output); EventInterface& events_interface; NvCore::Container& core; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp index dc45169ad9..a174442a6c 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp @@ -16,7 +16,7 @@ nvhost_nvdec::nvhost_nvdec(Core::System& system_, NvCore::Container& core_) nvhost_nvdec::~nvhost_nvdec() = default; NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::vector<u8>& output) { + std::span<u8> output) { switch (command.group) { case 0x0: switch (command.cmd) { @@ -56,13 +56,13 @@ NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> in } NvResult nvhost_nvdec::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::span<const u8> inline_input, std::vector<u8>& output) { + std::span<const u8> inline_input, std::span<u8> output) { UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); return NvResult::NotImplemented; } NvResult nvhost_nvdec::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::vector<u8>& output, std::vector<u8>& inline_output) { + std::span<u8> output, std::span<u8> inline_output) { UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); return NvResult::NotImplemented; } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h index 0d615bbcbf..ad2233c493 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h @@ -14,11 +14,11 @@ public: ~nvhost_nvdec() override; NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::vector<u8>& output) override; + std::span<u8> output) override; NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::span<const u8> inline_input, std::vector<u8>& output) override; - NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output, - std::vector<u8>& inline_output) override; + std::span<const u8> inline_input, std::span<u8> output) override; + NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, + std::span<u8> inline_output) override; void OnOpen(DeviceFD fd) override; void OnClose(DeviceFD fd) override; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp index 1ab51f10b0..61649aa4a0 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp @@ -36,7 +36,7 @@ std::size_t SliceVectors(std::span<const u8> input, std::vector<T>& dst, std::si // Writes the data in src to an offset into the dst vector. The offset is specified in bytes // Returns the number of bytes written into dst. template <typename T> -std::size_t WriteVectors(std::vector<u8>& dst, const std::vector<T>& src, std::size_t offset) { +std::size_t WriteVectors(std::span<u8> dst, const std::vector<T>& src, std::size_t offset) { if (src.empty()) { return 0; } @@ -72,8 +72,7 @@ NvResult nvhost_nvdec_common::SetNVMAPfd(std::span<const u8> input) { return NvResult::Success; } -NvResult nvhost_nvdec_common::Submit(DeviceFD fd, std::span<const u8> input, - std::vector<u8>& output) { +NvResult nvhost_nvdec_common::Submit(DeviceFD fd, std::span<const u8> input, std::span<u8> output) { IoctlSubmit params{}; std::memcpy(¶ms, input.data(), sizeof(IoctlSubmit)); LOG_DEBUG(Service_NVDRV, "called NVDEC Submit, cmd_buffer_count={}", params.cmd_buffer_count); @@ -121,7 +120,7 @@ NvResult nvhost_nvdec_common::Submit(DeviceFD fd, std::span<const u8> input, return NvResult::Success; } -NvResult nvhost_nvdec_common::GetSyncpoint(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_nvdec_common::GetSyncpoint(std::span<const u8> input, std::span<u8> output) { IoctlGetSyncpoint params{}; std::memcpy(¶ms, input.data(), sizeof(IoctlGetSyncpoint)); LOG_DEBUG(Service_NVDRV, "called GetSyncpoint, id={}", params.param); @@ -133,7 +132,7 @@ NvResult nvhost_nvdec_common::GetSyncpoint(std::span<const u8> input, std::vecto return NvResult::Success; } -NvResult nvhost_nvdec_common::GetWaitbase(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_nvdec_common::GetWaitbase(std::span<const u8> input, std::span<u8> output) { IoctlGetWaitbase params{}; LOG_CRITICAL(Service_NVDRV, "called WAITBASE"); std::memcpy(¶ms, input.data(), sizeof(IoctlGetWaitbase)); @@ -142,7 +141,7 @@ NvResult nvhost_nvdec_common::GetWaitbase(std::span<const u8> input, std::vector return NvResult::Success; } -NvResult nvhost_nvdec_common::MapBuffer(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_nvdec_common::MapBuffer(std::span<const u8> input, std::span<u8> output) { IoctlMapBuffer params{}; std::memcpy(¶ms, input.data(), sizeof(IoctlMapBuffer)); std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries); @@ -159,7 +158,7 @@ NvResult nvhost_nvdec_common::MapBuffer(std::span<const u8> input, std::vector<u return NvResult::Success; } -NvResult nvhost_nvdec_common::UnmapBuffer(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_nvdec_common::UnmapBuffer(std::span<const u8> input, std::span<u8> output) { IoctlMapBuffer params{}; std::memcpy(¶ms, input.data(), sizeof(IoctlMapBuffer)); std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries); @@ -173,7 +172,7 @@ NvResult nvhost_nvdec_common::UnmapBuffer(std::span<const u8> input, std::vector return NvResult::Success; } -NvResult nvhost_nvdec_common::SetSubmitTimeout(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_nvdec_common::SetSubmitTimeout(std::span<const u8> input, std::span<u8> output) { std::memcpy(&submit_timeout, input.data(), input.size()); LOG_WARNING(Service_NVDRV, "(STUBBED) called"); return NvResult::Success; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h index 5af26a26fa..9bb573bfee 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h @@ -108,12 +108,12 @@ protected: /// Ioctl command implementations NvResult SetNVMAPfd(std::span<const u8> input); - NvResult Submit(DeviceFD fd, std::span<const u8> input, std::vector<u8>& output); - NvResult GetSyncpoint(std::span<const u8> input, std::vector<u8>& output); - NvResult GetWaitbase(std::span<const u8> input, std::vector<u8>& output); - NvResult MapBuffer(std::span<const u8> input, std::vector<u8>& output); - NvResult UnmapBuffer(std::span<const u8> input, std::vector<u8>& output); - NvResult SetSubmitTimeout(std::span<const u8> input, std::vector<u8>& output); + NvResult Submit(DeviceFD fd, std::span<const u8> input, std::span<u8> output); + NvResult GetSyncpoint(std::span<const u8> input, std::span<u8> output); + NvResult GetWaitbase(std::span<const u8> input, std::span<u8> output); + NvResult MapBuffer(std::span<const u8> input, std::span<u8> output); + NvResult UnmapBuffer(std::span<const u8> input, std::span<u8> output); + NvResult SetSubmitTimeout(std::span<const u8> input, std::span<u8> output); Kernel::KEvent* QueryEvent(u32 event_id) override; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp index 39f30e7c86..a05c8cdaef 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp @@ -13,7 +13,7 @@ nvhost_nvjpg::nvhost_nvjpg(Core::System& system_) : nvdevice{system_} {} nvhost_nvjpg::~nvhost_nvjpg() = default; NvResult nvhost_nvjpg::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::vector<u8>& output) { + std::span<u8> output) { switch (command.group) { case 'H': switch (command.cmd) { @@ -32,13 +32,13 @@ NvResult nvhost_nvjpg::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> in } NvResult nvhost_nvjpg::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::span<const u8> inline_input, std::vector<u8>& output) { + std::span<const u8> inline_input, std::span<u8> output) { UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); return NvResult::NotImplemented; } NvResult nvhost_nvjpg::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::vector<u8>& output, std::vector<u8>& inline_output) { + std::span<u8> output, std::span<u8> inline_output) { UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); return NvResult::NotImplemented; } @@ -46,7 +46,7 @@ NvResult nvhost_nvjpg::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> in void nvhost_nvjpg::OnOpen(DeviceFD fd) {} void nvhost_nvjpg::OnClose(DeviceFD fd) {} -NvResult nvhost_nvjpg::SetNVMAPfd(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_nvjpg::SetNVMAPfd(std::span<const u8> input, std::span<u8> output) { IoctlSetNvmapFD params{}; std::memcpy(¶ms, input.data(), input.size()); LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h index 41b57e872d..5623e0d473 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h @@ -16,11 +16,11 @@ public: ~nvhost_nvjpg() override; NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::vector<u8>& output) override; + std::span<u8> output) override; NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::span<const u8> inline_input, std::vector<u8>& output) override; - NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output, - std::vector<u8>& inline_output) override; + std::span<const u8> inline_input, std::span<u8> output) override; + NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, + std::span<u8> inline_output) override; void OnOpen(DeviceFD fd) override; void OnClose(DeviceFD fd) override; @@ -33,7 +33,7 @@ private: s32_le nvmap_fd{}; - NvResult SetNVMAPfd(std::span<const u8> input, std::vector<u8>& output); + NvResult SetNVMAPfd(std::span<const u8> input, std::span<u8> output); }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp index b0ea402a72..c0b8684c38 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp @@ -16,7 +16,7 @@ nvhost_vic::nvhost_vic(Core::System& system_, NvCore::Container& core_) nvhost_vic::~nvhost_vic() = default; NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::vector<u8>& output) { + std::span<u8> output) { switch (command.group) { case 0x0: switch (command.cmd) { @@ -56,13 +56,13 @@ NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inpu } NvResult nvhost_vic::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::span<const u8> inline_input, std::vector<u8>& output) { + std::span<const u8> inline_input, std::span<u8> output) { UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); return NvResult::NotImplemented; } NvResult nvhost_vic::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::vector<u8>& output, std::vector<u8>& inline_output) { + std::span<u8> output, std::span<u8> inline_output) { UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); return NvResult::NotImplemented; } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h index b5e350a83d..cadbcb0a5c 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h @@ -13,11 +13,11 @@ public: ~nvhost_vic(); NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::vector<u8>& output) override; + std::span<u8> output) override; NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::span<const u8> inline_input, std::vector<u8>& output) override; - NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output, - std::vector<u8>& inline_output) override; + std::span<const u8> inline_input, std::span<u8> output) override; + NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, + std::span<u8> inline_output) override; void OnOpen(DeviceFD fd) override; void OnClose(DeviceFD fd) override; diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp index 07417f0455..e7f7e273b5 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.cpp +++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp @@ -26,7 +26,7 @@ nvmap::nvmap(Core::System& system_, NvCore::Container& container_) nvmap::~nvmap() = default; NvResult nvmap::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::vector<u8>& output) { + std::span<u8> output) { switch (command.group) { case 0x1: switch (command.cmd) { @@ -55,13 +55,13 @@ NvResult nvmap::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, } NvResult nvmap::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::span<const u8> inline_input, std::vector<u8>& output) { + std::span<const u8> inline_input, std::span<u8> output) { UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); return NvResult::NotImplemented; } -NvResult nvmap::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::vector<u8>& output, std::vector<u8>& inline_output) { +NvResult nvmap::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, + std::span<u8> inline_output) { UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); return NvResult::NotImplemented; } @@ -69,7 +69,7 @@ NvResult nvmap::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, void nvmap::OnOpen(DeviceFD fd) {} void nvmap::OnClose(DeviceFD fd) {} -NvResult nvmap::IocCreate(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvmap::IocCreate(std::span<const u8> input, std::span<u8> output) { IocCreateParams params; std::memcpy(¶ms, input.data(), sizeof(params)); LOG_DEBUG(Service_NVDRV, "called, size=0x{:08X}", params.size); @@ -89,7 +89,7 @@ NvResult nvmap::IocCreate(std::span<const u8> input, std::vector<u8>& output) { return NvResult::Success; } -NvResult nvmap::IocAlloc(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvmap::IocAlloc(std::span<const u8> input, std::span<u8> output) { IocAllocParams params; std::memcpy(¶ms, input.data(), sizeof(params)); LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.address); @@ -137,7 +137,7 @@ NvResult nvmap::IocAlloc(std::span<const u8> input, std::vector<u8>& output) { return result; } -NvResult nvmap::IocGetId(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvmap::IocGetId(std::span<const u8> input, std::span<u8> output) { IocGetIdParams params; std::memcpy(¶ms, input.data(), sizeof(params)); @@ -161,7 +161,7 @@ NvResult nvmap::IocGetId(std::span<const u8> input, std::vector<u8>& output) { return NvResult::Success; } -NvResult nvmap::IocFromId(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvmap::IocFromId(std::span<const u8> input, std::span<u8> output) { IocFromIdParams params; std::memcpy(¶ms, input.data(), sizeof(params)); @@ -192,7 +192,7 @@ NvResult nvmap::IocFromId(std::span<const u8> input, std::vector<u8>& output) { return NvResult::Success; } -NvResult nvmap::IocParam(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvmap::IocParam(std::span<const u8> input, std::span<u8> output) { enum class ParamTypes { Size = 1, Alignment = 2, Base = 3, Heap = 4, Kind = 5, Compr = 6 }; IocParamParams params; @@ -241,7 +241,7 @@ NvResult nvmap::IocParam(std::span<const u8> input, std::vector<u8>& output) { return NvResult::Success; } -NvResult nvmap::IocFree(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvmap::IocFree(std::span<const u8> input, std::span<u8> output) { IocFreeParams params; std::memcpy(¶ms, input.data(), sizeof(params)); diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h index 82bd3b118f..40c65b430b 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.h +++ b/src/core/hle/service/nvdrv/devices/nvmap.h @@ -27,11 +27,11 @@ public: nvmap& operator=(const nvmap&) = delete; NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::vector<u8>& output) override; + std::span<u8> output) override; NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::span<const u8> inline_input, std::vector<u8>& output) override; - NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output, - std::vector<u8>& inline_output) override; + std::span<const u8> inline_input, std::span<u8> output) override; + NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, + std::span<u8> inline_output) override; void OnOpen(DeviceFD fd) override; void OnClose(DeviceFD fd) override; @@ -106,12 +106,12 @@ private: }; static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size"); - NvResult IocCreate(std::span<const u8> input, std::vector<u8>& output); - NvResult IocAlloc(std::span<const u8> input, std::vector<u8>& output); - NvResult IocGetId(std::span<const u8> input, std::vector<u8>& output); - NvResult IocFromId(std::span<const u8> input, std::vector<u8>& output); - NvResult IocParam(std::span<const u8> input, std::vector<u8>& output); - NvResult IocFree(std::span<const u8> input, std::vector<u8>& output); + NvResult IocCreate(std::span<const u8> input, std::span<u8> output); + NvResult IocAlloc(std::span<const u8> input, std::span<u8> output); + NvResult IocGetId(std::span<const u8> input, std::span<u8> output); + NvResult IocFromId(std::span<const u8> input, std::span<u8> output); + NvResult IocParam(std::span<const u8> input, std::span<u8> output); + NvResult IocFree(std::span<const u8> input, std::span<u8> output); NvCore::Container& container; NvCore::NvMap& file; diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp index 3d774eec49..9e46ee8dd2 100644 --- a/src/core/hle/service/nvdrv/nvdrv.cpp +++ b/src/core/hle/service/nvdrv/nvdrv.cpp @@ -130,7 +130,7 @@ DeviceFD Module::Open(const std::string& device_name) { } NvResult Module::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::vector<u8>& output) { + std::span<u8> output) { if (fd < 0) { LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd); return NvResult::InvalidState; @@ -147,7 +147,7 @@ NvResult Module::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, } NvResult Module::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::span<const u8> inline_input, std::vector<u8>& output) { + std::span<const u8> inline_input, std::span<u8> output) { if (fd < 0) { LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd); return NvResult::InvalidState; @@ -163,8 +163,8 @@ NvResult Module::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, return itr->second->Ioctl2(fd, command, input, inline_input, output); } -NvResult Module::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::vector<u8>& output, std::vector<u8>& inline_output) { +NvResult Module::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, + std::span<u8> inline_output) { if (fd < 0) { LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd); return NvResult::InvalidState; diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h index 668be742b1..d8622b3ca4 100644 --- a/src/core/hle/service/nvdrv/nvdrv.h +++ b/src/core/hle/service/nvdrv/nvdrv.h @@ -80,13 +80,13 @@ public: DeviceFD Open(const std::string& device_name); /// Sends an ioctl command to the specified file descriptor. - NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output); + NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output); NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::span<const u8> inline_input, std::vector<u8>& output); + std::span<const u8> inline_input, std::span<u8> output); - NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output, - std::vector<u8>& inline_output); + NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, + std::span<u8> inline_output); /// Closes a device file descriptor and returns operation success. NvResult Close(DeviceFD fd); diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.cpp b/src/core/hle/service/nvdrv/nvdrv_interface.cpp index d010a1e03b..348207e250 100644 --- a/src/core/hle/service/nvdrv/nvdrv_interface.cpp +++ b/src/core/hle/service/nvdrv/nvdrv_interface.cpp @@ -63,12 +63,12 @@ void NVDRV::Ioctl1(HLERequestContext& ctx) { } // Check device - std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0)); + tmp_output.resize_destructive(ctx.GetWriteBufferSize(0)); const auto input_buffer = ctx.ReadBuffer(0); - const auto nv_result = nvdrv->Ioctl1(fd, command, input_buffer, output_buffer); + const auto nv_result = nvdrv->Ioctl1(fd, command, input_buffer, tmp_output); if (command.is_out != 0) { - ctx.WriteBuffer(output_buffer); + ctx.WriteBuffer(tmp_output); } IPC::ResponseBuilder rb{ctx, 3}; @@ -90,12 +90,12 @@ void NVDRV::Ioctl2(HLERequestContext& ctx) { const auto input_buffer = ctx.ReadBuffer(0); const auto input_inlined_buffer = ctx.ReadBuffer(1); - std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0)); + tmp_output.resize_destructive(ctx.GetWriteBufferSize(0)); const auto nv_result = - nvdrv->Ioctl2(fd, command, input_buffer, input_inlined_buffer, output_buffer); + nvdrv->Ioctl2(fd, command, input_buffer, input_inlined_buffer, tmp_output); if (command.is_out != 0) { - ctx.WriteBuffer(output_buffer); + ctx.WriteBuffer(tmp_output); } IPC::ResponseBuilder rb{ctx, 3}; @@ -116,14 +116,12 @@ void NVDRV::Ioctl3(HLERequestContext& ctx) { } const auto input_buffer = ctx.ReadBuffer(0); - std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0)); - std::vector<u8> output_buffer_inline(ctx.GetWriteBufferSize(1)); - - const auto nv_result = - nvdrv->Ioctl3(fd, command, input_buffer, output_buffer, output_buffer_inline); + tmp_output.resize_destructive(ctx.GetWriteBufferSize(0)); + tmp_output_inline.resize_destructive(ctx.GetWriteBufferSize(1)); + const auto nv_result = nvdrv->Ioctl3(fd, command, input_buffer, tmp_output, tmp_output_inline); if (command.is_out != 0) { - ctx.WriteBuffer(output_buffer, 0); - ctx.WriteBuffer(output_buffer_inline, 1); + ctx.WriteBuffer(tmp_output, 0); + ctx.WriteBuffer(tmp_output_inline, 1); } IPC::ResponseBuilder rb{ctx, 3}; diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.h b/src/core/hle/service/nvdrv/nvdrv_interface.h index 881ea1a6bf..4b593ff90d 100644 --- a/src/core/hle/service/nvdrv/nvdrv_interface.h +++ b/src/core/hle/service/nvdrv/nvdrv_interface.h @@ -4,6 +4,7 @@ #pragma once #include <memory> +#include "common/scratch_buffer.h" #include "core/hle/service/nvdrv/nvdrv.h" #include "core/hle/service/service.h" @@ -33,6 +34,8 @@ private: u64 pid{}; bool is_initialized{}; + Common::ScratchBuffer<u8> tmp_output; + Common::ScratchBuffer<u8> tmp_output_inline; }; } // namespace Service::Nvidia diff --git a/src/core/hle/service/nvnflinger/nvnflinger.cpp b/src/core/hle/service/nvnflinger/nvnflinger.cpp index da2d5890f7..5f55cd31e0 100644 --- a/src/core/hle/service/nvnflinger/nvnflinger.cpp +++ b/src/core/hle/service/nvnflinger/nvnflinger.cpp @@ -43,14 +43,10 @@ void Nvnflinger::SplitVSync(std::stop_token stop_token) { Common::SetCurrentThreadPriority(Common::ThreadPriority::High); while (!stop_token.stop_requested()) { - vsync_signal.wait(false); - vsync_signal.store(false); - - guard->lock(); + vsync_signal.Wait(); + const auto lock_guard = Lock(); Compose(); - - guard->unlock(); } } @@ -69,8 +65,8 @@ Nvnflinger::Nvnflinger(Core::System& system_, HosBinderDriverServer& hos_binder_ "ScreenComposition", [this](std::uintptr_t, s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { - vsync_signal.store(true); - vsync_signal.notify_all(); + { const auto lock_guard = Lock(); } + vsync_signal.Set(); return std::chrono::nanoseconds(GetNextTicks()); }); @@ -96,8 +92,7 @@ Nvnflinger::~Nvnflinger() { if (system.IsMulticore()) { system.CoreTiming().UnscheduleEvent(multi_composition_event, {}); vsync_thread.request_stop(); - vsync_signal.store(true); - vsync_signal.notify_all(); + vsync_signal.Set(); } else { system.CoreTiming().UnscheduleEvent(single_composition_event, {}); } diff --git a/src/core/hle/service/nvnflinger/nvnflinger.h b/src/core/hle/service/nvnflinger/nvnflinger.h index a043cceb2d..ef236303ab 100644 --- a/src/core/hle/service/nvnflinger/nvnflinger.h +++ b/src/core/hle/service/nvnflinger/nvnflinger.h @@ -12,6 +12,7 @@ #include "common/common_types.h" #include "common/polyfill_thread.h" +#include "common/thread.h" #include "core/hle/result.h" #include "core/hle/service/kernel_helpers.h" @@ -143,7 +144,7 @@ private: Core::System& system; - std::atomic<bool> vsync_signal; + Common::Event vsync_signal; std::jthread vsync_thread; diff --git a/src/core/hle/service/nvnflinger/parcel.h b/src/core/hle/service/nvnflinger/parcel.h index fb56d75d76..23ba315a05 100644 --- a/src/core/hle/service/nvnflinger/parcel.h +++ b/src/core/hle/service/nvnflinger/parcel.h @@ -6,6 +6,7 @@ #include <memory> #include <span> #include <vector> +#include <boost/container/small_vector.hpp> #include "common/alignment.h" #include "common/assert.h" @@ -167,7 +168,7 @@ public: private: template <typename T> requires(std::is_trivially_copyable_v<T>) - void WriteImpl(const T& val, std::vector<u8>& buffer) { + void WriteImpl(const T& val, boost::container::small_vector<u8, 0x200>& buffer) { const size_t aligned_size = Common::AlignUp(sizeof(T), 4); const size_t old_size = buffer.size(); buffer.resize(old_size + aligned_size); @@ -176,8 +177,8 @@ private: } private: - std::vector<u8> m_data_buffer; - std::vector<u8> m_object_buffer; + boost::container::small_vector<u8, 0x200> m_data_buffer; + boost::container::small_vector<u8, 0x200> m_object_buffer; }; } // namespace Service::android diff --git a/src/core/hle/service/server_manager.cpp b/src/core/hle/service/server_manager.cpp index 156bc27d8b..d1e99b1843 100644 --- a/src/core/hle/service/server_manager.cpp +++ b/src/core/hle/service/server_manager.cpp @@ -44,7 +44,7 @@ ServerManager::~ServerManager() { m_event->Signal(); // Wait for processing to stop. - m_stopped.wait(false); + m_stopped.Wait(); m_threads.clear(); // Clean up ports. @@ -182,10 +182,7 @@ void ServerManager::StartAdditionalHostThreads(const char* name, size_t num_thre } Result ServerManager::LoopProcess() { - SCOPE_EXIT({ - m_stopped.store(true); - m_stopped.notify_all(); - }); + SCOPE_EXIT({ m_stopped.Set(); }); R_RETURN(this->LoopProcessImpl()); } diff --git a/src/core/hle/service/server_manager.h b/src/core/hle/service/server_manager.h index fdb8af2ff1..58b0a08320 100644 --- a/src/core/hle/service/server_manager.h +++ b/src/core/hle/service/server_manager.h @@ -3,7 +3,6 @@ #pragma once -#include <atomic> #include <functional> #include <list> #include <map> @@ -12,6 +11,7 @@ #include <vector> #include "common/polyfill_thread.h" +#include "common/thread.h" #include "core/hle/result.h" #include "core/hle/service/mutex.h" @@ -82,7 +82,7 @@ private: std::list<RequestState> m_deferrals{}; // Host state tracking - std::atomic<bool> m_stopped{}; + Common::Event m_stopped{}; std::vector<std::jthread> m_threads{}; std::stop_source m_stop_source{}; }; diff --git a/src/core/hle/service/time/clock_types.h b/src/core/hle/service/time/clock_types.h index e6293ffb9b..9fc01ea907 100644 --- a/src/core/hle/service/time/clock_types.h +++ b/src/core/hle/service/time/clock_types.h @@ -3,6 +3,8 @@ #pragma once +#include <ratio> + #include "common/common_funcs.h" #include "common/common_types.h" #include "common/uuid.h" @@ -74,18 +76,19 @@ static_assert(std::is_trivially_copyable_v<ContinuousAdjustmentTimePoint>, /// https://switchbrew.org/wiki/Glue_services#TimeSpanType struct TimeSpanType { s64 nanoseconds{}; - static constexpr s64 ns_per_second{1000000000ULL}; s64 ToSeconds() const { - return nanoseconds / ns_per_second; + return nanoseconds / std::nano::den; } static TimeSpanType FromSeconds(s64 seconds) { - return {seconds * ns_per_second}; + return {seconds * std::nano::den}; } - static TimeSpanType FromTicks(u64 ticks, u64 frequency) { - return FromSeconds(static_cast<s64>(ticks) / static_cast<s64>(frequency)); + template <u64 Frequency> + static TimeSpanType FromTicks(u64 ticks) { + using TicksToNSRatio = std::ratio<std::nano::den, Frequency>; + return {static_cast<s64>(ticks * TicksToNSRatio::num / TicksToNSRatio::den)}; } }; static_assert(sizeof(TimeSpanType) == 8, "TimeSpanType is incorrect size"); diff --git a/src/core/hle/service/time/standard_steady_clock_core.cpp b/src/core/hle/service/time/standard_steady_clock_core.cpp index 3dbbb98503..5627b70036 100644 --- a/src/core/hle/service/time/standard_steady_clock_core.cpp +++ b/src/core/hle/service/time/standard_steady_clock_core.cpp @@ -10,7 +10,7 @@ namespace Service::Time::Clock { TimeSpanType StandardSteadyClockCore::GetCurrentRawTimePoint(Core::System& system) { const TimeSpanType ticks_time_span{ - TimeSpanType::FromTicks(system.CoreTiming().GetClockTicks(), Core::Hardware::CNTFREQ)}; + TimeSpanType::FromTicks<Core::Hardware::CNTFREQ>(system.CoreTiming().GetClockTicks())}; TimeSpanType raw_time_point{setup_value.nanoseconds + ticks_time_span.nanoseconds}; if (raw_time_point.nanoseconds < cached_raw_time_point.nanoseconds) { diff --git a/src/core/hle/service/time/tick_based_steady_clock_core.cpp b/src/core/hle/service/time/tick_based_steady_clock_core.cpp index 27600413ee..0d9fb31433 100644 --- a/src/core/hle/service/time/tick_based_steady_clock_core.cpp +++ b/src/core/hle/service/time/tick_based_steady_clock_core.cpp @@ -10,7 +10,7 @@ namespace Service::Time::Clock { SteadyClockTimePoint TickBasedSteadyClockCore::GetTimePoint(Core::System& system) { const TimeSpanType ticks_time_span{ - TimeSpanType::FromTicks(system.CoreTiming().GetClockTicks(), Core::Hardware::CNTFREQ)}; + TimeSpanType::FromTicks<Core::Hardware::CNTFREQ>(system.CoreTiming().GetClockTicks())}; return {ticks_time_span.ToSeconds(), GetClockSourceId()}; } diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp index 868be60c59..7197ca30fc 100644 --- a/src/core/hle/service/time/time.cpp +++ b/src/core/hle/service/time/time.cpp @@ -240,8 +240,8 @@ void Module::Interface::CalculateMonotonicSystemClockBaseTimePoint(HLERequestCon const auto current_time_point{steady_clock_core.GetCurrentTimePoint(system)}; if (current_time_point.clock_source_id == context.steady_time_point.clock_source_id) { - const auto ticks{Clock::TimeSpanType::FromTicks(system.CoreTiming().GetClockTicks(), - Core::Hardware::CNTFREQ)}; + const auto ticks{Clock::TimeSpanType::FromTicks<Core::Hardware::CNTFREQ>( + system.CoreTiming().GetClockTicks())}; const s64 base_time_point{context.offset + current_time_point.time_point - ticks.ToSeconds()}; IPC::ResponseBuilder rb{ctx, (sizeof(s64) / 4) + 2}; diff --git a/src/core/hle/service/time/time_manager.cpp b/src/core/hle/service/time/time_manager.cpp index 28667710e9..fa0fd05316 100644 --- a/src/core/hle/service/time/time_manager.cpp +++ b/src/core/hle/service/time/time_manager.cpp @@ -22,10 +22,6 @@ s64 GetSecondsSinceEpoch() { return std::chrono::duration_cast<std::chrono::seconds>(time_since_epoch).count() + Settings::values.custom_rtc_differential; } - -s64 GetExternalRtcValue() { - return GetSecondsSinceEpoch() + TimeManager::GetExternalTimeZoneOffset(); -} } // Anonymous namespace struct TimeManager::Impl final { @@ -43,7 +39,7 @@ struct TimeManager::Impl final { std::make_shared<Clock::EphemeralNetworkSystemClockContextWriter>()}, time_zone_content_manager{system} { - const auto system_time{Clock::TimeSpanType::FromSeconds(GetExternalRtcValue())}; + const auto system_time{Clock::TimeSpanType::FromSeconds(GetSecondsSinceEpoch())}; SetupStandardSteadyClock(system, Common::UUID::MakeRandom(), system_time, {}, {}); SetupStandardLocalSystemClock(system, {}, system_time.ToSeconds()); @@ -107,7 +103,7 @@ struct TimeManager::Impl final { void SetupTimeZoneManager(std::string location_name, Clock::SteadyClockTimePoint time_zone_updated_time_point, - std::size_t total_location_name_count, u128 time_zone_rule_version, + std::vector<std::string> location_names, u128 time_zone_rule_version, FileSys::VirtualFile& vfs_file) { if (time_zone_content_manager.GetTimeZoneManager().SetDeviceLocationNameWithTimeZoneRule( location_name, vfs_file) != ResultSuccess) { @@ -117,20 +113,13 @@ struct TimeManager::Impl final { time_zone_content_manager.GetTimeZoneManager().SetUpdatedTime(time_zone_updated_time_point); time_zone_content_manager.GetTimeZoneManager().SetTotalLocationNameCount( - total_location_name_count); + location_names.size()); + time_zone_content_manager.GetTimeZoneManager().SetLocationNames(location_names); time_zone_content_manager.GetTimeZoneManager().SetTimeZoneRuleVersion( time_zone_rule_version); time_zone_content_manager.GetTimeZoneManager().MarkAsInitialized(); } - static s64 GetExternalTimeZoneOffset() { - // With "auto" timezone setting, we use the external system's timezone offset - if (Settings::GetTimeZoneString() == "auto") { - return Common::TimeZone::GetCurrentOffsetSeconds().count(); - } - return 0; - } - void SetupStandardSteadyClock(Core::System& system_, Common::UUID clock_source_id, Clock::TimeSpanType setup_value, Clock::TimeSpanType internal_offset, bool is_rtc_reset_detected) { @@ -295,19 +284,10 @@ void TimeManager::UpdateLocalSystemClockTime(s64 posix_time) { void TimeManager::SetupTimeZoneManager(std::string location_name, Clock::SteadyClockTimePoint time_zone_updated_time_point, - std::size_t total_location_name_count, + std::vector<std::string> location_names, u128 time_zone_rule_version, FileSys::VirtualFile& vfs_file) { - impl->SetupTimeZoneManager(location_name, time_zone_updated_time_point, - total_location_name_count, time_zone_rule_version, vfs_file); + impl->SetupTimeZoneManager(location_name, time_zone_updated_time_point, location_names, + time_zone_rule_version, vfs_file); } - -/*static*/ s64 TimeManager::GetExternalTimeZoneOffset() { - // With "auto" timezone setting, we use the external system's timezone offset - if (Settings::GetTimeZoneString() == "auto") { - return Common::TimeZone::GetCurrentOffsetSeconds().count(); - } - return 0; -} - } // namespace Service::Time diff --git a/src/core/hle/service/time/time_manager.h b/src/core/hle/service/time/time_manager.h index 4f046f2662..84572dbfac 100644 --- a/src/core/hle/service/time/time_manager.h +++ b/src/core/hle/service/time/time_manager.h @@ -61,11 +61,9 @@ public: void SetupTimeZoneManager(std::string location_name, Clock::SteadyClockTimePoint time_zone_updated_time_point, - std::size_t total_location_name_count, u128 time_zone_rule_version, + std::vector<std::string> location_names, u128 time_zone_rule_version, FileSys::VirtualFile& vfs_file); - static s64 GetExternalTimeZoneOffset(); - private: Core::System& system; diff --git a/src/core/hle/service/time/time_sharedmemory.cpp b/src/core/hle/service/time/time_sharedmemory.cpp index ce1c85bcc5..a00676669b 100644 --- a/src/core/hle/service/time/time_sharedmemory.cpp +++ b/src/core/hle/service/time/time_sharedmemory.cpp @@ -21,8 +21,9 @@ SharedMemory::~SharedMemory() = default; void SharedMemory::SetupStandardSteadyClock(const Common::UUID& clock_source_id, Clock::TimeSpanType current_time_point) { - const Clock::TimeSpanType ticks_time_span{Clock::TimeSpanType::FromTicks( - system.CoreTiming().GetClockTicks(), Core::Hardware::CNTFREQ)}; + const Clock::TimeSpanType ticks_time_span{ + Clock::TimeSpanType::FromTicks<Core::Hardware::CNTFREQ>( + system.CoreTiming().GetClockTicks())}; const Clock::SteadyClockContext context{ static_cast<u64>(current_time_point.nanoseconds - ticks_time_span.nanoseconds), clock_source_id}; diff --git a/src/core/hle/service/time/time_zone_content_manager.cpp b/src/core/hle/service/time/time_zone_content_manager.cpp index afbfe97153..5d60be67a3 100644 --- a/src/core/hle/service/time/time_zone_content_manager.cpp +++ b/src/core/hle/service/time/time_zone_content_manager.cpp @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include <chrono> #include <sstream> #include "common/logging/log.h" @@ -12,7 +13,11 @@ #include "core/file_sys/registered_cache.h" #include "core/file_sys/romfs.h" #include "core/file_sys/system_archive/system_archive.h" +#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs_types.h" +#include "core/hle/result.h" #include "core/hle/service/filesystem/filesystem.h" +#include "core/hle/service/time/errors.h" #include "core/hle/service/time/time_manager.h" #include "core/hle/service/time/time_zone_content_manager.h" @@ -71,19 +76,13 @@ TimeZoneContentManager::TimeZoneContentManager(Core::System& system_) : system{system_}, location_name_cache{BuildLocationNameCache(system)} {} void TimeZoneContentManager::Initialize(TimeManager& time_manager) { - std::string location_name; const auto timezone_setting = Settings::GetTimeZoneString(); - if (timezone_setting == "auto" || timezone_setting == "default") { - location_name = Common::TimeZone::GetDefaultTimeZone(); - } else { - location_name = timezone_setting; - } if (FileSys::VirtualFile vfs_file; - GetTimeZoneInfoFile(location_name, vfs_file) == ResultSuccess) { + GetTimeZoneInfoFile(timezone_setting, vfs_file) == ResultSuccess) { const auto time_point{ time_manager.GetStandardSteadyClockCore().GetCurrentTimePoint(system)}; - time_manager.SetupTimeZoneManager(location_name, time_point, location_name_cache.size(), {}, + time_manager.SetupTimeZoneManager(timezone_setting, time_point, location_name_cache, {}, vfs_file); } else { time_zone_manager.MarkAsInitialized(); @@ -126,8 +125,15 @@ Result TimeZoneContentManager::GetTimeZoneInfoFile(const std::string& location_n vfs_file = zoneinfo_dir->GetFileRelative(location_name); if (!vfs_file) { - LOG_ERROR(Service_Time, "{:016X} has no file \"{}\"! Using default timezone.", - time_zone_binary_titleid, location_name); + LOG_WARNING(Service_Time, "{:016X} has no file \"{}\"! Using system timezone.", + time_zone_binary_titleid, location_name); + const std::string system_time_zone{Common::TimeZone::FindSystemTimeZone()}; + vfs_file = zoneinfo_dir->GetFile(system_time_zone); + } + + if (!vfs_file) { + LOG_WARNING(Service_Time, "{:016X} has no file \"{}\"! Using default timezone.", + time_zone_binary_titleid, location_name); vfs_file = zoneinfo_dir->GetFile(Common::TimeZone::GetDefaultTimeZone()); } diff --git a/src/core/hle/service/time/time_zone_manager.cpp b/src/core/hle/service/time/time_zone_manager.cpp index 973f7837ae..205371a263 100644 --- a/src/core/hle/service/time/time_zone_manager.cpp +++ b/src/core/hle/service/time/time_zone_manager.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include <climits> +#include <limits> #include "common/assert.h" #include "common/logging/log.h" @@ -9,6 +10,7 @@ #include "core/file_sys/nca_metadata.h" #include "core/file_sys/registered_cache.h" #include "core/hle/service/time/time_zone_manager.h" +#include "core/hle/service/time/time_zone_types.h" namespace Service::Time::TimeZone { @@ -128,10 +130,10 @@ static constexpr int GetQZName(const char* name, int offset, char delimiter) { } static constexpr int GetTZName(const char* name, int offset) { - for (char value{name[offset]}; - value != '\0' && !IsDigit(value) && value != ',' && value != '-' && value != '+'; - offset++) { - value = name[offset]; + char c; + + while ((c = name[offset]) != '\0' && !IsDigit(c) && c != ',' && c != '-' && c != '+') { + ++offset; } return offset; } @@ -147,6 +149,7 @@ static constexpr bool GetInteger(const char* name, int& offset, int& value, int if (value > max) { return {}; } + offset++; temp = name[offset]; } while (IsDigit(temp)); @@ -471,6 +474,13 @@ static bool ParsePosixName(const char* name, TimeZoneRule& rule) { their_std_offset = their_offset; } } + + if (rule.time_count > 0) { + UNIMPLEMENTED(); + // TODO (lat9nq): Implement eggert/tz/localtime.c:tzparse:1329 + // Seems to be unused in yuzu for now: I never hit the UNIMPLEMENTED in testing + } + rule.ttis[0].gmt_offset = -std_offset; rule.ttis[0].is_dst = false; rule.ttis[0].abbreviation_list_index = 0; @@ -514,6 +524,7 @@ static bool ParseTimeZoneBinary(TimeZoneRule& time_zone_rule, FileSys::VirtualFi constexpr s32 time_zone_max_leaps{50}; constexpr s32 time_zone_max_chars{50}; + constexpr s32 time_zone_max_times{1000}; if (!(0 <= header.leap_count && header.leap_count < time_zone_max_leaps && 0 < header.type_count && header.type_count < s32(time_zone_rule.ttis.size()) && 0 <= header.time_count && header.time_count < s32(time_zone_rule.ats.size()) && @@ -546,7 +557,7 @@ static bool ParseTimeZoneBinary(TimeZoneRule& time_zone_rule, FileSys::VirtualFi for (int index{}; index < time_zone_rule.time_count; ++index) { const u8 type{*vfs_file->ReadByte(read_offset)}; read_offset += sizeof(u8); - if (time_zone_rule.time_count <= type) { + if (time_zone_rule.type_count <= type) { return {}; } if (time_zone_rule.types[index] != 0) { @@ -624,16 +635,109 @@ static bool ParseTimeZoneBinary(TimeZoneRule& time_zone_rule, FileSys::VirtualFi std::array<char, time_zone_name_max> name{}; std::memcpy(name.data(), temp_name.data() + 1, std::size_t(bytes_read - 1)); + // Fill in computed transition times with temp rule TimeZoneRule temp_rule; if (ParsePosixName(name.data(), temp_rule)) { - UNIMPLEMENTED(); + int have_abbreviation = 0; + int char_count = time_zone_rule.char_count; + + for (int i = 0; i < temp_rule.type_count; i++) { + char* temp_abbreviation = + temp_rule.chars.data() + temp_rule.ttis[i].abbreviation_list_index; + int j; + for (j = 0; j < char_count; j++) { + if (std::strcmp(time_zone_rule.chars.data() + j, temp_abbreviation) == 0) { + temp_rule.ttis[i].abbreviation_list_index = j; + have_abbreviation++; + break; + } + } + if (j >= char_count) { + int temp_abbreviation_length = static_cast<int>(std::strlen(temp_abbreviation)); + if (j + temp_abbreviation_length < time_zone_max_chars) { + std::strcpy(time_zone_rule.chars.data() + j, temp_abbreviation); + char_count = j + temp_abbreviation_length + 1; + temp_rule.ttis[i].abbreviation_list_index = j; + have_abbreviation++; + } + } + } + + if (have_abbreviation == temp_rule.type_count) { + time_zone_rule.char_count = char_count; + + // Original comment: + /* Ignore any trailing, no-op transitions generated + by zic as they don't help here and can run afoul + of bugs in zic 2016j or earlier. */ + // This is possibly unnecessary for yuzu, since Nintendo doesn't run zic + while (1 < time_zone_rule.time_count && + (time_zone_rule.types[time_zone_rule.time_count - 1] == + time_zone_rule.types[time_zone_rule.time_count - 2])) { + time_zone_rule.time_count--; + } + + for (int i = 0; + i < temp_rule.time_count && time_zone_rule.time_count < time_zone_max_times; + i++) { + const s64 transition_time = temp_rule.ats[i]; + if (0 < time_zone_rule.time_count && + transition_time <= time_zone_rule.ats[time_zone_rule.time_count - 1]) { + continue; + } + + time_zone_rule.ats[time_zone_rule.time_count] = transition_time; + time_zone_rule.types[time_zone_rule.time_count] = + static_cast<s8>(time_zone_rule.type_count + temp_rule.types[i]); + time_zone_rule.time_count++; + } + for (int i = 0; i < temp_rule.type_count; i++) { + time_zone_rule.ttis[time_zone_rule.type_count++] = temp_rule.ttis[i]; + } + } } } + + const auto typesequiv = [](TimeZoneRule& rule, int a, int b) -> bool { + if (a < 0 || a >= rule.type_count || b < 0 || b >= rule.type_count) { + return {}; + } + + const struct TimeTypeInfo* ap = &rule.ttis[a]; + const struct TimeTypeInfo* bp = &rule.ttis[b]; + + return (ap->gmt_offset == bp->gmt_offset && ap->is_dst == bp->is_dst && + (std::strcmp(&rule.chars[ap->abbreviation_list_index], + &rule.chars[bp->abbreviation_list_index]) == 0)); + }; + if (time_zone_rule.type_count == 0) { return {}; } if (time_zone_rule.time_count > 1) { - UNIMPLEMENTED(); + if (time_zone_rule.ats[0] <= std::numeric_limits<s64>::max() - seconds_per_repeat) { + s64 repeatat = time_zone_rule.ats[0] + seconds_per_repeat; + int repeatattype = time_zone_rule.types[0]; + for (int i = 1; i < time_zone_rule.time_count; ++i) { + if (time_zone_rule.ats[i] == repeatat && + typesequiv(time_zone_rule, time_zone_rule.types[i], repeatattype)) { + time_zone_rule.go_back = true; + break; + } + } + } + if (std::numeric_limits<s64>::min() + seconds_per_repeat <= + time_zone_rule.ats[time_zone_rule.time_count - 1]) { + s64 repeatat = time_zone_rule.ats[time_zone_rule.time_count - 1] - seconds_per_repeat; + int repeatattype = time_zone_rule.types[time_zone_rule.time_count - 1]; + for (int i = time_zone_rule.time_count; i >= 0; --i) { + if (time_zone_rule.ats[i] == repeatat && + typesequiv(time_zone_rule, time_zone_rule.types[i], repeatattype)) { + time_zone_rule.go_ahead = true; + break; + } + } + } } s32 default_type{}; @@ -745,8 +849,9 @@ static Result CreateCalendarTime(s64 time, int gmt_offset, CalendarTimeInternal& static Result ToCalendarTimeInternal(const TimeZoneRule& rules, s64 time, CalendarTimeInternal& calendar_time, CalendarAdditionalInfo& calendar_additional_info) { - if ((rules.go_ahead && time < rules.ats[0]) || - (rules.go_back && time > rules.ats[rules.time_count - 1])) { + ASSERT(rules.go_ahead ? rules.time_count > 0 : true); + if ((rules.go_back && time < rules.ats[0]) || + (rules.go_ahead && time > rules.ats[rules.time_count - 1])) { s64 seconds{}; if (time < rules.ats[0]) { seconds = rules.ats[0] - time; @@ -806,9 +911,13 @@ static Result ToCalendarTimeInternal(const TimeZoneRule& rules, s64 time, calendar_additional_info.is_dst = rules.ttis[tti_index].is_dst; const char* time_zone{&rules.chars[rules.ttis[tti_index].abbreviation_list_index]}; - for (int index{}; time_zone[index] != '\0'; ++index) { + u32 index; + for (index = 0; time_zone[index] != '\0' && time_zone[index] != ',' && + index < calendar_additional_info.timezone_name.size() - 1; + ++index) { calendar_additional_info.timezone_name[index] = time_zone[index]; } + calendar_additional_info.timezone_name[index] = '\0'; return ResultSuccess; } @@ -1038,4 +1147,36 @@ Result TimeZoneManager::GetDeviceLocationName(LocationName& value) const { return ResultSuccess; } +Result TimeZoneManager::GetTotalLocationNameCount(s32& count) const { + if (!is_initialized) { + return ERROR_UNINITIALIZED_CLOCK; + } + count = static_cast<u32>(total_location_name_count); + + return ResultSuccess; +} + +Result TimeZoneManager::GetTimeZoneRuleVersion(u128& version) const { + if (!is_initialized) { + return ERROR_UNINITIALIZED_CLOCK; + } + version = time_zone_rule_version; + + return ResultSuccess; +} + +Result TimeZoneManager::LoadLocationNameList(std::vector<LocationName>& values) const { + if (!is_initialized) { + return ERROR_UNINITIALIZED_CLOCK; + } + + for (const auto& name : total_location_names) { + LocationName entry{}; + std::memcpy(entry.data(), name.c_str(), name.size()); + values.push_back(entry); + } + + return ResultSuccess; +} + } // namespace Service::Time::TimeZone diff --git a/src/core/hle/service/time/time_zone_manager.h b/src/core/hle/service/time/time_zone_manager.h index 5ebd4035ec..8664f28d15 100644 --- a/src/core/hle/service/time/time_zone_manager.h +++ b/src/core/hle/service/time/time_zone_manager.h @@ -21,6 +21,10 @@ public: total_location_name_count = value; } + void SetLocationNames(std::vector<std::string> location_names) { + total_location_names = location_names; + } + void SetTimeZoneRuleVersion(const u128& value) { time_zone_rule_version = value; } @@ -33,6 +37,9 @@ public: FileSys::VirtualFile& vfs_file); Result SetUpdatedTime(const Clock::SteadyClockTimePoint& value); Result GetDeviceLocationName(TimeZone::LocationName& value) const; + Result GetTotalLocationNameCount(s32& count) const; + Result GetTimeZoneRuleVersion(u128& version) const; + Result LoadLocationNameList(std::vector<TimeZone::LocationName>& values) const; Result ToCalendarTime(const TimeZoneRule& rules, s64 time, CalendarInfo& calendar) const; Result ToCalendarTimeWithMyRules(s64 time, CalendarInfo& calendar) const; Result ParseTimeZoneRuleBinary(TimeZoneRule& rules, FileSys::VirtualFile& vfs_file) const; @@ -46,6 +53,7 @@ private: std::string device_location_name{"GMT"}; u128 time_zone_rule_version{}; std::size_t total_location_name_count{}; + std::vector<std::string> total_location_names{}; Clock::SteadyClockTimePoint time_zone_update_time_point{ Clock::SteadyClockTimePoint::GetRandom()}; }; diff --git a/src/core/hle/service/time/time_zone_service.cpp b/src/core/hle/service/time/time_zone_service.cpp index cda8d83430..8171c82a5b 100644 --- a/src/core/hle/service/time/time_zone_service.cpp +++ b/src/core/hle/service/time/time_zone_service.cpp @@ -15,10 +15,10 @@ ITimeZoneService::ITimeZoneService(Core::System& system_, static const FunctionInfo functions[] = { {0, &ITimeZoneService::GetDeviceLocationName, "GetDeviceLocationName"}, {1, nullptr, "SetDeviceLocationName"}, - {2, nullptr, "GetTotalLocationNameCount"}, - {3, nullptr, "LoadLocationNameList"}, + {2, &ITimeZoneService::GetTotalLocationNameCount, "GetTotalLocationNameCount"}, + {3, &ITimeZoneService::LoadLocationNameList, "LoadLocationNameList"}, {4, &ITimeZoneService::LoadTimeZoneRule, "LoadTimeZoneRule"}, - {5, nullptr, "GetTimeZoneRuleVersion"}, + {5, &ITimeZoneService::GetTimeZoneRuleVersion, "GetTimeZoneRuleVersion"}, {6, nullptr, "GetDeviceLocationNameAndUpdatedTime"}, {100, &ITimeZoneService::ToCalendarTime, "ToCalendarTime"}, {101, &ITimeZoneService::ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"}, @@ -45,6 +45,57 @@ void ITimeZoneService::GetDeviceLocationName(HLERequestContext& ctx) { rb.PushRaw(location_name); } +void ITimeZoneService::GetTotalLocationNameCount(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called"); + + s32 count{}; + if (const Result result{ + time_zone_content_manager.GetTimeZoneManager().GetTotalLocationNameCount(count)}; + result != ResultSuccess) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); + return; + } + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(count); +} + +void ITimeZoneService::LoadLocationNameList(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called"); + + std::vector<TimeZone::LocationName> location_names{}; + if (const Result result{ + time_zone_content_manager.GetTimeZoneManager().LoadLocationNameList(location_names)}; + result != ResultSuccess) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); + return; + } + + ctx.WriteBuffer(location_names); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(static_cast<s32>(location_names.size())); +} +void ITimeZoneService::GetTimeZoneRuleVersion(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called"); + + u128 rule_version{}; + if (const Result result{ + time_zone_content_manager.GetTimeZoneManager().GetTimeZoneRuleVersion(rule_version)}; + result != ResultSuccess) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); + return; + } + + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(ResultSuccess); + rb.PushRaw(rule_version); +} + void ITimeZoneService::LoadTimeZoneRule(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto raw_location_name{rp.PopRaw<std::array<u8, 0x24>>()}; @@ -61,20 +112,14 @@ void ITimeZoneService::LoadTimeZoneRule(HLERequestContext& ctx) { LOG_DEBUG(Service_Time, "called, location_name={}", location_name); TimeZone::TimeZoneRule time_zone_rule{}; - if (const Result result{ - time_zone_content_manager.LoadTimeZoneRule(time_zone_rule, location_name)}; - result != ResultSuccess) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } + const Result result{time_zone_content_manager.LoadTimeZoneRule(time_zone_rule, location_name)}; std::vector<u8> time_zone_rule_outbuffer(sizeof(TimeZone::TimeZoneRule)); std::memcpy(time_zone_rule_outbuffer.data(), &time_zone_rule, sizeof(TimeZone::TimeZoneRule)); ctx.WriteBuffer(time_zone_rule_outbuffer); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void ITimeZoneService::ToCalendarTime(HLERequestContext& ctx) { diff --git a/src/core/hle/service/time/time_zone_service.h b/src/core/hle/service/time/time_zone_service.h index ea83b57142..952fcb0e25 100644 --- a/src/core/hle/service/time/time_zone_service.h +++ b/src/core/hle/service/time/time_zone_service.h @@ -22,6 +22,9 @@ public: private: void GetDeviceLocationName(HLERequestContext& ctx); + void GetTotalLocationNameCount(HLERequestContext& ctx); + void LoadLocationNameList(HLERequestContext& ctx); + void GetTimeZoneRuleVersion(HLERequestContext& ctx); void LoadTimeZoneRule(HLERequestContext& ctx); void ToCalendarTime(HLERequestContext& ctx); void ToCalendarTimeWithMyRule(HLERequestContext& ctx); diff --git a/src/input_common/drivers/joycon.cpp b/src/input_common/drivers/joycon.cpp index b2b5677c8c..52494e0d9d 100644 --- a/src/input_common/drivers/joycon.cpp +++ b/src/input_common/drivers/joycon.cpp @@ -195,8 +195,8 @@ void Joycons::RegisterNewDevice(SDL_hid_device_info* device_info) { OnMotionUpdate(port, type, id, value); }}, .on_ring_data = {[this](f32 ring_data) { OnRingConUpdate(ring_data); }}, - .on_amiibo_data = {[this, port, type](const std::vector<u8>& amiibo_data) { - OnAmiiboUpdate(port, type, amiibo_data); + .on_amiibo_data = {[this, port, type](const Joycon::TagInfo& tag_info) { + OnAmiiboUpdate(port, type, tag_info); }}, .on_camera_data = {[this, port](const std::vector<u8>& camera_data, Joycon::IrsResolution format) { @@ -291,13 +291,105 @@ Common::Input::NfcState Joycons::SupportsNfc(const PadIdentifier& identifier_) c return Common::Input::NfcState::Success; }; +Common::Input::NfcState Joycons::StartNfcPolling(const PadIdentifier& identifier) { + auto handle = GetHandle(identifier); + if (handle == nullptr) { + return Common::Input::NfcState::Unknown; + } + return TranslateDriverResult(handle->StartNfcPolling()); +}; + +Common::Input::NfcState Joycons::StopNfcPolling(const PadIdentifier& identifier) { + auto handle = GetHandle(identifier); + if (handle == nullptr) { + return Common::Input::NfcState::Unknown; + } + return TranslateDriverResult(handle->StopNfcPolling()); +}; + +Common::Input::NfcState Joycons::ReadAmiiboData(const PadIdentifier& identifier, + std::vector<u8>& out_data) { + auto handle = GetHandle(identifier); + if (handle == nullptr) { + return Common::Input::NfcState::Unknown; + } + return TranslateDriverResult(handle->ReadAmiiboData(out_data)); +} + Common::Input::NfcState Joycons::WriteNfcData(const PadIdentifier& identifier, const std::vector<u8>& data) { auto handle = GetHandle(identifier); - if (handle->WriteNfcData(data) != Joycon::DriverResult::Success) { - return Common::Input::NfcState::WriteFailed; + if (handle == nullptr) { + return Common::Input::NfcState::Unknown; } - return Common::Input::NfcState::Success; + return TranslateDriverResult(handle->WriteNfcData(data)); +}; + +Common::Input::NfcState Joycons::ReadMifareData(const PadIdentifier& identifier, + const Common::Input::MifareRequest& request, + Common::Input::MifareRequest& data) { + auto handle = GetHandle(identifier); + if (handle == nullptr) { + return Common::Input::NfcState::Unknown; + } + + const auto command = static_cast<Joycon::MifareCmd>(request.data[0].command); + std::vector<Joycon::MifareReadChunk> read_request{}; + for (const auto& request_data : request.data) { + if (request_data.command == 0) { + continue; + } + Joycon::MifareReadChunk chunk = { + .command = command, + .sector_key = {}, + .sector = request_data.sector, + }; + memcpy(chunk.sector_key.data(), request_data.key.data(), + sizeof(Joycon::MifareReadChunk::sector_key)); + read_request.emplace_back(chunk); + } + + std::vector<Joycon::MifareReadData> read_data(read_request.size()); + const auto result = handle->ReadMifareData(read_request, read_data); + if (result == Joycon::DriverResult::Success) { + for (std::size_t i = 0; i < read_request.size(); i++) { + data.data[i] = { + .command = static_cast<u8>(command), + .sector = read_data[i].sector, + .key = {}, + .data = read_data[i].data, + }; + } + } + return TranslateDriverResult(result); +}; + +Common::Input::NfcState Joycons::WriteMifareData(const PadIdentifier& identifier, + const Common::Input::MifareRequest& request) { + auto handle = GetHandle(identifier); + if (handle == nullptr) { + return Common::Input::NfcState::Unknown; + } + + const auto command = static_cast<Joycon::MifareCmd>(request.data[0].command); + std::vector<Joycon::MifareWriteChunk> write_request{}; + for (const auto& request_data : request.data) { + if (request_data.command == 0) { + continue; + } + Joycon::MifareWriteChunk chunk = { + .command = command, + .sector_key = {}, + .sector = request_data.sector, + .data = {}, + }; + memcpy(chunk.sector_key.data(), request_data.key.data(), + sizeof(Joycon::MifareReadChunk::sector_key)); + memcpy(chunk.data.data(), request_data.data.data(), sizeof(Joycon::MifareWriteChunk::data)); + write_request.emplace_back(chunk); + } + + return TranslateDriverResult(handle->WriteMifareData(write_request)); }; Common::Input::DriverResult Joycons::SetPollingMode(const PadIdentifier& identifier, @@ -403,11 +495,20 @@ void Joycons::OnRingConUpdate(f32 ring_data) { } void Joycons::OnAmiiboUpdate(std::size_t port, Joycon::ControllerType type, - const std::vector<u8>& amiibo_data) { + const Joycon::TagInfo& tag_info) { const auto identifier = GetIdentifier(port, type); - const auto nfc_state = amiibo_data.empty() ? Common::Input::NfcState::AmiiboRemoved - : Common::Input::NfcState::NewAmiibo; - SetNfc(identifier, {nfc_state, amiibo_data}); + const auto nfc_state = tag_info.uuid_length == 0 ? Common::Input::NfcState::AmiiboRemoved + : Common::Input::NfcState::NewAmiibo; + + const Common::Input::NfcStatus nfc_status{ + .state = nfc_state, + .uuid_length = tag_info.uuid_length, + .protocol = tag_info.protocol, + .tag_type = tag_info.tag_type, + .uuid = tag_info.uuid, + }; + + SetNfc(identifier, nfc_status); } void Joycons::OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data, @@ -726,4 +827,18 @@ std::string Joycons::JoyconName(Joycon::ControllerType type) const { return "Unknown Switch Controller"; } } + +Common::Input::NfcState Joycons::TranslateDriverResult(Joycon::DriverResult result) const { + switch (result) { + case Joycon::DriverResult::Success: + return Common::Input::NfcState::Success; + case Joycon::DriverResult::Disabled: + return Common::Input::NfcState::WrongDeviceState; + case Joycon::DriverResult::NotSupported: + return Common::Input::NfcState::NotSupported; + default: + return Common::Input::NfcState::Unknown; + } +} + } // namespace InputCommon diff --git a/src/input_common/drivers/joycon.h b/src/input_common/drivers/joycon.h index e3f0ad78f9..4c323d7d6a 100644 --- a/src/input_common/drivers/joycon.h +++ b/src/input_common/drivers/joycon.h @@ -15,6 +15,7 @@ using SerialNumber = std::array<u8, 15>; struct Battery; struct Color; struct MotionData; +struct TagInfo; enum class ControllerType : u8; enum class DriverResult; enum class IrsResolution; @@ -39,9 +40,18 @@ public: Common::Input::DriverResult SetCameraFormat(const PadIdentifier& identifier, Common::Input::CameraFormat camera_format) override; - Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) const override; - Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier_, + Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier) const override; + Common::Input::NfcState StartNfcPolling(const PadIdentifier& identifier) override; + Common::Input::NfcState StopNfcPolling(const PadIdentifier& identifier) override; + Common::Input::NfcState ReadAmiiboData(const PadIdentifier& identifier, + std::vector<u8>& out_data) override; + Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier, const std::vector<u8>& data) override; + Common::Input::NfcState ReadMifareData(const PadIdentifier& identifier, + const Common::Input::MifareRequest& request, + Common::Input::MifareRequest& out_data) override; + Common::Input::NfcState WriteMifareData(const PadIdentifier& identifier, + const Common::Input::MifareRequest& request) override; Common::Input::DriverResult SetPollingMode( const PadIdentifier& identifier, const Common::Input::PollingMode polling_mode) override; @@ -82,7 +92,7 @@ private: const Joycon::MotionData& value); void OnRingConUpdate(f32 ring_data); void OnAmiiboUpdate(std::size_t port, Joycon::ControllerType type, - const std::vector<u8>& amiibo_data); + const Joycon::TagInfo& amiibo_data); void OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data, Joycon::IrsResolution format); @@ -102,6 +112,8 @@ private: /// Returns the name of the device in text format std::string JoyconName(Joycon::ControllerType type) const; + Common::Input::NfcState TranslateDriverResult(Joycon::DriverResult result) const; + std::jthread scan_thread; // Joycon types are split by type to ease supporting dualjoycon configurations diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp index 9a0439bb50..9f26392b17 100644 --- a/src/input_common/drivers/sdl_driver.cpp +++ b/src/input_common/drivers/sdl_driver.cpp @@ -150,6 +150,8 @@ public: if (sdl_controller) { const auto type = SDL_GameControllerGetType(sdl_controller.get()); return (type == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO) || + (type == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT) || + (type == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT) || (type == SDL_CONTROLLER_TYPE_PS5); } return false; @@ -228,9 +230,8 @@ public: return false; } - Common::Input::BatteryLevel GetBatteryLevel() { - const auto level = SDL_JoystickCurrentPowerLevel(sdl_joystick.get()); - switch (level) { + Common::Input::BatteryLevel GetBatteryLevel(SDL_JoystickPowerLevel battery_level) { + switch (battery_level) { case SDL_JOYSTICK_POWER_EMPTY: return Common::Input::BatteryLevel::Empty; case SDL_JOYSTICK_POWER_LOW: @@ -378,7 +379,6 @@ void SDLDriver::InitJoystick(int joystick_index) { if (joystick_map.find(guid) == joystick_map.end()) { auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller); PreSetController(joystick->GetPadIdentifier()); - SetBattery(joystick->GetPadIdentifier(), joystick->GetBatteryLevel()); joystick->EnableMotion(); joystick_map[guid].emplace_back(std::move(joystick)); return; @@ -398,7 +398,6 @@ void SDLDriver::InitJoystick(int joystick_index) { const int port = static_cast<int>(joystick_guid_list.size()); auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_gamecontroller); PreSetController(joystick->GetPadIdentifier()); - SetBattery(joystick->GetPadIdentifier(), joystick->GetBatteryLevel()); joystick->EnableMotion(); joystick_guid_list.emplace_back(std::move(joystick)); } @@ -438,8 +437,6 @@ void SDLDriver::HandleGameControllerEvent(const SDL_Event& event) { if (const auto joystick = GetSDLJoystickBySDLID(event.jbutton.which)) { const PadIdentifier identifier = joystick->GetPadIdentifier(); SetButton(identifier, event.jbutton.button, true); - // Battery doesn't trigger an event so just update every button press - SetBattery(identifier, joystick->GetBatteryLevel()); } break; } @@ -466,6 +463,13 @@ void SDLDriver::HandleGameControllerEvent(const SDL_Event& event) { } break; } + case SDL_JOYBATTERYUPDATED: { + if (auto joystick = GetSDLJoystickBySDLID(event.jbattery.which)) { + const PadIdentifier identifier = joystick->GetPadIdentifier(); + SetBattery(identifier, joystick->GetBatteryLevel(event.jbattery.level)); + } + break; + } case SDL_JOYDEVICEREMOVED: LOG_DEBUG(Input, "Controller removed with Instance_ID {}", event.jdevice.which); CloseJoystick(SDL_JoystickFromInstanceID(event.jdevice.which)); @@ -483,6 +487,10 @@ void SDLDriver::CloseJoysticks() { } SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_engine_)) { + // Set our application name. Currently passed to DBus by SDL and visible to the user through + // their desktop environment. + SDL_SetHint(SDL_HINT_APP_NAME, "yuzu"); + if (!Settings::values.enable_raw_input) { // Disable raw input. When enabled this setting causes SDL to die when a web applet opens SDL_SetHint(SDL_HINT_JOYSTICK_RAWINPUT, "0"); @@ -501,6 +509,9 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "0"); } else { SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1"); + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOYCON_HOME_LED, "0"); + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_COMBINE_JOY_CONS, "0"); + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_VERTICAL_JOY_CONS, "1"); } // Disable hidapi drivers for pro controllers when the custom joycon driver is enabled @@ -508,8 +519,11 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "0"); } else { SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "1"); + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_HOME_LED, "0"); } + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_PLAYER_LED, "1"); + // Disable hidapi driver for xbox. Already default on Windows, this causes conflict with native // driver on Linux. SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_XBOX, "0"); @@ -789,7 +803,9 @@ ButtonMapping SDLDriver::GetButtonMappingForDevice(const Common::ParamPackage& p // This list also excludes Screenshot since there's not really a mapping for that ButtonBindings switch_to_sdl_button; - if (SDL_GameControllerGetType(controller) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO) { + if (SDL_GameControllerGetType(controller) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO || + SDL_GameControllerGetType(controller) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT || + SDL_GameControllerGetType(controller) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT) { switch_to_sdl_button = GetNintendoButtonBinding(joystick); } else { switch_to_sdl_button = GetDefaultButtonBinding(); diff --git a/src/input_common/drivers/virtual_amiibo.cpp b/src/input_common/drivers/virtual_amiibo.cpp index f8bafe5534..180eb53ef1 100644 --- a/src/input_common/drivers/virtual_amiibo.cpp +++ b/src/input_common/drivers/virtual_amiibo.cpp @@ -29,14 +29,13 @@ Common::Input::DriverResult VirtualAmiibo::SetPollingMode( switch (polling_mode) { case Common::Input::PollingMode::NFC: - if (state == State::Initialized) { - state = State::WaitingForAmiibo; - } + state = State::Initialized; return Common::Input::DriverResult::Success; default: - if (state == State::AmiiboIsOpen) { + if (state == State::TagNearby) { CloseAmiibo(); } + state = State::Disabled; return Common::Input::DriverResult::NotSupported; } } @@ -45,6 +44,39 @@ Common::Input::NfcState VirtualAmiibo::SupportsNfc( [[maybe_unused]] const PadIdentifier& identifier_) const { return Common::Input::NfcState::Success; } +Common::Input::NfcState VirtualAmiibo::StartNfcPolling(const PadIdentifier& identifier_) { + if (state != State::Initialized) { + return Common::Input::NfcState::WrongDeviceState; + } + state = State::WaitingForAmiibo; + return Common::Input::NfcState::Success; +} + +Common::Input::NfcState VirtualAmiibo::StopNfcPolling(const PadIdentifier& identifier_) { + if (state == State::Disabled) { + return Common::Input::NfcState::WrongDeviceState; + } + if (state == State::TagNearby) { + CloseAmiibo(); + } + state = State::Initialized; + return Common::Input::NfcState::Success; +} + +Common::Input::NfcState VirtualAmiibo::ReadAmiiboData(const PadIdentifier& identifier_, + std::vector<u8>& out_data) { + if (state != State::TagNearby) { + return Common::Input::NfcState::WrongDeviceState; + } + + if (status.tag_type != 1U << 1) { + return Common::Input::NfcState::InvalidTagType; + } + + out_data.resize(nfc_data.size()); + memcpy(out_data.data(), nfc_data.data(), nfc_data.size()); + return Common::Input::NfcState::Success; +} Common::Input::NfcState VirtualAmiibo::WriteNfcData( [[maybe_unused]] const PadIdentifier& identifier_, const std::vector<u8>& data) { @@ -66,6 +98,69 @@ Common::Input::NfcState VirtualAmiibo::WriteNfcData( return Common::Input::NfcState::Success; } +Common::Input::NfcState VirtualAmiibo::ReadMifareData(const PadIdentifier& identifier_, + const Common::Input::MifareRequest& request, + Common::Input::MifareRequest& out_data) { + if (state != State::TagNearby) { + return Common::Input::NfcState::WrongDeviceState; + } + + if (status.tag_type != 1U << 6) { + return Common::Input::NfcState::InvalidTagType; + } + + for (std::size_t i = 0; i < request.data.size(); i++) { + if (request.data[i].command == 0) { + continue; + } + out_data.data[i].command = request.data[i].command; + out_data.data[i].sector = request.data[i].sector; + + const std::size_t sector_index = + request.data[i].sector * sizeof(Common::Input::MifareData::data); + + if (nfc_data.size() < sector_index + sizeof(Common::Input::MifareData::data)) { + return Common::Input::NfcState::WriteFailed; + } + + // Ignore the sector key as we don't support it + memcpy(out_data.data[i].data.data(), nfc_data.data() + sector_index, + sizeof(Common::Input::MifareData::data)); + } + + return Common::Input::NfcState::Success; +} + +Common::Input::NfcState VirtualAmiibo::WriteMifareData( + const PadIdentifier& identifier_, const Common::Input::MifareRequest& request) { + if (state != State::TagNearby) { + return Common::Input::NfcState::WrongDeviceState; + } + + if (status.tag_type != 1U << 6) { + return Common::Input::NfcState::InvalidTagType; + } + + for (std::size_t i = 0; i < request.data.size(); i++) { + if (request.data[i].command == 0) { + continue; + } + + const std::size_t sector_index = + request.data[i].sector * sizeof(Common::Input::MifareData::data); + + if (nfc_data.size() < sector_index + sizeof(Common::Input::MifareData::data)) { + return Common::Input::NfcState::WriteFailed; + } + + // Ignore the sector key as we don't support it + memcpy(nfc_data.data() + sector_index, request.data[i].data.data(), + sizeof(Common::Input::MifareData::data)); + } + + return Common::Input::NfcState::Success; +} + VirtualAmiibo::State VirtualAmiibo::GetCurrentState() const { return state; } @@ -82,6 +177,7 @@ VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(const std::string& filename) { switch (nfc_file.GetSize()) { case AmiiboSize: case AmiiboSizeWithoutPassword: + case AmiiboSizeWithSignature: data.resize(AmiiboSize); if (nfc_file.Read(data) < AmiiboSizeWithoutPassword) { return Info::NotAnAmiibo; @@ -109,24 +205,33 @@ VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(std::span<u8> data) { switch (data.size_bytes()) { case AmiiboSize: case AmiiboSizeWithoutPassword: + case AmiiboSizeWithSignature: nfc_data.resize(AmiiboSize); + status.tag_type = 1U << 1; + status.uuid_length = 7; break; case MifareSize: nfc_data.resize(MifareSize); + status.tag_type = 1U << 6; + status.uuid_length = 4; break; default: return Info::NotAnAmiibo; } - state = State::AmiiboIsOpen; + status.uuid = {}; + status.protocol = 1; + state = State::TagNearby; + status.state = Common::Input::NfcState::NewAmiibo, memcpy(nfc_data.data(), data.data(), data.size_bytes()); - SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, nfc_data}); + memcpy(status.uuid.data(), nfc_data.data(), status.uuid_length); + SetNfc(identifier, status); return Info::Success; } VirtualAmiibo::Info VirtualAmiibo::ReloadAmiibo() { - if (state == State::AmiiboIsOpen) { - SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, nfc_data}); + if (state == State::TagNearby) { + SetNfc(identifier, status); return Info::Success; } @@ -134,9 +239,14 @@ VirtualAmiibo::Info VirtualAmiibo::ReloadAmiibo() { } VirtualAmiibo::Info VirtualAmiibo::CloseAmiibo() { - state = polling_mode == Common::Input::PollingMode::NFC ? State::WaitingForAmiibo - : State::Initialized; - SetNfc(identifier, {Common::Input::NfcState::AmiiboRemoved, {}}); + if (state != State::TagNearby) { + return Info::Success; + } + + state = State::WaitingForAmiibo; + status.state = Common::Input::NfcState::AmiiboRemoved; + SetNfc(identifier, status); + status.tag_type = 0; return Info::Success; } diff --git a/src/input_common/drivers/virtual_amiibo.h b/src/input_common/drivers/virtual_amiibo.h index 34e97cd916..490f38e053 100644 --- a/src/input_common/drivers/virtual_amiibo.h +++ b/src/input_common/drivers/virtual_amiibo.h @@ -20,9 +20,10 @@ namespace InputCommon { class VirtualAmiibo final : public InputEngine { public: enum class State { + Disabled, Initialized, WaitingForAmiibo, - AmiiboIsOpen, + TagNearby, }; enum class Info { @@ -41,9 +42,17 @@ public: const PadIdentifier& identifier_, const Common::Input::PollingMode polling_mode_) override; Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) const override; - + Common::Input::NfcState StartNfcPolling(const PadIdentifier& identifier_) override; + Common::Input::NfcState StopNfcPolling(const PadIdentifier& identifier_) override; + Common::Input::NfcState ReadAmiiboData(const PadIdentifier& identifier_, + std::vector<u8>& out_data) override; Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier_, const std::vector<u8>& data) override; + Common::Input::NfcState ReadMifareData(const PadIdentifier& identifier_, + const Common::Input::MifareRequest& data, + Common::Input::MifareRequest& out_data) override; + Common::Input::NfcState WriteMifareData(const PadIdentifier& identifier_, + const Common::Input::MifareRequest& data) override; State GetCurrentState() const; @@ -57,11 +66,13 @@ public: private: static constexpr std::size_t AmiiboSize = 0x21C; static constexpr std::size_t AmiiboSizeWithoutPassword = AmiiboSize - 0x8; + static constexpr std::size_t AmiiboSizeWithSignature = AmiiboSize + 0x20; static constexpr std::size_t MifareSize = 0x400; std::string file_path{}; - State state{State::Initialized}; + State state{State::Disabled}; std::vector<u8> nfc_data; + Common::Input::NfcStatus status; Common::Input::PollingMode polling_mode{Common::Input::PollingMode::Passive}; }; } // namespace InputCommon diff --git a/src/input_common/helpers/joycon_driver.cpp b/src/input_common/helpers/joycon_driver.cpp index 95106f16d8..ec984a6473 100644 --- a/src/input_common/helpers/joycon_driver.cpp +++ b/src/input_common/helpers/joycon_driver.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" +#include "common/scope_exit.h" #include "common/swap.h" #include "common/thread.h" #include "input_common/helpers/joycon_driver.h" @@ -71,6 +72,7 @@ DriverResult JoyconDriver::InitializeDevice() { nfc_enabled = false; passive_enabled = false; irs_enabled = false; + input_only_device = false; gyro_sensitivity = Joycon::GyroSensitivity::DPS2000; gyro_performance = Joycon::GyroPerformance::HZ833; accelerometer_sensitivity = Joycon::AccelerometerSensitivity::G8; @@ -85,16 +87,23 @@ DriverResult JoyconDriver::InitializeDevice() { rumble_protocol = std::make_unique<RumbleProtocol>(hidapi_handle); // Get fixed joycon info - generic_protocol->GetVersionNumber(version); - generic_protocol->SetLowPowerMode(false); - generic_protocol->GetColor(color); - if (handle_device_type == ControllerType::Pro) { - // Some 3rd party controllers aren't pro controllers - generic_protocol->GetControllerType(device_type); - } else { - device_type = handle_device_type; + if (generic_protocol->GetVersionNumber(version) != DriverResult::Success) { + // If this command fails the device doesn't accept configuration commands + input_only_device = true; } - generic_protocol->GetSerialNumber(serial_number); + + if (!input_only_device) { + generic_protocol->SetLowPowerMode(false); + generic_protocol->GetColor(color); + if (handle_device_type == ControllerType::Pro) { + // Some 3rd party controllers aren't pro controllers + generic_protocol->GetControllerType(device_type); + } else { + device_type = handle_device_type; + } + generic_protocol->GetSerialNumber(serial_number); + } + supported_features = GetSupportedFeatures(); // Get Calibration data @@ -112,7 +121,7 @@ DriverResult JoyconDriver::InitializeDevice() { joycon_poller = std::make_unique<JoyconPoller>(device_type, left_stick_calibration, right_stick_calibration, motion_calibration); - // Start pooling for data + // Start polling for data is_connected = true; if (!input_thread_running) { input_thread = @@ -208,7 +217,7 @@ void JoyconDriver::OnNewData(std::span<u8> buffer) { joycon_poller->UpdateCamera(irs_protocol->GetImage(), irs_protocol->GetIrsFormat()); } - if (nfc_protocol->IsEnabled()) { + if (nfc_protocol->IsPolling()) { if (amiibo_detected) { if (!nfc_protocol->HasAmiibo()) { joycon_poller->UpdateAmiibo({}); @@ -218,10 +227,10 @@ void JoyconDriver::OnNewData(std::span<u8> buffer) { } if (!amiibo_detected) { - std::vector<u8> data(0x21C); - const auto result = nfc_protocol->ScanAmiibo(data); + Joycon::TagInfo tag_info; + const auto result = nfc_protocol->GetTagInfo(tag_info); if (result == DriverResult::Success) { - joycon_poller->UpdateAmiibo(data); + joycon_poller->UpdateAmiibo(tag_info); amiibo_detected = true; } } @@ -247,6 +256,7 @@ void JoyconDriver::OnNewData(std::span<u8> buffer) { } DriverResult JoyconDriver::SetPollingMode() { + SCOPE_EXIT({ disable_input_thread = false; }); disable_input_thread = true; rumble_protocol->EnableRumble(vibration_enabled && supported_features.vibration); @@ -259,6 +269,10 @@ DriverResult JoyconDriver::SetPollingMode() { generic_protocol->EnableImu(false); } + if (input_only_device) { + return DriverResult::NotSupported; + } + if (irs_protocol->IsEnabled()) { irs_protocol->DisableIrs(); } @@ -276,24 +290,21 @@ DriverResult JoyconDriver::SetPollingMode() { if (irs_enabled && supported_features.irs) { auto result = irs_protocol->EnableIrs(); if (result == DriverResult::Success) { - disable_input_thread = false; return result; } irs_protocol->DisableIrs(); LOG_ERROR(Input, "Error enabling IRS"); + return result; } if (nfc_enabled && supported_features.nfc) { auto result = nfc_protocol->EnableNfc(); if (result == DriverResult::Success) { - result = nfc_protocol->StartNFCPollingMode(); - } - if (result == DriverResult::Success) { - disable_input_thread = false; return result; } nfc_protocol->DisableNfc(); LOG_ERROR(Input, "Error enabling NFC"); + return result; } if (hidbus_enabled && supported_features.hidbus) { @@ -303,18 +314,17 @@ DriverResult JoyconDriver::SetPollingMode() { } if (result == DriverResult::Success) { ring_connected = true; - disable_input_thread = false; return result; } ring_connected = false; ring_protocol->DisableRingCon(); LOG_ERROR(Input, "Error enabling Ringcon"); + return result; } if (passive_enabled && supported_features.passive) { const auto result = generic_protocol->EnablePassiveMode(); if (result == DriverResult::Success) { - disable_input_thread = false; return result; } LOG_ERROR(Input, "Error enabling passive mode"); @@ -328,7 +338,6 @@ DriverResult JoyconDriver::SetPollingMode() { // Switch calls this function after enabling active mode generic_protocol->TriggersElapsed(); - disable_input_thread = false; return result; } @@ -339,6 +348,10 @@ JoyconDriver::SupportedFeatures JoyconDriver::GetSupportedFeatures() { .vibration = true, }; + if (input_only_device) { + return features; + } + if (device_type == ControllerType::Right) { features.nfc = true; features.irs = true; @@ -492,9 +505,68 @@ DriverResult JoyconDriver::SetRingConMode() { return result; } -DriverResult JoyconDriver::WriteNfcData(std::span<const u8> data) { +DriverResult JoyconDriver::StartNfcPolling() { + std::scoped_lock lock{mutex}; + + if (!supported_features.nfc) { + return DriverResult::NotSupported; + } + if (!nfc_protocol->IsEnabled()) { + return DriverResult::Disabled; + } + + disable_input_thread = true; + const auto result = nfc_protocol->StartNFCPollingMode(); + disable_input_thread = false; + + return result; +} + +DriverResult JoyconDriver::StopNfcPolling() { + std::scoped_lock lock{mutex}; + + if (!supported_features.nfc) { + return DriverResult::NotSupported; + } + if (!nfc_protocol->IsEnabled()) { + return DriverResult::Disabled; + } + + disable_input_thread = true; + const auto result = nfc_protocol->StopNFCPollingMode(); + disable_input_thread = false; + + if (amiibo_detected) { + amiibo_detected = false; + joycon_poller->UpdateAmiibo({}); + } + + return result; +} + +DriverResult JoyconDriver::ReadAmiiboData(std::vector<u8>& out_data) { std::scoped_lock lock{mutex}; + + if (!supported_features.nfc) { + return DriverResult::NotSupported; + } + if (!nfc_protocol->IsEnabled()) { + return DriverResult::Disabled; + } + if (!amiibo_detected) { + return DriverResult::ErrorWritingData; + } + + out_data.resize(0x21C); disable_input_thread = true; + const auto result = nfc_protocol->ReadAmiibo(out_data); + disable_input_thread = false; + + return result; +} + +DriverResult JoyconDriver::WriteNfcData(std::span<const u8> data) { + std::scoped_lock lock{mutex}; if (!supported_features.nfc) { return DriverResult::NotSupported; @@ -506,9 +578,51 @@ DriverResult JoyconDriver::WriteNfcData(std::span<const u8> data) { return DriverResult::ErrorWritingData; } + disable_input_thread = true; const auto result = nfc_protocol->WriteAmiibo(data); + disable_input_thread = false; + + return result; +} + +DriverResult JoyconDriver::ReadMifareData(std::span<const MifareReadChunk> data, + std::span<MifareReadData> out_data) { + std::scoped_lock lock{mutex}; + + if (!supported_features.nfc) { + return DriverResult::NotSupported; + } + if (!nfc_protocol->IsEnabled()) { + return DriverResult::Disabled; + } + if (!amiibo_detected) { + return DriverResult::ErrorWritingData; + } + + disable_input_thread = true; + const auto result = nfc_protocol->ReadMifare(data, out_data); + disable_input_thread = false; + + return result; +} + +DriverResult JoyconDriver::WriteMifareData(std::span<const MifareWriteChunk> data) { + std::scoped_lock lock{mutex}; + if (!supported_features.nfc) { + return DriverResult::NotSupported; + } + if (!nfc_protocol->IsEnabled()) { + return DriverResult::Disabled; + } + if (!amiibo_detected) { + return DriverResult::ErrorWritingData; + } + + disable_input_thread = true; + const auto result = nfc_protocol->WriteMifare(data); disable_input_thread = false; + return result; } diff --git a/src/input_common/helpers/joycon_driver.h b/src/input_common/helpers/joycon_driver.h index e9b2fccbb8..45b32d2f88 100644 --- a/src/input_common/helpers/joycon_driver.h +++ b/src/input_common/helpers/joycon_driver.h @@ -49,7 +49,13 @@ public: DriverResult SetIrMode(); DriverResult SetNfcMode(); DriverResult SetRingConMode(); + DriverResult StartNfcPolling(); + DriverResult StopNfcPolling(); + DriverResult ReadAmiiboData(std::vector<u8>& out_data); DriverResult WriteNfcData(std::span<const u8> data); + DriverResult ReadMifareData(std::span<const MifareReadChunk> request, + std::span<MifareReadData> out_data); + DriverResult WriteMifareData(std::span<const MifareWriteChunk> request); void SetCallbacks(const JoyconCallbacks& callbacks); @@ -114,6 +120,7 @@ private: // Hardware configuration u8 leds{}; ReportMode mode{}; + bool input_only_device{}; bool passive_enabled{}; // Low power mode, Ideal for multiple controllers at the same time bool hidbus_enabled{}; // External device support bool irs_enabled{}; // Infrared camera input diff --git a/src/input_common/helpers/joycon_protocol/common_protocol.cpp b/src/input_common/helpers/joycon_protocol/common_protocol.cpp index 51669261a7..88f4cec1cc 100644 --- a/src/input_common/helpers/joycon_protocol/common_protocol.cpp +++ b/src/input_common/helpers/joycon_protocol/common_protocol.cpp @@ -73,7 +73,7 @@ DriverResult JoyconCommonProtocol::SendRawData(std::span<const u8> buffer) { DriverResult JoyconCommonProtocol::GetSubCommandResponse(SubCommand sc, SubCommandResponse& output) { constexpr int timeout_mili = 66; - constexpr int MaxTries = 15; + constexpr int MaxTries = 3; int tries = 0; do { @@ -113,9 +113,7 @@ DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const return result; } - result = GetSubCommandResponse(sc, output); - - return DriverResult::Success; + return GetSubCommandResponse(sc, output); } DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const u8> buffer) { @@ -158,7 +156,7 @@ DriverResult JoyconCommonProtocol::SendVibrationReport(std::span<const u8> buffe DriverResult JoyconCommonProtocol::ReadRawSPI(SpiAddress addr, std::span<u8> output) { constexpr std::size_t HeaderSize = 5; - constexpr std::size_t MaxTries = 10; + constexpr std::size_t MaxTries = 5; std::size_t tries = 0; SubCommandResponse response{}; std::array<u8, sizeof(ReadSpiPacket)> buffer{}; diff --git a/src/input_common/helpers/joycon_protocol/joycon_types.h b/src/input_common/helpers/joycon_protocol/joycon_types.h index 5007b0e188..e0e4311568 100644 --- a/src/input_common/helpers/joycon_protocol/joycon_types.h +++ b/src/input_common/helpers/joycon_protocol/joycon_types.h @@ -24,6 +24,7 @@ constexpr std::array<u8, 8> DefaultVibrationBuffer{0x0, 0x1, 0x40, 0x40, 0x0, 0x using MacAddress = std::array<u8, 6>; using SerialNumber = std::array<u8, 15>; using TagUUID = std::array<u8, 7>; +using MifareUUID = std::array<u8, 4>; enum class ControllerType : u8 { None = 0x00, @@ -307,6 +308,19 @@ enum class NFCStatus : u8 { WriteDone = 0x05, TagLost = 0x07, WriteReady = 0x09, + MifareDone = 0x10, +}; + +enum class MifareCmd : u8 { + None = 0x00, + Read = 0x30, + AuthA = 0x60, + AuthB = 0x61, + Write = 0xA0, + Transfer = 0xB0, + Decrement = 0xC0, + Increment = 0xC1, + Store = 0xC2 }; enum class IrsMode : u8 { @@ -592,6 +606,14 @@ struct NFCWriteCommandData { static_assert(sizeof(NFCWriteCommandData) == 0x15, "NFCWriteCommandData is an invalid size"); #pragma pack(pop) +struct MifareCommandData { + u8 unknown1; + u8 unknown2; + u8 number_of_short_bytes; + MifareUUID uid; +}; +static_assert(sizeof(MifareCommandData) == 0x7, "MifareCommandData is an invalid size"); + struct NFCPollingCommandData { u8 enable_mifare; u8 unknown_1; @@ -629,6 +651,41 @@ struct NFCWritePackage { std::array<NFCDataChunk, 4> data_chunks; }; +struct MifareReadChunk { + MifareCmd command; + std::array<u8, 0x6> sector_key; + u8 sector; +}; + +struct MifareWriteChunk { + MifareCmd command; + std::array<u8, 0x6> sector_key; + u8 sector; + std::array<u8, 0x10> data; +}; + +struct MifareReadData { + u8 sector; + std::array<u8, 0x10> data; +}; + +struct MifareReadPackage { + MifareCommandData command_data; + std::array<MifareReadChunk, 0x10> data_chunks; +}; + +struct MifareWritePackage { + MifareCommandData command_data; + std::array<MifareWriteChunk, 0x10> data_chunks; +}; + +struct TagInfo { + u8 uuid_length; + u8 protocol; + u8 tag_type; + std::array<u8, 10> uuid; +}; + struct IrsConfigure { MCUCommand command; MCUSubCommand sub_command; @@ -744,7 +801,7 @@ struct JoyconCallbacks { std::function<void(int, f32)> on_stick_data; std::function<void(int, const MotionData&)> on_motion_data; std::function<void(f32)> on_ring_data; - std::function<void(const std::vector<u8>&)> on_amiibo_data; + std::function<void(const Joycon::TagInfo&)> on_amiibo_data; std::function<void(const std::vector<u8>&, IrsResolution)> on_camera_data; }; diff --git a/src/input_common/helpers/joycon_protocol/nfc.cpp b/src/input_common/helpers/joycon_protocol/nfc.cpp index f7058c4a76..261f46255b 100644 --- a/src/input_common/helpers/joycon_protocol/nfc.cpp +++ b/src/input_common/helpers/joycon_protocol/nfc.cpp @@ -40,6 +40,16 @@ DriverResult NfcProtocol::EnableNfc() { if (result == DriverResult::Success) { result = WaitUntilNfcIs(NFCStatus::Ready); } + if (result == DriverResult::Success) { + MCUCommandResponse output{}; + result = SendStopPollingRequest(output); + } + if (result == DriverResult::Success) { + result = WaitUntilNfcIs(NFCStatus::Ready); + } + if (result == DriverResult::Success) { + is_enabled = true; + } return result; } @@ -54,37 +64,50 @@ DriverResult NfcProtocol::DisableNfc() { } is_enabled = false; + is_polling = false; return result; } DriverResult NfcProtocol::StartNFCPollingMode() { - LOG_DEBUG(Input, "Start NFC pooling Mode"); + LOG_DEBUG(Input, "Start NFC polling Mode"); ScopedSetBlocking sb(this); DriverResult result{DriverResult::Success}; if (result == DriverResult::Success) { MCUCommandResponse output{}; - result = SendStopPollingRequest(output); + result = SendStartPollingRequest(output); } if (result == DriverResult::Success) { - result = WaitUntilNfcIs(NFCStatus::Ready); + result = WaitUntilNfcIs(NFCStatus::Polling); } if (result == DriverResult::Success) { + is_polling = true; + } + + return result; +} + +DriverResult NfcProtocol::StopNFCPollingMode() { + LOG_DEBUG(Input, "Stop NFC polling Mode"); + ScopedSetBlocking sb(this); + DriverResult result{DriverResult::Success}; + + if (result == DriverResult::Success) { MCUCommandResponse output{}; - result = SendStartPollingRequest(output); + result = SendStopPollingRequest(output); } if (result == DriverResult::Success) { - result = WaitUntilNfcIs(NFCStatus::Polling); + result = WaitUntilNfcIs(NFCStatus::WriteReady); } if (result == DriverResult::Success) { - is_enabled = true; + is_polling = false; } return result; } -DriverResult NfcProtocol::ScanAmiibo(std::vector<u8>& data) { +DriverResult NfcProtocol::GetTagInfo(Joycon::TagInfo& tag_info) { if (update_counter++ < AMIIBO_UPDATE_DELAY) { return DriverResult::Delayed; } @@ -100,11 +123,41 @@ DriverResult NfcProtocol::ScanAmiibo(std::vector<u8>& data) { } if (result == DriverResult::Success) { + tag_info = { + .uuid_length = tag_data.uuid_size, + .protocol = 1, + .tag_type = tag_data.type, + .uuid = {}, + }; + + memcpy(tag_info.uuid.data(), tag_data.uuid.data(), tag_data.uuid_size); + + // Investigate why mifare type is not correct + if (tag_info.tag_type == 144) { + tag_info.tag_type = 1U << 6; + } + std::string uuid_string; for (auto& content : tag_data.uuid) { uuid_string += fmt::format(" {:02x}", content); } LOG_INFO(Input, "Tag detected, type={}, uuid={}", tag_data.type, uuid_string); + } + + return result; +} + +DriverResult NfcProtocol::ReadAmiibo(std::vector<u8>& data) { + LOG_DEBUG(Input, "Scan for amiibos"); + ScopedSetBlocking sb(this); + DriverResult result{DriverResult::Success}; + TagFoundData tag_data{}; + + if (result == DriverResult::Success) { + result = IsTagInRange(tag_data, 7); + } + + if (result == DriverResult::Success) { result = GetAmiiboData(data); } @@ -154,6 +207,69 @@ DriverResult NfcProtocol::WriteAmiibo(std::span<const u8> data) { return result; } +DriverResult NfcProtocol::ReadMifare(std::span<const MifareReadChunk> read_request, + std::span<MifareReadData> out_data) { + LOG_DEBUG(Input, "Read mifare"); + ScopedSetBlocking sb(this); + DriverResult result{DriverResult::Success}; + TagFoundData tag_data{}; + MifareUUID tag_uuid{}; + + if (result == DriverResult::Success) { + result = IsTagInRange(tag_data, 7); + } + if (result == DriverResult::Success) { + memcpy(tag_uuid.data(), tag_data.uuid.data(), sizeof(MifareUUID)); + result = GetMifareData(tag_uuid, read_request, out_data); + } + if (result == DriverResult::Success) { + MCUCommandResponse output{}; + result = SendStopPollingRequest(output); + } + if (result == DriverResult::Success) { + result = WaitUntilNfcIs(NFCStatus::Ready); + } + if (result == DriverResult::Success) { + MCUCommandResponse output{}; + result = SendStartPollingRequest(output, true); + } + if (result == DriverResult::Success) { + result = WaitUntilNfcIs(NFCStatus::WriteReady); + } + return result; +} + +DriverResult NfcProtocol::WriteMifare(std::span<const MifareWriteChunk> write_request) { + LOG_DEBUG(Input, "Write mifare"); + ScopedSetBlocking sb(this); + DriverResult result{DriverResult::Success}; + TagFoundData tag_data{}; + MifareUUID tag_uuid{}; + + if (result == DriverResult::Success) { + result = IsTagInRange(tag_data, 7); + } + if (result == DriverResult::Success) { + memcpy(tag_uuid.data(), tag_data.uuid.data(), sizeof(MifareUUID)); + result = WriteMifareData(tag_uuid, write_request); + } + if (result == DriverResult::Success) { + MCUCommandResponse output{}; + result = SendStopPollingRequest(output); + } + if (result == DriverResult::Success) { + result = WaitUntilNfcIs(NFCStatus::Ready); + } + if (result == DriverResult::Success) { + MCUCommandResponse output{}; + result = SendStartPollingRequest(output, true); + } + if (result == DriverResult::Success) { + result = WaitUntilNfcIs(NFCStatus::WriteReady); + } + return result; +} + bool NfcProtocol::HasAmiibo() { if (update_counter++ < AMIIBO_UPDATE_DELAY) { return true; @@ -341,6 +457,158 @@ DriverResult NfcProtocol::WriteAmiiboData(const TagUUID& tag_uuid, std::span<con return result; } +DriverResult NfcProtocol::GetMifareData(const MifareUUID& tag_uuid, + std::span<const MifareReadChunk> read_request, + std::span<MifareReadData> out_data) { + constexpr std::size_t timeout_limit = 60; + const auto nfc_data = MakeMifareReadPackage(tag_uuid, read_request); + const std::vector<u8> nfc_buffer_data = SerializeMifareReadPackage(nfc_data); + std::span<const u8> buffer(nfc_buffer_data); + DriverResult result = DriverResult::Success; + MCUCommandResponse output{}; + u8 block_id = 1; + u8 package_index = 0; + std::size_t tries = 0; + std::size_t current_position = 0; + + LOG_INFO(Input, "Reading Mifare data"); + + // Send data request. Nfc buffer size is 31, Send the data in smaller packages + while (current_position < buffer.size() && tries++ < timeout_limit) { + const std::size_t next_position = + std::min(current_position + sizeof(NFCRequestState::raw_data), buffer.size()); + const std::size_t block_size = next_position - current_position; + const bool is_last_packet = block_size < sizeof(NFCRequestState::raw_data); + + SendReadDataMifareRequest(output, block_id, is_last_packet, + buffer.subspan(current_position, block_size)); + + const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]); + + if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::TagLost) { + return DriverResult::ErrorReadingData; + } + + // Increase position when data is confirmed by the joycon + if (output.mcu_report == MCUReport::NFCState && + (output.mcu_data[1] << 8) + output.mcu_data[0] == 0x0500 && + output.mcu_data[3] == block_id) { + block_id++; + current_position = next_position; + } + } + + if (result != DriverResult::Success) { + return result; + } + + // Wait for reply and save the output data + while (tries++ < timeout_limit) { + result = SendNextPackageRequest(output, package_index); + const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]); + + if (result != DriverResult::Success) { + return result; + } + + if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::TagLost) { + return DriverResult::ErrorReadingData; + } + + if (output.mcu_report == MCUReport::NFCState && output.mcu_data[1] == 0x10) { + constexpr std::size_t DATA_LENGHT = 0x10 + 1; + constexpr std::size_t DATA_START = 11; + const u8 number_of_elements = output.mcu_data[10]; + for (std::size_t i = 0; i < number_of_elements; i++) { + out_data[i].sector = output.mcu_data[DATA_START + (i * DATA_LENGHT)]; + memcpy(out_data[i].data.data(), + output.mcu_data.data() + DATA_START + 1 + (i * DATA_LENGHT), + sizeof(MifareReadData::data)); + } + package_index++; + continue; + } + + if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::MifareDone) { + LOG_INFO(Input, "Finished reading mifare"); + break; + } + } + + return result; +} + +DriverResult NfcProtocol::WriteMifareData(const MifareUUID& tag_uuid, + std::span<const MifareWriteChunk> write_request) { + constexpr std::size_t timeout_limit = 60; + const auto nfc_data = MakeMifareWritePackage(tag_uuid, write_request); + const std::vector<u8> nfc_buffer_data = SerializeMifareWritePackage(nfc_data); + std::span<const u8> buffer(nfc_buffer_data); + DriverResult result = DriverResult::Success; + MCUCommandResponse output{}; + u8 block_id = 1; + u8 package_index = 0; + std::size_t tries = 0; + std::size_t current_position = 0; + + LOG_INFO(Input, "Writing Mifare data"); + + // Send data request. Nfc buffer size is 31, Send the data in smaller packages + while (current_position < buffer.size() && tries++ < timeout_limit) { + const std::size_t next_position = + std::min(current_position + sizeof(NFCRequestState::raw_data), buffer.size()); + const std::size_t block_size = next_position - current_position; + const bool is_last_packet = block_size < sizeof(NFCRequestState::raw_data); + + SendReadDataMifareRequest(output, block_id, is_last_packet, + buffer.subspan(current_position, block_size)); + + const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]); + + if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::TagLost) { + return DriverResult::ErrorReadingData; + } + + // Increase position when data is confirmed by the joycon + if (output.mcu_report == MCUReport::NFCState && + (output.mcu_data[1] << 8) + output.mcu_data[0] == 0x0500 && + output.mcu_data[3] == block_id) { + block_id++; + current_position = next_position; + } + } + + if (result != DriverResult::Success) { + return result; + } + + // Wait for reply and ignore the output data + while (tries++ < timeout_limit) { + result = SendNextPackageRequest(output, package_index); + const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]); + + if (result != DriverResult::Success) { + return result; + } + + if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::TagLost) { + return DriverResult::ErrorReadingData; + } + + if (output.mcu_report == MCUReport::NFCState && output.mcu_data[1] == 0x10) { + package_index++; + continue; + } + + if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::MifareDone) { + LOG_INFO(Input, "Finished writing mifare"); + break; + } + } + + return result; +} + DriverResult NfcProtocol::SendStartPollingRequest(MCUCommandResponse& output, bool is_second_attempt) { NFCRequestState request{ @@ -477,6 +745,28 @@ DriverResult NfcProtocol::SendWriteDataAmiiboRequest(MCUCommandResponse& output, output); } +DriverResult NfcProtocol::SendReadDataMifareRequest(MCUCommandResponse& output, u8 block_id, + bool is_last_packet, std::span<const u8> data) { + const auto data_size = std::min(data.size(), sizeof(NFCRequestState::raw_data)); + NFCRequestState request{ + .command_argument = NFCCommand::Mifare, + .block_id = block_id, + .packet_id = {}, + .packet_flag = + is_last_packet ? MCUPacketFlag::LastCommandPacket : MCUPacketFlag::MorePacketsRemaining, + .data_length = static_cast<u8>(data_size), + .raw_data = {}, + .crc = {}, + }; + memcpy(request.raw_data.data(), data.data(), data_size); + + std::array<u8, sizeof(NFCRequestState)> request_data{}; + memcpy(request_data.data(), &request, sizeof(NFCRequestState)); + request_data[36] = CalculateMCU_CRC8(request_data.data(), 36); + return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, MCUSubCommand::ReadDeviceMode, request_data, + output); +} + std::vector<u8> NfcProtocol::SerializeWritePackage(const NFCWritePackage& package) const { const std::size_t header_size = sizeof(NFCWriteCommandData) + sizeof(NFCWritePackage::number_of_chunks); @@ -498,6 +788,48 @@ std::vector<u8> NfcProtocol::SerializeWritePackage(const NFCWritePackage& packag return serialized_data; } +std::vector<u8> NfcProtocol::SerializeMifareReadPackage(const MifareReadPackage& package) const { + const std::size_t header_size = sizeof(MifareCommandData); + std::vector<u8> serialized_data(header_size); + std::size_t start_index = 0; + + memcpy(serialized_data.data(), &package, header_size); + start_index += header_size; + + for (const auto& data_chunk : package.data_chunks) { + const std::size_t chunk_size = sizeof(MifareReadChunk); + if (data_chunk.command == MifareCmd::None) { + continue; + } + serialized_data.resize(start_index + chunk_size); + memcpy(serialized_data.data() + start_index, &data_chunk, chunk_size); + start_index += chunk_size; + } + + return serialized_data; +} + +std::vector<u8> NfcProtocol::SerializeMifareWritePackage(const MifareWritePackage& package) const { + const std::size_t header_size = sizeof(MifareCommandData); + std::vector<u8> serialized_data(header_size); + std::size_t start_index = 0; + + memcpy(serialized_data.data(), &package, header_size); + start_index += header_size; + + for (const auto& data_chunk : package.data_chunks) { + const std::size_t chunk_size = sizeof(MifareWriteChunk); + if (data_chunk.command == MifareCmd::None) { + continue; + } + serialized_data.resize(start_index + chunk_size); + memcpy(serialized_data.data() + start_index, &data_chunk, chunk_size); + start_index += chunk_size; + } + + return serialized_data; +} + NFCWritePackage NfcProtocol::MakeAmiiboWritePackage(const TagUUID& tag_uuid, std::span<const u8> data) const { return { @@ -527,6 +859,46 @@ NFCWritePackage NfcProtocol::MakeAmiiboWritePackage(const TagUUID& tag_uuid, }; } +MifareReadPackage NfcProtocol::MakeMifareReadPackage( + const MifareUUID& tag_uuid, std::span<const MifareReadChunk> read_request) const { + MifareReadPackage package{ + .command_data{ + .unknown1 = 0xd0, + .unknown2 = 0x07, + .number_of_short_bytes = static_cast<u8>( + ((read_request.size() * sizeof(MifareReadChunk)) + sizeof(MifareUUID)) / 2), + .uid = tag_uuid, + }, + .data_chunks = {}, + }; + + for (std::size_t i = 0; i < read_request.size() && i < package.data_chunks.size(); ++i) { + package.data_chunks[i] = read_request[i]; + } + + return package; +} + +MifareWritePackage NfcProtocol::MakeMifareWritePackage( + const MifareUUID& tag_uuid, std::span<const MifareWriteChunk> read_request) const { + MifareWritePackage package{ + .command_data{ + .unknown1 = 0xd0, + .unknown2 = 0x07, + .number_of_short_bytes = static_cast<u8>( + ((read_request.size() * sizeof(MifareReadChunk)) + sizeof(MifareUUID) + 2) / 2), + .uid = tag_uuid, + }, + .data_chunks = {}, + }; + + for (std::size_t i = 0; i < read_request.size() && i < package.data_chunks.size(); ++i) { + package.data_chunks[i] = read_request[i]; + } + + return package; +} + NFCDataChunk NfcProtocol::MakeAmiiboChunk(u8 page, u8 size, std::span<const u8> data) const { constexpr u8 NFC_PAGE_SIZE = 4; @@ -606,4 +978,8 @@ bool NfcProtocol::IsEnabled() const { return is_enabled; } +bool NfcProtocol::IsPolling() const { + return is_polling; +} + } // namespace InputCommon::Joycon diff --git a/src/input_common/helpers/joycon_protocol/nfc.h b/src/input_common/helpers/joycon_protocol/nfc.h index eb58c427db..0be95e40ef 100644 --- a/src/input_common/helpers/joycon_protocol/nfc.h +++ b/src/input_common/helpers/joycon_protocol/nfc.h @@ -25,14 +25,25 @@ public: DriverResult StartNFCPollingMode(); - DriverResult ScanAmiibo(std::vector<u8>& data); + DriverResult StopNFCPollingMode(); + + DriverResult GetTagInfo(Joycon::TagInfo& tag_info); + + DriverResult ReadAmiibo(std::vector<u8>& data); DriverResult WriteAmiibo(std::span<const u8> data); + DriverResult ReadMifare(std::span<const MifareReadChunk> read_request, + std::span<MifareReadData> out_data); + + DriverResult WriteMifare(std::span<const MifareWriteChunk> write_request); + bool HasAmiibo(); bool IsEnabled() const; + bool IsPolling() const; + private: // Number of times the function will be delayed until it outputs valid data static constexpr std::size_t AMIIBO_UPDATE_DELAY = 15; @@ -51,6 +62,13 @@ private: DriverResult WriteAmiiboData(const TagUUID& tag_uuid, std::span<const u8> data); + DriverResult GetMifareData(const MifareUUID& tag_uuid, + std::span<const MifareReadChunk> read_request, + std::span<MifareReadData> out_data); + + DriverResult WriteMifareData(const MifareUUID& tag_uuid, + std::span<const MifareWriteChunk> write_request); + DriverResult SendStartPollingRequest(MCUCommandResponse& output, bool is_second_attempt = false); @@ -65,17 +83,31 @@ private: DriverResult SendWriteDataAmiiboRequest(MCUCommandResponse& output, u8 block_id, bool is_last_packet, std::span<const u8> data); + DriverResult SendReadDataMifareRequest(MCUCommandResponse& output, u8 block_id, + bool is_last_packet, std::span<const u8> data); + std::vector<u8> SerializeWritePackage(const NFCWritePackage& package) const; + std::vector<u8> SerializeMifareReadPackage(const MifareReadPackage& package) const; + + std::vector<u8> SerializeMifareWritePackage(const MifareWritePackage& package) const; + NFCWritePackage MakeAmiiboWritePackage(const TagUUID& tag_uuid, std::span<const u8> data) const; NFCDataChunk MakeAmiiboChunk(u8 page, u8 size, std::span<const u8> data) const; + MifareReadPackage MakeMifareReadPackage(const MifareUUID& tag_uuid, + std::span<const MifareReadChunk> read_request) const; + + MifareWritePackage MakeMifareWritePackage(const MifareUUID& tag_uuid, + std::span<const MifareWriteChunk> read_request) const; + NFCReadBlockCommand GetReadBlockCommand(NFCPages pages) const; TagUUID GetTagUUID(std::span<const u8> data) const; bool is_enabled{}; + bool is_polling{}; std::size_t update_counter{}; }; diff --git a/src/input_common/helpers/joycon_protocol/poller.cpp b/src/input_common/helpers/joycon_protocol/poller.cpp index dca797f7ae..1aab9e12a3 100644 --- a/src/input_common/helpers/joycon_protocol/poller.cpp +++ b/src/input_common/helpers/joycon_protocol/poller.cpp @@ -70,8 +70,8 @@ void JoyconPoller::UpdateColor(const Color& color) { callbacks.on_color_data(color); } -void JoyconPoller::UpdateAmiibo(const std::vector<u8>& amiibo_data) { - callbacks.on_amiibo_data(amiibo_data); +void JoyconPoller::UpdateAmiibo(const Joycon::TagInfo& tag_info) { + callbacks.on_amiibo_data(tag_info); } void JoyconPoller::UpdateCamera(const std::vector<u8>& camera_data, IrsResolution format) { diff --git a/src/input_common/helpers/joycon_protocol/poller.h b/src/input_common/helpers/joycon_protocol/poller.h index 0fa72c6dbb..3746abe5d8 100644 --- a/src/input_common/helpers/joycon_protocol/poller.h +++ b/src/input_common/helpers/joycon_protocol/poller.h @@ -36,8 +36,8 @@ public: void UpdateColor(const Color& color); void UpdateRing(s16 value, const RingStatus& ring_status); - void UpdateAmiibo(const std::vector<u8>& amiibo_data); - void UpdateCamera(const std::vector<u8>& amiibo_data, IrsResolution format); + void UpdateAmiibo(const Joycon::TagInfo& tag_info); + void UpdateCamera(const std::vector<u8>& camera_data, IrsResolution format); private: void UpdateActiveLeftPadInput(const InputReportActive& input, diff --git a/src/input_common/input_engine.h b/src/input_common/input_engine.h index 50b5a3dc8c..c2d0cbb34f 100644 --- a/src/input_common/input_engine.h +++ b/src/input_common/input_engine.h @@ -143,12 +143,46 @@ public: return Common::Input::NfcState::NotSupported; } + // Start scanning for nfc tags + virtual Common::Input::NfcState StartNfcPolling( + [[maybe_unused]] const PadIdentifier& identifier_) { + return Common::Input::NfcState::NotSupported; + } + + // Start scanning for nfc tags + virtual Common::Input::NfcState StopNfcPolling( + [[maybe_unused]] const PadIdentifier& identifier_) { + return Common::Input::NfcState::NotSupported; + } + + // Reads data from amiibo tag + virtual Common::Input::NfcState ReadAmiiboData( + [[maybe_unused]] const PadIdentifier& identifier_, + [[maybe_unused]] std::vector<u8>& out_data) { + return Common::Input::NfcState::NotSupported; + } + // Writes data to an nfc tag virtual Common::Input::NfcState WriteNfcData([[maybe_unused]] const PadIdentifier& identifier, [[maybe_unused]] const std::vector<u8>& data) { return Common::Input::NfcState::NotSupported; } + // Reads data from mifare tag + virtual Common::Input::NfcState ReadMifareData( + [[maybe_unused]] const PadIdentifier& identifier_, + [[maybe_unused]] const Common::Input::MifareRequest& request, + [[maybe_unused]] Common::Input::MifareRequest& out_data) { + return Common::Input::NfcState::NotSupported; + } + + // Write data to mifare tag + virtual Common::Input::NfcState WriteMifareData( + [[maybe_unused]] const PadIdentifier& identifier_, + [[maybe_unused]] const Common::Input::MifareRequest& request) { + return Common::Input::NfcState::NotSupported; + } + // Returns the engine name [[nodiscard]] const std::string& GetEngineName() const; diff --git a/src/input_common/input_poller.cpp b/src/input_common/input_poller.cpp index 380a015422..870e76ab0a 100644 --- a/src/input_common/input_poller.cpp +++ b/src/input_common/input_poller.cpp @@ -792,8 +792,7 @@ public: const Common::Input::CallbackStatus status{ .type = Common::Input::InputType::Nfc, - .nfc_status = nfc_status.state, - .raw_data = nfc_status.data, + .nfc_status = nfc_status, }; TriggerOnChange(status); @@ -836,10 +835,31 @@ public: return input_engine->SupportsNfc(identifier); } + Common::Input::NfcState StartNfcPolling() { + return input_engine->StartNfcPolling(identifier); + } + + Common::Input::NfcState StopNfcPolling() { + return input_engine->StopNfcPolling(identifier); + } + + Common::Input::NfcState ReadAmiiboData(std::vector<u8>& out_data) { + return input_engine->ReadAmiiboData(identifier, out_data); + } + Common::Input::NfcState WriteNfcData(const std::vector<u8>& data) override { return input_engine->WriteNfcData(identifier, data); } + Common::Input::NfcState ReadMifareData(const Common::Input::MifareRequest& request, + Common::Input::MifareRequest& out_data) { + return input_engine->ReadMifareData(identifier, request, out_data); + } + + Common::Input::NfcState WriteMifareData(const Common::Input::MifareRequest& request) { + return input_engine->WriteMifareData(identifier, request); + } + private: const PadIdentifier identifier; InputEngine* input_engine; diff --git a/src/shader_recompiler/CMakeLists.txt b/src/shader_recompiler/CMakeLists.txt index 525b2363c3..07e75f9d8d 100644 --- a/src/shader_recompiler/CMakeLists.txt +++ b/src/shader_recompiler/CMakeLists.txt @@ -216,6 +216,7 @@ add_library(shader_recompiler STATIC frontend/maxwell/translate_program.h host_translate_info.h ir_opt/collect_shader_info_pass.cpp + ir_opt/conditional_barrier_pass.cpp ir_opt/constant_propagation_pass.cpp ir_opt/dead_code_elimination_pass.cpp ir_opt/dual_vertex_pass.cpp @@ -223,6 +224,7 @@ add_library(shader_recompiler STATIC ir_opt/identity_removal_pass.cpp ir_opt/layer_pass.cpp ir_opt/lower_fp16_to_fp32.cpp + ir_opt/lower_fp64_to_fp32.cpp ir_opt/lower_int64_to_int32.cpp ir_opt/passes.h ir_opt/position_pass.cpp diff --git a/src/shader_recompiler/backend/glasm/emit_glasm.cpp b/src/shader_recompiler/backend/glasm/emit_glasm.cpp index fd4a61a4de..b795c01797 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm.cpp @@ -461,7 +461,7 @@ std::string EmitGLASM(const Profile& profile, const RuntimeInfo& runtime_info, I header += fmt::format("R{},", index); } if (program.local_memory_size > 0) { - header += fmt::format("lmem[{}],", program.local_memory_size); + header += fmt::format("lmem[{}],", Common::DivCeil(program.local_memory_size, 4U)); } if (program.info.uses_fswzadd) { header += "FSWZA[4],FSWZB[4],"; diff --git a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp index c3c2281bbc..9ff4028c26 100644 --- a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp +++ b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp @@ -479,7 +479,7 @@ void EmitContext::DefineGenericOutput(size_t index, u32 invocations) { const u32 remainder{4 - element}; const TransformFeedbackVarying* xfb_varying{}; const size_t xfb_varying_index{base_index + element}; - if (xfb_varying_index < runtime_info.xfb_varyings.size()) { + if (xfb_varying_index < runtime_info.xfb_count) { xfb_varying = &runtime_info.xfb_varyings[xfb_varying_index]; xfb_varying = xfb_varying->components > 0 ? xfb_varying : nullptr; } diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp index 0f86a80049..34592a01ff 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp @@ -387,7 +387,7 @@ void SetupSignedNanCapabilities(const Profile& profile, const IR::Program& progr } void SetupTransformFeedbackCapabilities(EmitContext& ctx, Id main_func) { - if (ctx.runtime_info.xfb_varyings.empty()) { + if (ctx.runtime_info.xfb_count == 0) { return; } ctx.AddCapability(spv::Capability::TransformFeedback); diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index fd15f47eaf..bec5db1735 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -160,7 +160,7 @@ void DefineGenericOutput(EmitContext& ctx, size_t index, std::optional<u32> invo const u32 remainder{4 - element}; const TransformFeedbackVarying* xfb_varying{}; const size_t xfb_varying_index{base_attr_index + element}; - if (xfb_varying_index < ctx.runtime_info.xfb_varyings.size()) { + if (xfb_varying_index < ctx.runtime_info.xfb_count) { xfb_varying = &ctx.runtime_info.xfb_varyings[xfb_varying_index]; xfb_varying = xfb_varying->components > 0 ? xfb_varying : nullptr; } diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp index 17a6d48883..928b355611 100644 --- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp @@ -280,12 +280,18 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo RemoveUnreachableBlocks(program); // Replace instructions before the SSA rewrite + if (!host_info.support_float64) { + Optimization::LowerFp64ToFp32(program); + } if (!host_info.support_float16) { Optimization::LowerFp16ToFp32(program); } if (!host_info.support_int64) { Optimization::LowerInt64ToInt32(program); } + if (!host_info.support_conditional_barrier) { + Optimization::ConditionalBarrierPass(program); + } Optimization::SsaRewritePass(program); Optimization::ConstantPropagationPass(env, program); diff --git a/src/shader_recompiler/host_translate_info.h b/src/shader_recompiler/host_translate_info.h index 2aaa6c5eaa..7d2ded9070 100644 --- a/src/shader_recompiler/host_translate_info.h +++ b/src/shader_recompiler/host_translate_info.h @@ -10,6 +10,7 @@ namespace Shader { /// Misc information about the host struct HostTranslateInfo { + bool support_float64{}; ///< True when the device supports 64-bit floats bool support_float16{}; ///< True when the device supports 16-bit floats bool support_int64{}; ///< True when the device supports 64-bit integers bool needs_demote_reorder{}; ///< True when the device needs DemoteToHelperInvocation reordered @@ -17,6 +18,8 @@ struct HostTranslateInfo { bool support_viewport_index_layer{}; ///< True when the device supports gl_Layer in VS bool support_geometry_shader_passthrough{}; ///< True when the device supports geometry ///< passthrough shaders + bool support_conditional_barrier{}; ///< True when the device supports barriers in conditional + ///< control flow }; } // namespace Shader diff --git a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp index 5a41952175..70292686fc 100644 --- a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp +++ b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp @@ -424,6 +424,10 @@ void VisitUsages(Info& info, IR::Inst& inst) { info.used_constant_buffer_types |= IR::Type::U32 | IR::Type::U32x2; info.used_storage_buffer_types |= IR::Type::U32 | IR::Type::U32x2 | IR::Type::U32x4; break; + case IR::Opcode::LoadLocal: + case IR::Opcode::WriteLocal: + info.uses_local_memory = true; + break; default: break; } diff --git a/src/shader_recompiler/ir_opt/conditional_barrier_pass.cpp b/src/shader_recompiler/ir_opt/conditional_barrier_pass.cpp new file mode 100644 index 0000000000..c3ed27f4f2 --- /dev/null +++ b/src/shader_recompiler/ir_opt/conditional_barrier_pass.cpp @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "shader_recompiler/frontend/ir/program.h" +#include "shader_recompiler/ir_opt/passes.h" + +namespace Shader::Optimization { + +void ConditionalBarrierPass(IR::Program& program) { + s32 conditional_control_flow_count{0}; + s32 conditional_return_count{0}; + for (IR::AbstractSyntaxNode& node : program.syntax_list) { + switch (node.type) { + case IR::AbstractSyntaxNode::Type::If: + case IR::AbstractSyntaxNode::Type::Loop: + conditional_control_flow_count++; + break; + case IR::AbstractSyntaxNode::Type::EndIf: + case IR::AbstractSyntaxNode::Type::Repeat: + conditional_control_flow_count--; + break; + case IR::AbstractSyntaxNode::Type::Unreachable: + case IR::AbstractSyntaxNode::Type::Return: + if (conditional_control_flow_count > 0) { + conditional_return_count++; + } + break; + case IR::AbstractSyntaxNode::Type::Block: + for (IR::Inst& inst : node.data.block->Instructions()) { + if ((conditional_control_flow_count > 0 || conditional_return_count > 0) && + inst.GetOpcode() == IR::Opcode::Barrier) { + LOG_WARNING(Shader, "Barrier within conditional control flow"); + inst.ReplaceOpcode(IR::Opcode::Identity); + } + } + break; + default: + break; + } + } + ASSERT(conditional_control_flow_count == 0); +} + +} // namespace Shader::Optimization diff --git a/src/shader_recompiler/ir_opt/lower_fp64_to_fp32.cpp b/src/shader_recompiler/ir_opt/lower_fp64_to_fp32.cpp new file mode 100644 index 0000000000..5db7a38add --- /dev/null +++ b/src/shader_recompiler/ir_opt/lower_fp64_to_fp32.cpp @@ -0,0 +1,185 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "shader_recompiler/frontend/ir/ir_emitter.h" +#include "shader_recompiler/frontend/ir/opcodes.h" +#include "shader_recompiler/frontend/ir/value.h" +#include "shader_recompiler/ir_opt/passes.h" + +namespace Shader::Optimization { +namespace { + +constexpr s32 F64ToF32Exp = +1023 - 127; +constexpr s32 F32ToF64Exp = +127 - 1023; + +IR::F32 PackedF64ToF32(IR::IREmitter& ir, const IR::Value& packed) { + const IR::U32 lo{ir.CompositeExtract(packed, 0)}; + const IR::U32 hi{ir.CompositeExtract(packed, 1)}; + const IR::U32 sign{ir.BitFieldExtract(hi, ir.Imm32(31), ir.Imm32(1))}; + const IR::U32 exp{ir.BitFieldExtract(hi, ir.Imm32(20), ir.Imm32(11))}; + const IR::U32 mantissa_hi{ir.BitFieldExtract(hi, ir.Imm32(0), ir.Imm32(20))}; + const IR::U32 mantissa_lo{ir.BitFieldExtract(lo, ir.Imm32(29), ir.Imm32(3))}; + const IR::U32 mantissa{ + ir.BitwiseOr(ir.ShiftLeftLogical(mantissa_hi, ir.Imm32(3)), mantissa_lo)}; + const IR::U32 exp_if_subnorm{ + ir.Select(ir.IEqual(exp, ir.Imm32(0)), ir.Imm32(0), ir.IAdd(exp, ir.Imm32(F64ToF32Exp)))}; + const IR::U32 exp_if_infnan{ + ir.Select(ir.IEqual(exp, ir.Imm32(0x7ff)), ir.Imm32(0xff), exp_if_subnorm)}; + const IR::U32 result{ + ir.BitwiseOr(ir.ShiftLeftLogical(sign, ir.Imm32(31)), + ir.BitwiseOr(ir.ShiftLeftLogical(exp_if_infnan, ir.Imm32(23)), mantissa))}; + return ir.BitCast<IR::F32>(result); +} + +IR::Value F32ToPackedF64(IR::IREmitter& ir, const IR::Value& raw) { + const IR::U32 value{ir.BitCast<IR::U32>(IR::F32(raw))}; + const IR::U32 sign{ir.BitFieldExtract(value, ir.Imm32(31), ir.Imm32(1))}; + const IR::U32 exp{ir.BitFieldExtract(value, ir.Imm32(23), ir.Imm32(8))}; + const IR::U32 mantissa{ir.BitFieldExtract(value, ir.Imm32(0), ir.Imm32(23))}; + const IR::U32 mantissa_hi{ir.BitFieldExtract(mantissa, ir.Imm32(3), ir.Imm32(20))}; + const IR::U32 mantissa_lo{ir.BitFieldExtract(mantissa, ir.Imm32(0), ir.Imm32(3))}; + const IR::U32 exp_if_subnorm{ + ir.Select(ir.IEqual(exp, ir.Imm32(0)), ir.Imm32(0), ir.IAdd(exp, ir.Imm32(F32ToF64Exp)))}; + const IR::U32 exp_if_infnan{ + ir.Select(ir.IEqual(exp, ir.Imm32(0xff)), ir.Imm32(0x7ff), exp_if_subnorm)}; + const IR::U32 lo{ir.ShiftLeftLogical(mantissa_lo, ir.Imm32(29))}; + const IR::U32 hi{ + ir.BitwiseOr(ir.ShiftLeftLogical(sign, ir.Imm32(31)), + ir.BitwiseOr(ir.ShiftLeftLogical(exp_if_infnan, ir.Imm32(20)), mantissa_hi))}; + return ir.CompositeConstruct(lo, hi); +} + +IR::Opcode Replace(IR::Opcode op) { + switch (op) { + case IR::Opcode::FPAbs64: + return IR::Opcode::FPAbs32; + case IR::Opcode::FPAdd64: + return IR::Opcode::FPAdd32; + case IR::Opcode::FPCeil64: + return IR::Opcode::FPCeil32; + case IR::Opcode::FPFloor64: + return IR::Opcode::FPFloor32; + case IR::Opcode::FPFma64: + return IR::Opcode::FPFma32; + case IR::Opcode::FPMul64: + return IR::Opcode::FPMul32; + case IR::Opcode::FPNeg64: + return IR::Opcode::FPNeg32; + case IR::Opcode::FPRoundEven64: + return IR::Opcode::FPRoundEven32; + case IR::Opcode::FPSaturate64: + return IR::Opcode::FPSaturate32; + case IR::Opcode::FPClamp64: + return IR::Opcode::FPClamp32; + case IR::Opcode::FPTrunc64: + return IR::Opcode::FPTrunc32; + case IR::Opcode::CompositeConstructF64x2: + return IR::Opcode::CompositeConstructF32x2; + case IR::Opcode::CompositeConstructF64x3: + return IR::Opcode::CompositeConstructF32x3; + case IR::Opcode::CompositeConstructF64x4: + return IR::Opcode::CompositeConstructF32x4; + case IR::Opcode::CompositeExtractF64x2: + return IR::Opcode::CompositeExtractF32x2; + case IR::Opcode::CompositeExtractF64x3: + return IR::Opcode::CompositeExtractF32x3; + case IR::Opcode::CompositeExtractF64x4: + return IR::Opcode::CompositeExtractF32x4; + case IR::Opcode::CompositeInsertF64x2: + return IR::Opcode::CompositeInsertF32x2; + case IR::Opcode::CompositeInsertF64x3: + return IR::Opcode::CompositeInsertF32x3; + case IR::Opcode::CompositeInsertF64x4: + return IR::Opcode::CompositeInsertF32x4; + case IR::Opcode::FPOrdEqual64: + return IR::Opcode::FPOrdEqual32; + case IR::Opcode::FPUnordEqual64: + return IR::Opcode::FPUnordEqual32; + case IR::Opcode::FPOrdNotEqual64: + return IR::Opcode::FPOrdNotEqual32; + case IR::Opcode::FPUnordNotEqual64: + return IR::Opcode::FPUnordNotEqual32; + case IR::Opcode::FPOrdLessThan64: + return IR::Opcode::FPOrdLessThan32; + case IR::Opcode::FPUnordLessThan64: + return IR::Opcode::FPUnordLessThan32; + case IR::Opcode::FPOrdGreaterThan64: + return IR::Opcode::FPOrdGreaterThan32; + case IR::Opcode::FPUnordGreaterThan64: + return IR::Opcode::FPUnordGreaterThan32; + case IR::Opcode::FPOrdLessThanEqual64: + return IR::Opcode::FPOrdLessThanEqual32; + case IR::Opcode::FPUnordLessThanEqual64: + return IR::Opcode::FPUnordLessThanEqual32; + case IR::Opcode::FPOrdGreaterThanEqual64: + return IR::Opcode::FPOrdGreaterThanEqual32; + case IR::Opcode::FPUnordGreaterThanEqual64: + return IR::Opcode::FPUnordGreaterThanEqual32; + case IR::Opcode::FPIsNan64: + return IR::Opcode::FPIsNan32; + case IR::Opcode::ConvertS16F64: + return IR::Opcode::ConvertS16F32; + case IR::Opcode::ConvertS32F64: + return IR::Opcode::ConvertS32F32; + case IR::Opcode::ConvertS64F64: + return IR::Opcode::ConvertS64F32; + case IR::Opcode::ConvertU16F64: + return IR::Opcode::ConvertU16F32; + case IR::Opcode::ConvertU32F64: + return IR::Opcode::ConvertU32F32; + case IR::Opcode::ConvertU64F64: + return IR::Opcode::ConvertU64F32; + case IR::Opcode::ConvertF32F64: + return IR::Opcode::Identity; + case IR::Opcode::ConvertF64F32: + return IR::Opcode::Identity; + case IR::Opcode::ConvertF64S8: + return IR::Opcode::ConvertF32S8; + case IR::Opcode::ConvertF64S16: + return IR::Opcode::ConvertF32S16; + case IR::Opcode::ConvertF64S32: + return IR::Opcode::ConvertF32S32; + case IR::Opcode::ConvertF64S64: + return IR::Opcode::ConvertF32S64; + case IR::Opcode::ConvertF64U8: + return IR::Opcode::ConvertF32U8; + case IR::Opcode::ConvertF64U16: + return IR::Opcode::ConvertF32U16; + case IR::Opcode::ConvertF64U32: + return IR::Opcode::ConvertF32U32; + case IR::Opcode::ConvertF64U64: + return IR::Opcode::ConvertF32U64; + default: + return op; + } +} + +void Lower(IR::Block& block, IR::Inst& inst) { + switch (inst.GetOpcode()) { + case IR::Opcode::PackDouble2x32: { + IR::IREmitter ir(block, IR::Block::InstructionList::s_iterator_to(inst)); + inst.ReplaceUsesWith(PackedF64ToF32(ir, inst.Arg(0))); + break; + } + case IR::Opcode::UnpackDouble2x32: { + IR::IREmitter ir(block, IR::Block::InstructionList::s_iterator_to(inst)); + inst.ReplaceUsesWith(F32ToPackedF64(ir, inst.Arg(0))); + break; + } + default: + inst.ReplaceOpcode(Replace(inst.GetOpcode())); + break; + } +} + +} // Anonymous namespace + +void LowerFp64ToFp32(IR::Program& program) { + for (IR::Block* const block : program.blocks) { + for (IR::Inst& inst : block->Instructions()) { + Lower(*block, inst); + } + } +} + +} // namespace Shader::Optimization diff --git a/src/shader_recompiler/ir_opt/passes.h b/src/shader_recompiler/ir_opt/passes.h index 1f8f2ba95e..629d18fa19 100644 --- a/src/shader_recompiler/ir_opt/passes.h +++ b/src/shader_recompiler/ir_opt/passes.h @@ -13,10 +13,12 @@ struct HostTranslateInfo; namespace Shader::Optimization { void CollectShaderInfoPass(Environment& env, IR::Program& program); +void ConditionalBarrierPass(IR::Program& program); void ConstantPropagationPass(Environment& env, IR::Program& program); void DeadCodeEliminationPass(IR::Program& program); void GlobalMemoryToStorageBufferPass(IR::Program& program); void IdentityRemovalPass(IR::Program& program); +void LowerFp64ToFp32(IR::Program& program); void LowerFp16ToFp32(IR::Program& program); void LowerInt64ToInt32(IR::Program& program); void RescalingPass(IR::Program& program); diff --git a/src/shader_recompiler/runtime_info.h b/src/shader_recompiler/runtime_info.h index 3b63c249f5..619c0b1387 100644 --- a/src/shader_recompiler/runtime_info.h +++ b/src/shader_recompiler/runtime_info.h @@ -84,7 +84,8 @@ struct RuntimeInfo { bool glasm_use_storage_buffers{}; /// Transform feedback state for each varying - std::vector<TransformFeedbackVarying> xfb_varyings; + std::array<TransformFeedbackVarying, 256> xfb_varyings{}; + u32 xfb_count{0}; }; } // namespace Shader diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h index d308db9424..b4b4afd37d 100644 --- a/src/shader_recompiler/shader_info.h +++ b/src/shader_recompiler/shader_info.h @@ -172,6 +172,7 @@ struct Info { bool stores_indexed_attributes{}; bool stores_global_memory{}; + bool uses_local_memory{}; bool uses_fp16{}; bool uses_fp64{}; diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 251a4a8804..58a45ab67c 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -207,7 +207,7 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am if (has_new_downloads) { memory_tracker.MarkRegionAsGpuModified(*cpu_dest_address, amount); } - tmp_buffer.resize(amount); + tmp_buffer.resize_destructive(amount); cpu_memory.ReadBlockUnsafe(*cpu_src_address, tmp_buffer.data(), amount); cpu_memory.WriteBlockUnsafe(*cpu_dest_address, tmp_buffer.data(), amount); return true; @@ -715,13 +715,19 @@ void BufferCache<P>::BindHostIndexBuffer() { template <class P> void BufferCache<P>::BindHostVertexBuffers() { - HostBindings host_bindings; + HostBindings<typename P::Buffer> host_bindings; bool any_valid{false}; auto& flags = maxwell3d->dirty.flags; for (u32 index = 0; index < NUM_VERTEX_BUFFERS; ++index) { + const Binding& binding = channel_state->vertex_buffers[index]; + Buffer& buffer = slot_buffers[binding.buffer_id]; + TouchBuffer(buffer, binding.buffer_id); + SynchronizeBuffer(buffer, binding.cpu_addr, binding.size); if (!flags[Dirty::VertexBuffer0 + index]) { continue; } + flags[Dirty::VertexBuffer0 + index] = false; + host_bindings.min_index = std::min(host_bindings.min_index, index); host_bindings.max_index = std::max(host_bindings.max_index, index); any_valid = true; @@ -735,13 +741,10 @@ void BufferCache<P>::BindHostVertexBuffers() { const Binding& binding = channel_state->vertex_buffers[index]; Buffer& buffer = slot_buffers[binding.buffer_id]; - TouchBuffer(buffer, binding.buffer_id); - SynchronizeBuffer(buffer, binding.cpu_addr, binding.size); - const u32 stride = maxwell3d->regs.vertex_streams[index].stride; const u32 offset = buffer.Offset(binding.cpu_addr); - host_bindings.buffers.push_back(reinterpret_cast<void*>(&buffer)); + host_bindings.buffers.push_back(&buffer); host_bindings.offsets.push_back(offset); host_bindings.sizes.push_back(binding.size); host_bindings.strides.push_back(stride); @@ -900,7 +903,7 @@ void BufferCache<P>::BindHostTransformFeedbackBuffers() { if (maxwell3d->regs.transform_feedback_enabled == 0) { return; } - HostBindings host_bindings; + HostBindings<typename P::Buffer> host_bindings; for (u32 index = 0; index < NUM_TRANSFORM_FEEDBACK_BUFFERS; ++index) { const Binding& binding = channel_state->transform_feedback_buffers[index]; if (maxwell3d->regs.transform_feedback.controls[index].varying_count == 0 && @@ -913,7 +916,7 @@ void BufferCache<P>::BindHostTransformFeedbackBuffers() { SynchronizeBuffer(buffer, binding.cpu_addr, size); const u32 offset = buffer.Offset(binding.cpu_addr); - host_bindings.buffers.push_back(reinterpret_cast<void*>(&buffer)); + host_bindings.buffers.push_back(&buffer); host_bindings.offsets.push_back(offset); host_bindings.sizes.push_back(binding.size); } @@ -1276,7 +1279,7 @@ template <class P> typename BufferCache<P>::OverlapResult BufferCache<P>::ResolveOverlaps(VAddr cpu_addr, u32 wanted_size) { static constexpr int STREAM_LEAP_THRESHOLD = 16; - std::vector<BufferId> overlap_ids; + boost::container::small_vector<BufferId, 16> overlap_ids; VAddr begin = cpu_addr; VAddr end = cpu_addr + wanted_size; int stream_score = 0; diff --git a/src/video_core/buffer_cache/buffer_cache_base.h b/src/video_core/buffer_cache/buffer_cache_base.h index cf359e2413..fe6068cfee 100644 --- a/src/video_core/buffer_cache/buffer_cache_base.h +++ b/src/video_core/buffer_cache/buffer_cache_base.h @@ -105,8 +105,9 @@ static constexpr Binding NULL_BINDING{ .buffer_id = NULL_BUFFER_ID, }; +template <typename Buffer> struct HostBindings { - boost::container::small_vector<void*, NUM_VERTEX_BUFFERS> buffers; + boost::container::small_vector<Buffer*, NUM_VERTEX_BUFFERS> buffers; boost::container::small_vector<u64, NUM_VERTEX_BUFFERS> offsets; boost::container::small_vector<u64, NUM_VERTEX_BUFFERS> sizes; boost::container::small_vector<u64, NUM_VERTEX_BUFFERS> strides; @@ -228,7 +229,7 @@ class BufferCache : public VideoCommon::ChannelSetupCaches<BufferCacheChannelInf using OverlapCounter = boost::icl::split_interval_map<VAddr, int>; struct OverlapResult { - std::vector<BufferId> ids; + boost::container::small_vector<BufferId, 16> ids; VAddr begin; VAddr end; bool has_stream_leap = false; @@ -581,7 +582,7 @@ private: BufferId inline_buffer_id; std::array<BufferId, ((1ULL << 39) >> CACHING_PAGEBITS)> page_table; - std::vector<u8> tmp_buffer; + Common::ScratchBuffer<u8> tmp_buffer; }; } // namespace VideoCommon diff --git a/src/video_core/cdma_pusher.h b/src/video_core/cdma_pusher.h index 83112dfce4..7d660af47a 100644 --- a/src/video_core/cdma_pusher.h +++ b/src/video_core/cdma_pusher.h @@ -63,7 +63,6 @@ struct ChCommand { }; using ChCommandHeaderList = std::vector<ChCommandHeader>; -using ChCommandList = std::vector<ChCommand>; struct ThiRegisters { u32_le increment_syncpt{}; diff --git a/src/video_core/dma_pusher.h b/src/video_core/dma_pusher.h index 1cdb690ed5..8a2784cdca 100644 --- a/src/video_core/dma_pusher.h +++ b/src/video_core/dma_pusher.h @@ -6,6 +6,7 @@ #include <array> #include <span> #include <vector> +#include <boost/container/small_vector.hpp> #include <queue> #include "common/bit_field.h" @@ -102,11 +103,12 @@ inline CommandHeader BuildCommandHeader(BufferMethods method, u32 arg_count, Sub struct CommandList final { CommandList() = default; explicit CommandList(std::size_t size) : command_lists(size) {} - explicit CommandList(std::vector<CommandHeader>&& prefetch_command_list_) + explicit CommandList( + boost::container::small_vector<CommandHeader, 512>&& prefetch_command_list_) : prefetch_command_list{std::move(prefetch_command_list_)} {} - std::vector<CommandListHeader> command_lists; - std::vector<CommandHeader> prefetch_command_list; + boost::container::small_vector<CommandListHeader, 512> command_lists; + boost::container::small_vector<CommandHeader, 512> prefetch_command_list; }; /** diff --git a/src/video_core/engines/draw_manager.cpp b/src/video_core/engines/draw_manager.cpp index 0e94c521ac..f34090791c 100644 --- a/src/video_core/engines/draw_manager.cpp +++ b/src/video_core/engines/draw_manager.cpp @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "common/settings.h" #include "video_core/dirty_flags.h" #include "video_core/engines/draw_manager.h" #include "video_core/rasterizer_interface.h" @@ -195,8 +196,12 @@ void DrawManager::DrawTexture() { if (lower_left) { draw_texture_state.dst_y0 -= dst_height; } - draw_texture_state.dst_x1 = draw_texture_state.dst_x0 + dst_width; - draw_texture_state.dst_y1 = draw_texture_state.dst_y0 + dst_height; + draw_texture_state.dst_x1 = + draw_texture_state.dst_x0 + + static_cast<f32>(Settings::values.resolution_info.ScaleUp(static_cast<u32>(dst_width))); + draw_texture_state.dst_y1 = + draw_texture_state.dst_y0 + + static_cast<f32>(Settings::values.resolution_info.ScaleUp(static_cast<u32>(dst_height))); draw_texture_state.src_x0 = static_cast<float>(regs.draw_texture.src_x0) / 4096.f; draw_texture_state.src_y0 = static_cast<float>(regs.draw_texture.src_y0) / 4096.f; draw_texture_state.src_x1 = @@ -207,7 +212,6 @@ void DrawManager::DrawTexture() { draw_texture_state.src_y0; draw_texture_state.src_sampler = regs.draw_texture.src_sampler; draw_texture_state.src_texture = regs.draw_texture.src_texture; - maxwell3d->rasterizer->DrawTexture(); } diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp index ebe5536dec..bc1eb41e78 100644 --- a/src/video_core/engines/maxwell_dma.cpp +++ b/src/video_core/engines/maxwell_dma.cpp @@ -108,9 +108,11 @@ void MaxwellDMA::Launch() { if (regs.launch_dma.remap_enable != 0 && is_const_a_dst) { ASSERT(regs.remap_const.component_size_minus_one == 3); accelerate.BufferClear(regs.offset_out, regs.line_length_in, regs.remap_consta_value); - std::vector<u32> tmp_buffer(regs.line_length_in, regs.remap_consta_value); + read_buffer.resize_destructive(regs.line_length_in * sizeof(u32)); + std::span<u32> span(reinterpret_cast<u32*>(read_buffer.data()), regs.line_length_in); + std::ranges::fill(span, regs.remap_consta_value); memory_manager.WriteBlockUnsafe(regs.offset_out, - reinterpret_cast<u8*>(tmp_buffer.data()), + reinterpret_cast<u8*>(read_buffer.data()), regs.line_length_in * sizeof(u32)); } else { memory_manager.FlushCaching(); @@ -126,32 +128,32 @@ void MaxwellDMA::Launch() { UNIMPLEMENTED_IF(regs.line_length_in % 16 != 0); UNIMPLEMENTED_IF(regs.offset_in % 16 != 0); UNIMPLEMENTED_IF(regs.offset_out % 16 != 0); - std::vector<u8> tmp_buffer(16); + read_buffer.resize_destructive(16); for (u32 offset = 0; offset < regs.line_length_in; offset += 16) { memory_manager.ReadBlockUnsafe( convert_linear_2_blocklinear_addr(regs.offset_in + offset), - tmp_buffer.data(), tmp_buffer.size()); - memory_manager.WriteBlockCached(regs.offset_out + offset, tmp_buffer.data(), - tmp_buffer.size()); + read_buffer.data(), read_buffer.size()); + memory_manager.WriteBlockCached(regs.offset_out + offset, read_buffer.data(), + read_buffer.size()); } } else if (is_src_pitch && !is_dst_pitch) { UNIMPLEMENTED_IF(regs.line_length_in % 16 != 0); UNIMPLEMENTED_IF(regs.offset_in % 16 != 0); UNIMPLEMENTED_IF(regs.offset_out % 16 != 0); - std::vector<u8> tmp_buffer(16); + read_buffer.resize_destructive(16); for (u32 offset = 0; offset < regs.line_length_in; offset += 16) { - memory_manager.ReadBlockUnsafe(regs.offset_in + offset, tmp_buffer.data(), - tmp_buffer.size()); + memory_manager.ReadBlockUnsafe(regs.offset_in + offset, read_buffer.data(), + read_buffer.size()); memory_manager.WriteBlockCached( convert_linear_2_blocklinear_addr(regs.offset_out + offset), - tmp_buffer.data(), tmp_buffer.size()); + read_buffer.data(), read_buffer.size()); } } else { if (!accelerate.BufferCopy(regs.offset_in, regs.offset_out, regs.line_length_in)) { - std::vector<u8> tmp_buffer(regs.line_length_in); - memory_manager.ReadBlockUnsafe(regs.offset_in, tmp_buffer.data(), + read_buffer.resize_destructive(regs.line_length_in); + memory_manager.ReadBlockUnsafe(regs.offset_in, read_buffer.data(), regs.line_length_in); - memory_manager.WriteBlockCached(regs.offset_out, tmp_buffer.data(), + memory_manager.WriteBlockCached(regs.offset_out, read_buffer.data(), regs.line_length_in); } } @@ -171,7 +173,8 @@ void MaxwellDMA::CopyBlockLinearToPitch() { src_operand.address = regs.offset_in; DMA::BufferOperand dst_operand; - dst_operand.pitch = regs.pitch_out; + u32 abs_pitch_out = std::abs(static_cast<s32>(regs.pitch_out)); + dst_operand.pitch = abs_pitch_out; dst_operand.width = regs.line_length_in; dst_operand.height = regs.line_count; dst_operand.address = regs.offset_out; @@ -218,7 +221,7 @@ void MaxwellDMA::CopyBlockLinearToPitch() { const size_t src_size = CalculateSize(true, bytes_per_pixel, width, height, depth, block_height, block_depth); - const size_t dst_size = static_cast<size_t>(regs.pitch_out) * regs.line_count; + const size_t dst_size = static_cast<size_t>(abs_pitch_out) * regs.line_count; read_buffer.resize_destructive(src_size); write_buffer.resize_destructive(dst_size); @@ -227,7 +230,7 @@ void MaxwellDMA::CopyBlockLinearToPitch() { UnswizzleSubrect(write_buffer, read_buffer, bytes_per_pixel, width, height, depth, x_offset, src_params.origin.y, x_elements, regs.line_count, block_height, block_depth, - regs.pitch_out); + abs_pitch_out); memory_manager.WriteBlockCached(regs.offset_out, write_buffer.data(), dst_size); } diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 456f733cf3..db385076da 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -193,18 +193,13 @@ struct GPU::Impl { } [[nodiscard]] u64 GetTicks() const { - // This values were reversed engineered by fincs from NVN - // The gpu clock is reported in units of 385/625 nanoseconds - constexpr u64 gpu_ticks_num = 384; - constexpr u64 gpu_ticks_den = 625; + u64 gpu_tick = system.CoreTiming().GetGPUTicks(); - u64 nanoseconds = system.CoreTiming().GetCPUTimeNs().count(); if (Settings::values.use_fast_gpu_time.GetValue()) { - nanoseconds /= 256; + gpu_tick /= 256; } - const u64 nanoseconds_num = nanoseconds / gpu_ticks_den; - const u64 nanoseconds_rem = nanoseconds % gpu_ticks_den; - return nanoseconds_num * gpu_ticks_num + (nanoseconds_rem * gpu_ticks_num) / gpu_ticks_den; + + return gpu_tick; } [[nodiscard]] bool IsAsync() const { diff --git a/src/video_core/host1x/codecs/h264.cpp b/src/video_core/host1x/codecs/h264.cpp index 6ce179167c..ce827eb6c2 100644 --- a/src/video_core/host1x/codecs/h264.cpp +++ b/src/video_core/host1x/codecs/h264.cpp @@ -4,6 +4,7 @@ #include <array> #include <bit> +#include "common/scratch_buffer.h" #include "common/settings.h" #include "video_core/host1x/codecs/h264.h" #include "video_core/host1x/host1x.h" @@ -188,7 +189,8 @@ void H264BitWriter::WriteBit(bool state) { } void H264BitWriter::WriteScalingList(std::span<const u8> list, s32 start, s32 count) { - std::vector<u8> scan(count); + static Common::ScratchBuffer<u8> scan{}; + scan.resize_destructive(count); if (count == 16) { std::memcpy(scan.data(), zig_zag_scan.data(), scan.size()); } else { diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index 2442c3c294..e61d9af806 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -33,6 +33,7 @@ set(SHADER_FILES opengl_fidelityfx_fsr.frag opengl_fidelityfx_fsr_easu.frag opengl_fidelityfx_fsr_rcas.frag + opengl_lmem_warmup.comp opengl_present.frag opengl_present.vert opengl_present_scaleforce.frag diff --git a/src/video_core/host_shaders/opengl_lmem_warmup.comp b/src/video_core/host_shaders/opengl_lmem_warmup.comp new file mode 100644 index 0000000000..518268477a --- /dev/null +++ b/src/video_core/host_shaders/opengl_lmem_warmup.comp @@ -0,0 +1,47 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +// This shader is a workaround for a quirk in NVIDIA OpenGL drivers +// Shaders using local memory see a great performance benefit if a shader that was dispatched +// before it had more local memory allocated. +// This shader allocates the maximum local memory allowed on NVIDIA drivers to ensure that +// subsequent shaders see the performance boost. + +// NOTE: This shader does no actual meaningful work and returns immediately, +// it is simply a means to have the driver expect a shader using lots of local memory. + +#version 450 + +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(location = 0) uniform uint uniform_data; + +layout(binding = 0, rgba8) uniform writeonly restrict image2DArray dest_image; + +#define MAX_LMEM_SIZE 4080 // Size chosen to avoid errors in Nvidia's GLSL compiler +#define NUM_LMEM_CONSTANTS 1 +#define ARRAY_SIZE MAX_LMEM_SIZE - NUM_LMEM_CONSTANTS + +uint lmem_0[ARRAY_SIZE]; +const uvec4 constant_values[NUM_LMEM_CONSTANTS] = uvec4[](uvec4(0)); + +void main() { + const uint global_id = gl_GlobalInvocationID.x; + if (global_id <= 128) { + // Since the shader is called with a dispatch of 1x1x1 + // This should always be the case, and this shader will not actually execute + return; + } + for (uint t = 0; t < uniform_data; t++) { + const uint offset = (t * uniform_data); + lmem_0[offset] = t; + } + const uint offset = (gl_GlobalInvocationID.y * uniform_data + gl_GlobalInvocationID.x); + const uint value = lmem_0[offset]; + const uint const_value = constant_values[offset / 4][offset % 4]; + const uvec4 color = uvec4(value + const_value); + + // A "side-effect" is needed so the variables don't get optimized out, + // but this should never execute so there should be no clobbering of previously bound state. + imageStore(dest_image, ivec3(gl_GlobalInvocationID), color); +} diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index 7b2cde7a74..45141e4883 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp @@ -111,7 +111,7 @@ GPUVAddr MemoryManager::PageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cp [[maybe_unused]] const auto current_entry_type = GetEntry<false>(current_gpu_addr); SetEntry<false>(current_gpu_addr, entry_type); if (current_entry_type != entry_type) { - rasterizer->ModifyGPUMemory(unique_identifier, gpu_addr, page_size); + rasterizer->ModifyGPUMemory(unique_identifier, current_gpu_addr, page_size); } if constexpr (entry_type == EntryType::Mapped) { const VAddr current_cpu_addr = cpu_addr + offset; @@ -134,7 +134,7 @@ GPUVAddr MemoryManager::BigPageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr [[maybe_unused]] const auto current_entry_type = GetEntry<true>(current_gpu_addr); SetEntry<true>(current_gpu_addr, entry_type); if (current_entry_type != entry_type) { - rasterizer->ModifyGPUMemory(unique_identifier, gpu_addr, big_page_size); + rasterizer->ModifyGPUMemory(unique_identifier, current_gpu_addr, big_page_size); } if constexpr (entry_type == EntryType::Mapped) { const VAddr current_cpu_addr = cpu_addr + offset; @@ -587,7 +587,7 @@ void MemoryManager::InvalidateRegion(GPUVAddr gpu_addr, size_t size, void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size, VideoCommon::CacheType which) { - std::vector<u8> tmp_buffer(size); + tmp_buffer.resize_destructive(size); ReadBlock(gpu_src_addr, tmp_buffer.data(), size, which); // The output block must be flushed in case it has data modified from the GPU. @@ -670,9 +670,9 @@ bool MemoryManager::IsFullyMappedRange(GPUVAddr gpu_addr, std::size_t size) cons return result; } -std::vector<std::pair<GPUVAddr, std::size_t>> MemoryManager::GetSubmappedRange( - GPUVAddr gpu_addr, std::size_t size) const { - std::vector<std::pair<GPUVAddr, std::size_t>> result{}; +boost::container::small_vector<std::pair<GPUVAddr, std::size_t>, 32> +MemoryManager::GetSubmappedRange(GPUVAddr gpu_addr, std::size_t size) const { + boost::container::small_vector<std::pair<GPUVAddr, std::size_t>, 32> result{}; GetSubmappedRangeImpl<true>(gpu_addr, size, result); return result; } @@ -680,8 +680,9 @@ std::vector<std::pair<GPUVAddr, std::size_t>> MemoryManager::GetSubmappedRange( template <bool is_gpu_address> void MemoryManager::GetSubmappedRangeImpl( GPUVAddr gpu_addr, std::size_t size, - std::vector<std::pair<std::conditional_t<is_gpu_address, GPUVAddr, VAddr>, std::size_t>>& - result) const { + boost::container::small_vector< + std::pair<std::conditional_t<is_gpu_address, GPUVAddr, VAddr>, std::size_t>, 32>& result) + const { std::optional<std::pair<std::conditional_t<is_gpu_address, GPUVAddr, VAddr>, std::size_t>> last_segment{}; std::optional<VAddr> old_page_addr{}; diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h index 794535122e..4202c26ff1 100644 --- a/src/video_core/memory_manager.h +++ b/src/video_core/memory_manager.h @@ -8,10 +8,12 @@ #include <mutex> #include <optional> #include <vector> +#include <boost/container/small_vector.hpp> #include "common/common_types.h" #include "common/multi_level_page_table.h" #include "common/range_map.h" +#include "common/scratch_buffer.h" #include "common/virtual_buffer.h" #include "video_core/cache_types.h" #include "video_core/pte_kind.h" @@ -107,8 +109,8 @@ public: * if the region is continuous, a single pair will be returned. If it's unmapped, an empty * vector will be returned; */ - std::vector<std::pair<GPUVAddr, std::size_t>> GetSubmappedRange(GPUVAddr gpu_addr, - std::size_t size) const; + boost::container::small_vector<std::pair<GPUVAddr, std::size_t>, 32> GetSubmappedRange( + GPUVAddr gpu_addr, std::size_t size) const; GPUVAddr Map(GPUVAddr gpu_addr, VAddr cpu_addr, std::size_t size, PTEKind kind = PTEKind::INVALID, bool is_big_pages = true); @@ -165,7 +167,8 @@ private: template <bool is_gpu_address> void GetSubmappedRangeImpl( GPUVAddr gpu_addr, std::size_t size, - std::vector<std::pair<std::conditional_t<is_gpu_address, GPUVAddr, VAddr>, std::size_t>>& + boost::container::small_vector< + std::pair<std::conditional_t<is_gpu_address, GPUVAddr, VAddr>, std::size_t>, 32>& result) const; Core::System& system; @@ -215,8 +218,8 @@ private: Common::VirtualBuffer<u32> big_page_table_cpu; std::vector<u64> big_page_continuous; - std::vector<std::pair<VAddr, std::size_t>> page_stash{}; - std::vector<std::pair<VAddr, std::size_t>> page_stash2{}; + boost::container::small_vector<std::pair<VAddr, std::size_t>, 32> page_stash{}; + boost::container::small_vector<std::pair<VAddr, std::size_t>, 32> page_stash2{}; mutable std::mutex guard; @@ -226,6 +229,8 @@ private: std::unique_ptr<VideoCommon::InvalidationAccumulator> accumulator; static std::atomic<size_t> unique_identifier_generator; + + Common::ScratchBuffer<u8> tmp_buffer; }; } // namespace Tegra diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp index 0cc546a3a0..38d553d3c2 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp @@ -232,12 +232,12 @@ void BufferCacheRuntime::BindVertexBuffer(u32 index, Buffer& buffer, u32 offset, } } -void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings& bindings) { - for (u32 index = 0; index < bindings.buffers.size(); index++) { - BindVertexBuffer( - bindings.min_index + index, *reinterpret_cast<Buffer*>(bindings.buffers[index]), - static_cast<u32>(bindings.offsets[index]), static_cast<u32>(bindings.sizes[index]), - static_cast<u32>(bindings.strides[index])); +void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings) { + for (u32 index = 0; index < bindings.buffers.size(); ++index) { + BindVertexBuffer(bindings.min_index + index, *bindings.buffers[index], + static_cast<u32>(bindings.offsets[index]), + static_cast<u32>(bindings.sizes[index]), + static_cast<u32>(bindings.strides[index])); } } @@ -329,10 +329,9 @@ void BufferCacheRuntime::BindTransformFeedbackBuffer(u32 index, Buffer& buffer, static_cast<GLintptr>(offset), static_cast<GLsizeiptr>(size)); } -void BufferCacheRuntime::BindTransformFeedbackBuffers(VideoCommon::HostBindings& bindings) { - for (u32 index = 0; index < bindings.buffers.size(); index++) { - glBindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, index, - reinterpret_cast<Buffer*>(bindings.buffers[index])->Handle(), +void BufferCacheRuntime::BindTransformFeedbackBuffers(VideoCommon::HostBindings<Buffer>& bindings) { + for (u32 index = 0; index < bindings.buffers.size(); ++index) { + glBindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, index, bindings.buffers[index]->Handle(), static_cast<GLintptr>(bindings.offsets[index]), static_cast<GLsizeiptr>(bindings.sizes[index])); } diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h index e4e0002848..41b746f3bf 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.h +++ b/src/video_core/renderer_opengl/gl_buffer_cache.h @@ -87,7 +87,8 @@ public: void BindIndexBuffer(Buffer& buffer, u32 offset, u32 size); void BindVertexBuffer(u32 index, Buffer& buffer, u32 offset, u32 size, u32 stride); - void BindVertexBuffers(VideoCommon::HostBindings& bindings); + + void BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings); void BindUniformBuffer(size_t stage, u32 binding_index, Buffer& buffer, u32 offset, u32 size); @@ -100,7 +101,8 @@ public: bool is_written); void BindTransformFeedbackBuffer(u32 index, Buffer& buffer, u32 offset, u32 size); - void BindTransformFeedbackBuffers(VideoCommon::HostBindings& bindings); + + void BindTransformFeedbackBuffers(VideoCommon::HostBindings<Buffer>& bindings); void BindTextureBuffer(Buffer& buffer, u32 offset, u32 size, VideoCore::Surface::PixelFormat format); diff --git a/src/video_core/renderer_opengl/gl_compute_pipeline.cpp b/src/video_core/renderer_opengl/gl_compute_pipeline.cpp index 1a0cea9b78..f9ca55c36d 100644 --- a/src/video_core/renderer_opengl/gl_compute_pipeline.cpp +++ b/src/video_core/renderer_opengl/gl_compute_pipeline.cpp @@ -63,6 +63,7 @@ ComputePipeline::ComputePipeline(const Device& device, TextureCache& texture_cac writes_global_memory = !use_storage_buffers && std::ranges::any_of(info.storage_buffers_descriptors, [](const auto& desc) { return desc.is_written; }); + uses_local_memory = info.uses_local_memory; if (force_context_flush) { std::scoped_lock lock{built_mutex}; built_fence.Create(); @@ -87,7 +88,8 @@ void ComputePipeline::Configure() { texture_cache.SynchronizeComputeDescriptors(); boost::container::static_vector<VideoCommon::ImageViewInOut, MAX_TEXTURES + MAX_IMAGES> views; - std::array<GLuint, MAX_TEXTURES> samplers; + boost::container::static_vector<VideoCommon::SamplerId, MAX_TEXTURES> samplers; + std::array<GLuint, MAX_TEXTURES> gl_samplers; std::array<GLuint, MAX_TEXTURES> textures; std::array<GLuint, MAX_IMAGES> images; GLsizei sampler_binding{}; @@ -131,7 +133,6 @@ void ComputePipeline::Configure() { for (u32 index = 0; index < desc.count; ++index) { const auto handle{read_handle(desc, index)}; views.push_back({handle.first}); - samplers[sampler_binding++] = 0; } } for (const auto& desc : info.image_buffer_descriptors) { @@ -142,8 +143,8 @@ void ComputePipeline::Configure() { const auto handle{read_handle(desc, index)}; views.push_back({handle.first}); - Sampler* const sampler = texture_cache.GetComputeSampler(handle.second); - samplers[sampler_binding++] = sampler->Handle(); + VideoCommon::SamplerId sampler = texture_cache.GetComputeSamplerId(handle.second); + samplers.push_back(sampler); } } for (const auto& desc : info.image_descriptors) { @@ -186,10 +187,17 @@ void ComputePipeline::Configure() { const VideoCommon::ImageViewInOut* views_it{views.data() + num_texture_buffers + num_image_buffers}; + const VideoCommon::SamplerId* samplers_it{samplers.data()}; texture_binding += num_texture_buffers; image_binding += num_image_buffers; u32 texture_scaling_mask{}; + + for (const auto& desc : info.texture_buffer_descriptors) { + for (u32 index = 0; index < desc.count; ++index) { + gl_samplers[sampler_binding++] = 0; + } + } for (const auto& desc : info.texture_descriptors) { for (u32 index = 0; index < desc.count; ++index) { ImageView& image_view{texture_cache.GetImageView((views_it++)->id)}; @@ -198,6 +206,12 @@ void ComputePipeline::Configure() { texture_scaling_mask |= 1u << texture_binding; } ++texture_binding; + + const Sampler& sampler{texture_cache.GetSampler(*(samplers_it++))}; + const bool use_fallback_sampler{sampler.HasAddedAnisotropy() && + !image_view.SupportsAnisotropy()}; + gl_samplers[sampler_binding++] = + use_fallback_sampler ? sampler.HandleWithDefaultAnisotropy() : sampler.Handle(); } } u32 image_scaling_mask{}; @@ -228,7 +242,7 @@ void ComputePipeline::Configure() { if (texture_binding != 0) { ASSERT(texture_binding == sampler_binding); glBindTextures(0, texture_binding, textures.data()); - glBindSamplers(0, sampler_binding, samplers.data()); + glBindSamplers(0, sampler_binding, gl_samplers.data()); } if (image_binding != 0) { glBindImageTextures(0, image_binding, images.data()); diff --git a/src/video_core/renderer_opengl/gl_compute_pipeline.h b/src/video_core/renderer_opengl/gl_compute_pipeline.h index 9bcc72b59e..c26b4fa5e2 100644 --- a/src/video_core/renderer_opengl/gl_compute_pipeline.h +++ b/src/video_core/renderer_opengl/gl_compute_pipeline.h @@ -59,6 +59,10 @@ public: return writes_global_memory; } + [[nodiscard]] bool UsesLocalMemory() const noexcept { + return uses_local_memory; + } + void SetEngine(Tegra::Engines::KeplerCompute* kepler_compute_, Tegra::MemoryManager* gpu_memory_) { kepler_compute = kepler_compute_; @@ -84,6 +88,7 @@ private: bool use_storage_buffers{}; bool writes_global_memory{}; + bool uses_local_memory{}; std::mutex built_mutex; std::condition_variable built_condvar; diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp index 400c219814..33e63c17d7 100644 --- a/src/video_core/renderer_opengl/gl_device.cpp +++ b/src/video_core/renderer_opengl/gl_device.cpp @@ -194,6 +194,7 @@ Device::Device(Core::Frontend::EmuWindow& emu_window) { has_bool_ref_bug = true; } } + has_lmem_perf_bug = is_nvidia; strict_context_required = emu_window.StrictContextRequired(); // Blocks AMD and Intel OpenGL drivers on Windows from using asynchronous shader compilation. @@ -201,6 +202,7 @@ Device::Device(Core::Frontend::EmuWindow& emu_window) { use_asynchronous_shaders = Settings::values.use_asynchronous_shaders.GetValue() && !(is_amd || (is_intel && !is_linux)) && !strict_context_required; use_driver_cache = is_nvidia; + supports_conditional_barriers = !is_intel; LOG_INFO(Render_OpenGL, "Renderer_VariableAOFFI: {}", has_variable_aoffi); LOG_INFO(Render_OpenGL, "Renderer_ComponentIndexingBug: {}", has_component_indexing_bug); diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h index cc0b95f1a5..a5a6bbbba7 100644 --- a/src/video_core/renderer_opengl/gl_device.h +++ b/src/video_core/renderer_opengl/gl_device.h @@ -188,6 +188,14 @@ public: return strict_context_required; } + bool SupportsConditionalBarriers() const { + return supports_conditional_barriers; + } + + bool HasLmemPerfBug() const { + return has_lmem_perf_bug; + } + private: static bool TestVariableAoffi(); static bool TestPreciseBug(); @@ -233,6 +241,8 @@ private: bool has_bool_ref_bug{}; bool can_report_memory{}; bool strict_context_required{}; + bool supports_conditional_barriers{}; + bool has_lmem_perf_bug{}; std::string vendor_name; }; diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp index 89000d6e01..23a48c6fe7 100644 --- a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp +++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp @@ -215,6 +215,7 @@ GraphicsPipeline::GraphicsPipeline(const Device& device, TextureCache& texture_c writes_global_memory |= std::ranges::any_of( info.storage_buffers_descriptors, [](const auto& desc) { return desc.is_written; }); + uses_local_memory |= info.uses_local_memory; } ASSERT(num_textures <= MAX_TEXTURES); ASSERT(num_images <= MAX_IMAGES); @@ -275,9 +276,9 @@ GraphicsPipeline::GraphicsPipeline(const Device& device, TextureCache& texture_c template <typename Spec> void GraphicsPipeline::ConfigureImpl(bool is_indexed) { std::array<VideoCommon::ImageViewInOut, MAX_TEXTURES + MAX_IMAGES> views; - std::array<GLuint, MAX_TEXTURES> samplers; + std::array<VideoCommon::SamplerId, MAX_TEXTURES> samplers; size_t views_index{}; - GLsizei sampler_binding{}; + size_t samplers_index{}; texture_cache.SynchronizeGraphicsDescriptors(); @@ -337,7 +338,6 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { for (u32 index = 0; index < desc.count; ++index) { const auto handle{read_handle(desc, index)}; views[views_index++] = {handle.first}; - samplers[sampler_binding++] = 0; } } } @@ -351,8 +351,8 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { const auto handle{read_handle(desc, index)}; views[views_index++] = {handle.first}; - Sampler* const sampler{texture_cache.GetGraphicsSampler(handle.second)}; - samplers[sampler_binding++] = sampler->Handle(); + VideoCommon::SamplerId sampler{texture_cache.GetGraphicsSamplerId(handle.second)}; + samplers[samplers_index++] = sampler; } } if constexpr (Spec::has_images) { @@ -445,10 +445,13 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { program_manager.BindSourcePrograms(source_programs); } const VideoCommon::ImageViewInOut* views_it{views.data()}; + const VideoCommon::SamplerId* samplers_it{samplers.data()}; GLsizei texture_binding = 0; GLsizei image_binding = 0; + GLsizei sampler_binding{}; std::array<GLuint, MAX_TEXTURES> textures; std::array<GLuint, MAX_IMAGES> images; + std::array<GLuint, MAX_TEXTURES> gl_samplers; const auto prepare_stage{[&](size_t stage) { buffer_cache.runtime.SetImagePointers(&textures[texture_binding], &images[image_binding]); buffer_cache.BindHostStageBuffers(stage); @@ -465,6 +468,13 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { u32 stage_image_binding{}; const auto& info{stage_infos[stage]}; + if constexpr (Spec::has_texture_buffers) { + for (const auto& desc : info.texture_buffer_descriptors) { + for (u32 index = 0; index < desc.count; ++index) { + gl_samplers[sampler_binding++] = 0; + } + } + } for (const auto& desc : info.texture_descriptors) { for (u32 index = 0; index < desc.count; ++index) { ImageView& image_view{texture_cache.GetImageView((views_it++)->id)}; @@ -474,6 +484,12 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { } ++texture_binding; ++stage_texture_binding; + + const Sampler& sampler{texture_cache.GetSampler(*(samplers_it++))}; + const bool use_fallback_sampler{sampler.HasAddedAnisotropy() && + !image_view.SupportsAnisotropy()}; + gl_samplers[sampler_binding++] = + use_fallback_sampler ? sampler.HandleWithDefaultAnisotropy() : sampler.Handle(); } } for (const auto& desc : info.image_descriptors) { @@ -534,7 +550,7 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { if (texture_binding != 0) { ASSERT(texture_binding == sampler_binding); glBindTextures(0, texture_binding, textures.data()); - glBindSamplers(0, sampler_binding, samplers.data()); + glBindSamplers(0, sampler_binding, gl_samplers.data()); } if (image_binding != 0) { glBindImageTextures(0, image_binding, images.data()); diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.h b/src/video_core/renderer_opengl/gl_graphics_pipeline.h index 7bab3be0a7..7b3d7eae81 100644 --- a/src/video_core/renderer_opengl/gl_graphics_pipeline.h +++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.h @@ -98,6 +98,10 @@ public: return writes_global_memory; } + [[nodiscard]] bool UsesLocalMemory() const noexcept { + return uses_local_memory; + } + [[nodiscard]] bool IsBuilt() noexcept; template <typename Spec> @@ -146,6 +150,7 @@ private: bool use_storage_buffers{}; bool writes_global_memory{}; + bool uses_local_memory{}; static constexpr std::size_t XFB_ENTRY_STRIDE = 3; GLsizei num_xfb_attribs{}; diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index fc711c44ae..edf527f2d3 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -222,6 +222,9 @@ void RasterizerOpenGL::PrepareDraw(bool is_indexed, Func&& draw_func) { gpu.TickWork(); std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; + if (pipeline->UsesLocalMemory()) { + program_manager.LocalMemoryWarmup(); + } pipeline->SetEngine(maxwell3d, gpu_memory); pipeline->Configure(is_indexed); @@ -371,6 +374,9 @@ void RasterizerOpenGL::DispatchCompute() { if (!pipeline) { return; } + if (pipeline->UsesLocalMemory()) { + program_manager.LocalMemoryWarmup(); + } pipeline->SetEngine(kepler_compute, gpu_memory); pipeline->Configure(); const auto& qmd{kepler_compute->launch_description}; diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 6ecda29842..0329ed8203 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -85,7 +85,9 @@ Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key, case Shader::Stage::VertexB: case Shader::Stage::Geometry: if (!use_assembly_shaders && key.xfb_enabled != 0) { - info.xfb_varyings = VideoCommon::MakeTransformFeedbackVaryings(key.xfb_state); + auto [varyings, count] = VideoCommon::MakeTransformFeedbackVaryings(key.xfb_state); + info.xfb_varyings = varyings; + info.xfb_count = count; } break; case Shader::Stage::TessellationEval: @@ -232,12 +234,14 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo .gl_max_compute_smem_size = device.GetMaxComputeSharedMemorySize(), }, host_info{ + .support_float64 = true, .support_float16 = false, .support_int64 = device.HasShaderInt64(), .needs_demote_reorder = device.IsAmd(), .support_snorm_render_buffer = false, .support_viewport_index_layer = device.HasVertexViewportLayer(), .support_geometry_shader_passthrough = device.HasGeometryShaderPassthrough(), + .support_conditional_barrier = device.SupportsConditionalBarriers(), } { if (use_asynchronous_shaders) { workers = CreateWorkers(); diff --git a/src/video_core/renderer_opengl/gl_shader_context.h b/src/video_core/renderer_opengl/gl_shader_context.h index 207a75d42d..d12cd06fa7 100644 --- a/src/video_core/renderer_opengl/gl_shader_context.h +++ b/src/video_core/renderer_opengl/gl_shader_context.h @@ -16,9 +16,9 @@ struct ShaderPools { inst.ReleaseContents(); } - Shader::ObjectPool<Shader::IR::Inst> inst; - Shader::ObjectPool<Shader::IR::Block> block; - Shader::ObjectPool<Shader::Maxwell::Flow::Block> flow_block; + Shader::ObjectPool<Shader::IR::Inst> inst{8192}; + Shader::ObjectPool<Shader::IR::Block> block{32}; + Shader::ObjectPool<Shader::Maxwell::Flow::Block> flow_block{32}; }; struct Context { diff --git a/src/video_core/renderer_opengl/gl_shader_manager.cpp b/src/video_core/renderer_opengl/gl_shader_manager.cpp index 98841ae65e..03d4b9d061 100644 --- a/src/video_core/renderer_opengl/gl_shader_manager.cpp +++ b/src/video_core/renderer_opengl/gl_shader_manager.cpp @@ -3,7 +3,9 @@ #include <glad/glad.h> +#include "video_core/host_shaders/opengl_lmem_warmup_comp.h" #include "video_core/renderer_opengl/gl_shader_manager.h" +#include "video_core/renderer_opengl/gl_shader_util.h" namespace OpenGL { @@ -17,6 +19,10 @@ ProgramManager::ProgramManager(const Device& device) { if (device.UseAssemblyShaders()) { glEnable(GL_COMPUTE_PROGRAM_NV); } + if (device.HasLmemPerfBug()) { + lmem_warmup_program = + CreateProgram(HostShaders::OPENGL_LMEM_WARMUP_COMP, GL_COMPUTE_SHADER); + } } void ProgramManager::BindComputeProgram(GLuint program) { @@ -98,6 +104,13 @@ void ProgramManager::BindAssemblyPrograms(std::span<const OGLAssemblyProgram, NU void ProgramManager::RestoreGuestCompute() {} +void ProgramManager::LocalMemoryWarmup() { + if (lmem_warmup_program.handle != 0) { + BindComputeProgram(lmem_warmup_program.handle); + glDispatchCompute(1, 1, 1); + } +} + void ProgramManager::BindPipeline() { if (!is_pipeline_bound) { is_pipeline_bound = true; diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h index 07ffab77f1..852d8c88e8 100644 --- a/src/video_core/renderer_opengl/gl_shader_manager.h +++ b/src/video_core/renderer_opengl/gl_shader_manager.h @@ -30,6 +30,8 @@ public: void RestoreGuestCompute(); + void LocalMemoryWarmup(); + private: void BindPipeline(); @@ -44,6 +46,7 @@ private: u32 current_stage_mask = 0; std::array<GLuint, NUM_STAGES> current_programs{}; GLuint current_assembly_compute_program = 0; + OGLProgram lmem_warmup_program; }; } // namespace OpenGL diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp index 1c5dbcdd88..3b446be074 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.cpp +++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp @@ -1268,36 +1268,48 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const TSCEntry& config) { UNIMPLEMENTED_IF(config.cubemap_anisotropy != 1); - sampler.Create(); - const GLuint handle = sampler.handle; - glSamplerParameteri(handle, GL_TEXTURE_WRAP_S, MaxwellToGL::WrapMode(config.wrap_u)); - glSamplerParameteri(handle, GL_TEXTURE_WRAP_T, MaxwellToGL::WrapMode(config.wrap_v)); - glSamplerParameteri(handle, GL_TEXTURE_WRAP_R, MaxwellToGL::WrapMode(config.wrap_p)); - glSamplerParameteri(handle, GL_TEXTURE_COMPARE_MODE, compare_mode); - glSamplerParameteri(handle, GL_TEXTURE_COMPARE_FUNC, compare_func); - glSamplerParameteri(handle, GL_TEXTURE_MAG_FILTER, mag); - glSamplerParameteri(handle, GL_TEXTURE_MIN_FILTER, min); - glSamplerParameterf(handle, GL_TEXTURE_LOD_BIAS, config.LodBias()); - glSamplerParameterf(handle, GL_TEXTURE_MIN_LOD, config.MinLod()); - glSamplerParameterf(handle, GL_TEXTURE_MAX_LOD, config.MaxLod()); - glSamplerParameterfv(handle, GL_TEXTURE_BORDER_COLOR, config.BorderColor().data()); - - if (GLAD_GL_ARB_texture_filter_anisotropic || GLAD_GL_EXT_texture_filter_anisotropic) { - const f32 max_anisotropy = std::clamp(config.MaxAnisotropy(), 1.0f, 16.0f); - glSamplerParameterf(handle, GL_TEXTURE_MAX_ANISOTROPY, max_anisotropy); - } else { - LOG_WARNING(Render_OpenGL, "GL_ARB_texture_filter_anisotropic is required"); - } - if (GLAD_GL_ARB_texture_filter_minmax || GLAD_GL_EXT_texture_filter_minmax) { - glSamplerParameteri(handle, GL_TEXTURE_REDUCTION_MODE_ARB, reduction_filter); - } else if (reduction_filter != GL_WEIGHTED_AVERAGE_ARB) { - LOG_WARNING(Render_OpenGL, "GL_ARB_texture_filter_minmax is required"); - } - if (GLAD_GL_ARB_seamless_cubemap_per_texture || GLAD_GL_AMD_seamless_cubemap_per_texture) { - glSamplerParameteri(handle, GL_TEXTURE_CUBE_MAP_SEAMLESS, seamless); - } else if (seamless == GL_FALSE) { - // We default to false because it's more common - LOG_WARNING(Render_OpenGL, "GL_ARB_seamless_cubemap_per_texture is required"); + const f32 max_anisotropy = std::clamp(config.MaxAnisotropy(), 1.0f, 16.0f); + + const auto create_sampler = [&](const f32 anisotropy) { + OGLSampler new_sampler; + new_sampler.Create(); + const GLuint handle = new_sampler.handle; + glSamplerParameteri(handle, GL_TEXTURE_WRAP_S, MaxwellToGL::WrapMode(config.wrap_u)); + glSamplerParameteri(handle, GL_TEXTURE_WRAP_T, MaxwellToGL::WrapMode(config.wrap_v)); + glSamplerParameteri(handle, GL_TEXTURE_WRAP_R, MaxwellToGL::WrapMode(config.wrap_p)); + glSamplerParameteri(handle, GL_TEXTURE_COMPARE_MODE, compare_mode); + glSamplerParameteri(handle, GL_TEXTURE_COMPARE_FUNC, compare_func); + glSamplerParameteri(handle, GL_TEXTURE_MAG_FILTER, mag); + glSamplerParameteri(handle, GL_TEXTURE_MIN_FILTER, min); + glSamplerParameterf(handle, GL_TEXTURE_LOD_BIAS, config.LodBias()); + glSamplerParameterf(handle, GL_TEXTURE_MIN_LOD, config.MinLod()); + glSamplerParameterf(handle, GL_TEXTURE_MAX_LOD, config.MaxLod()); + glSamplerParameterfv(handle, GL_TEXTURE_BORDER_COLOR, config.BorderColor().data()); + + if (GLAD_GL_ARB_texture_filter_anisotropic || GLAD_GL_EXT_texture_filter_anisotropic) { + glSamplerParameterf(handle, GL_TEXTURE_MAX_ANISOTROPY, anisotropy); + } else { + LOG_WARNING(Render_OpenGL, "GL_ARB_texture_filter_anisotropic is required"); + } + if (GLAD_GL_ARB_texture_filter_minmax || GLAD_GL_EXT_texture_filter_minmax) { + glSamplerParameteri(handle, GL_TEXTURE_REDUCTION_MODE_ARB, reduction_filter); + } else if (reduction_filter != GL_WEIGHTED_AVERAGE_ARB) { + LOG_WARNING(Render_OpenGL, "GL_ARB_texture_filter_minmax is required"); + } + if (GLAD_GL_ARB_seamless_cubemap_per_texture || GLAD_GL_AMD_seamless_cubemap_per_texture) { + glSamplerParameteri(handle, GL_TEXTURE_CUBE_MAP_SEAMLESS, seamless); + } else if (seamless == GL_FALSE) { + // We default to false because it's more common + LOG_WARNING(Render_OpenGL, "GL_ARB_seamless_cubemap_per_texture is required"); + } + return new_sampler; + }; + + sampler = create_sampler(max_anisotropy); + + const f32 max_anisotropy_default = static_cast<f32>(1U << config.max_anisotropy); + if (max_anisotropy > max_anisotropy_default) { + sampler_default_anisotropy = create_sampler(max_anisotropy_default); } } diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h index 1148b73d70..3676eaaa96 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.h +++ b/src/video_core/renderer_opengl/gl_texture_cache.h @@ -309,12 +309,21 @@ class Sampler { public: explicit Sampler(TextureCacheRuntime&, const Tegra::Texture::TSCEntry&); - GLuint Handle() const noexcept { + [[nodiscard]] GLuint Handle() const noexcept { return sampler.handle; } + [[nodiscard]] GLuint HandleWithDefaultAnisotropy() const noexcept { + return sampler_default_anisotropy.handle; + } + + [[nodiscard]] bool HasAddedAnisotropy() const noexcept { + return static_cast<bool>(sampler_default_anisotropy.handle); + } + private: OGLSampler sampler; + OGLSampler sampler_default_anisotropy; }; class Framebuffer { diff --git a/src/video_core/renderer_vulkan/pipeline_helper.h b/src/video_core/renderer_vulkan/pipeline_helper.h index 983e1c2e11..71c783709c 100644 --- a/src/video_core/renderer_vulkan/pipeline_helper.h +++ b/src/video_core/renderer_vulkan/pipeline_helper.h @@ -178,7 +178,7 @@ public: inline void PushImageDescriptors(TextureCache& texture_cache, GuestDescriptorQueue& guest_descriptor_queue, const Shader::Info& info, RescalingPushConstant& rescaling, - const VkSampler*& samplers, + const VideoCommon::SamplerId*& samplers, const VideoCommon::ImageViewInOut*& views) { const u32 num_texture_buffers = Shader::NumDescriptors(info.texture_buffer_descriptors); const u32 num_image_buffers = Shader::NumDescriptors(info.image_buffer_descriptors); @@ -187,10 +187,15 @@ inline void PushImageDescriptors(TextureCache& texture_cache, for (const auto& desc : info.texture_descriptors) { for (u32 index = 0; index < desc.count; ++index) { const VideoCommon::ImageViewId image_view_id{(views++)->id}; - const VkSampler sampler{*(samplers++)}; + const VideoCommon::SamplerId sampler_id{*(samplers++)}; ImageView& image_view{texture_cache.GetImageView(image_view_id)}; const VkImageView vk_image_view{image_view.Handle(desc.type)}; - guest_descriptor_queue.AddSampledImage(vk_image_view, sampler); + const Sampler& sampler{texture_cache.GetSampler(sampler_id)}; + const bool use_fallback_sampler{sampler.HasAddedAnisotropy() && + !image_view.SupportsAnisotropy()}; + const VkSampler vk_sampler{use_fallback_sampler ? sampler.HandleWithDefaultAnisotropy() + : sampler.Handle()}; + guest_descriptor_queue.AddSampledImage(vk_image_view, vk_sampler); rescaling.PushTexture(texture_cache.IsRescaling(image_view)); } } diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index d72d99899b..f47301ad5a 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp @@ -361,7 +361,7 @@ void BufferCacheRuntime::CopyBuffer(VkBuffer dst_buffer, VkBuffer src_buffer, .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, }; // Measuring a popular game, this number never exceeds the specified size once data is warmed up - boost::container::small_vector<VkBufferCopy, 3> vk_copies(copies.size()); + boost::container::small_vector<VkBufferCopy, 8> vk_copies(copies.size()); std::ranges::transform(copies, vk_copies.begin(), MakeBufferCopy); scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([src_buffer, dst_buffer, vk_copies, barrier](vk::CommandBuffer cmdbuf) { @@ -501,11 +501,10 @@ void BufferCacheRuntime::BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset } } -void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings& bindings) { +void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings) { boost::container::small_vector<VkBuffer, 32> buffer_handles; - for (u32 index = 0; index < bindings.buffers.size(); index++) { - auto& buffer = *reinterpret_cast<Buffer*>(bindings.buffers[index]); - auto handle = buffer.Handle(); + for (u32 index = 0; index < bindings.buffers.size(); ++index) { + auto handle = bindings.buffers[index]->Handle(); if (handle == VK_NULL_HANDLE) { bindings.offsets[index] = 0; bindings.sizes[index] = VK_WHOLE_SIZE; @@ -517,20 +516,17 @@ void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings& bindings) buffer_handles.push_back(handle); } if (device.IsExtExtendedDynamicStateSupported()) { - scheduler.Record([bindings = bindings, - buffer_handles = buffer_handles](vk::CommandBuffer cmdbuf) { + scheduler.Record([bindings = std::move(bindings), + buffer_handles = std::move(buffer_handles)](vk::CommandBuffer cmdbuf) { cmdbuf.BindVertexBuffers2EXT( bindings.min_index, bindings.max_index - bindings.min_index, buffer_handles.data(), - reinterpret_cast<const VkDeviceSize*>(bindings.offsets.data()), - reinterpret_cast<const VkDeviceSize*>(bindings.sizes.data()), - reinterpret_cast<const VkDeviceSize*>(bindings.strides.data())); + bindings.offsets.data(), bindings.sizes.data(), bindings.strides.data()); }); } else { - scheduler.Record([bindings = bindings, - buffer_handles = buffer_handles](vk::CommandBuffer cmdbuf) { - cmdbuf.BindVertexBuffers( - bindings.min_index, bindings.max_index - bindings.min_index, buffer_handles.data(), - reinterpret_cast<const VkDeviceSize*>(bindings.offsets.data())); + scheduler.Record([bindings = std::move(bindings), + buffer_handles = std::move(buffer_handles)](vk::CommandBuffer cmdbuf) { + cmdbuf.BindVertexBuffers(bindings.min_index, bindings.max_index - bindings.min_index, + buffer_handles.data(), bindings.offsets.data()); }); } } @@ -556,23 +552,21 @@ void BufferCacheRuntime::BindTransformFeedbackBuffer(u32 index, VkBuffer buffer, }); } -void BufferCacheRuntime::BindTransformFeedbackBuffers(VideoCommon::HostBindings& bindings) { +void BufferCacheRuntime::BindTransformFeedbackBuffers(VideoCommon::HostBindings<Buffer>& bindings) { if (!device.IsExtTransformFeedbackSupported()) { // Already logged in the rasterizer return; } boost::container::small_vector<VkBuffer, 4> buffer_handles; - for (u32 index = 0; index < bindings.buffers.size(); index++) { - auto& buffer = *reinterpret_cast<Buffer*>(bindings.buffers[index]); - buffer_handles.push_back(buffer.Handle()); - } - scheduler.Record( - [bindings = bindings, buffer_handles = buffer_handles](vk::CommandBuffer cmdbuf) { - cmdbuf.BindTransformFeedbackBuffersEXT( - 0, static_cast<u32>(buffer_handles.size()), buffer_handles.data(), - reinterpret_cast<const VkDeviceSize*>(bindings.offsets.data()), - reinterpret_cast<const VkDeviceSize*>(bindings.sizes.data())); - }); + for (u32 index = 0; index < bindings.buffers.size(); ++index) { + buffer_handles.push_back(bindings.buffers[index]->Handle()); + } + scheduler.Record([bindings = std::move(bindings), + buffer_handles = std::move(buffer_handles)](vk::CommandBuffer cmdbuf) { + cmdbuf.BindTransformFeedbackBuffersEXT(0, static_cast<u32>(buffer_handles.size()), + buffer_handles.data(), bindings.offsets.data(), + bindings.sizes.data()); + }); } void BufferCacheRuntime::ReserveNullBuffer() { diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h index 92d3e9f323..cdeef88464 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.h +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h @@ -97,10 +97,12 @@ public: void BindQuadIndexBuffer(PrimitiveTopology topology, u32 first, u32 count); void BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size, u32 stride); - void BindVertexBuffers(VideoCommon::HostBindings& bindings); + + void BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings); void BindTransformFeedbackBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size); - void BindTransformFeedbackBuffers(VideoCommon::HostBindings& bindings); + + void BindTransformFeedbackBuffers(VideoCommon::HostBindings<Buffer>& bindings); std::span<u8> BindMappedUniformBuffer([[maybe_unused]] size_t stage, [[maybe_unused]] u32 binding_index, u32 size) { diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp index 733e70d9d2..73e585c2b7 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp @@ -115,7 +115,7 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute, static constexpr size_t max_elements = 64; boost::container::static_vector<VideoCommon::ImageViewInOut, max_elements> views; - boost::container::static_vector<VkSampler, max_elements> samplers; + boost::container::static_vector<VideoCommon::SamplerId, max_elements> samplers; const auto& qmd{kepler_compute.launch_description}; const auto& cbufs{qmd.const_buffer_config}; @@ -160,8 +160,8 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute, const auto handle{read_handle(desc, index)}; views.push_back({handle.first}); - Sampler* const sampler = texture_cache.GetComputeSampler(handle.second); - samplers.push_back(sampler->Handle()); + VideoCommon::SamplerId sampler = texture_cache.GetComputeSamplerId(handle.second); + samplers.push_back(sampler); } } for (const auto& desc : info.image_descriptors) { @@ -192,7 +192,7 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute, buffer_cache.BindHostComputeBuffers(); RescalingPushConstant rescaling; - const VkSampler* samplers_it{samplers.data()}; + const VideoCommon::SamplerId* samplers_it{samplers.data()}; const VideoCommon::ImageViewInOut* views_it{views.data()}; PushImageDescriptors(texture_cache, guest_descriptor_queue, info, rescaling, samplers_it, views_it); diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 506b78f08e..c1595642ec 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -298,7 +298,7 @@ void GraphicsPipeline::AddTransition(GraphicsPipeline* transition) { template <typename Spec> void GraphicsPipeline::ConfigureImpl(bool is_indexed) { std::array<VideoCommon::ImageViewInOut, MAX_IMAGE_ELEMENTS> views; - std::array<VkSampler, MAX_IMAGE_ELEMENTS> samplers; + std::array<VideoCommon::SamplerId, MAX_IMAGE_ELEMENTS> samplers; size_t sampler_index{}; size_t view_index{}; @@ -367,8 +367,8 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { const auto handle{read_handle(desc, index)}; views[view_index++] = {handle.first}; - Sampler* const sampler{texture_cache.GetGraphicsSampler(handle.second)}; - samplers[sampler_index++] = sampler->Handle(); + VideoCommon::SamplerId sampler{texture_cache.GetGraphicsSamplerId(handle.second)}; + samplers[sampler_index++] = sampler; } } if constexpr (Spec::has_images) { @@ -453,7 +453,7 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { RescalingPushConstant rescaling; RenderAreaPushConstant render_area; - const VkSampler* samplers_it{samplers.data()}; + const VideoCommon::SamplerId* samplers_it{samplers.data()}; const VideoCommon::ImageViewInOut* views_it{views.data()}; const auto prepare_stage{[&](size_t stage) LAMBDA_FORCEINLINE { buffer_cache.BindHostStageBuffers(stage); diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp index b128c4f6e0..6b288b994f 100644 --- a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp +++ b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp @@ -3,6 +3,7 @@ #include <thread> +#include "common/polyfill_ranges.h" #include "common/settings.h" #include "video_core/renderer_vulkan/vk_master_semaphore.h" #include "video_core/vulkan_common/vulkan_device.h" @@ -74,15 +75,9 @@ void MasterSemaphore::Refresh() { void MasterSemaphore::Wait(u64 tick) { if (!semaphore) { - // If we don't support timeline semaphores, use an atomic wait - while (true) { - u64 current_value = gpu_tick.load(std::memory_order_relaxed); - if (current_value >= tick) { - return; - } - gpu_tick.wait(current_value); - } - + // If we don't support timeline semaphores, wait for the value normally + std::unique_lock lk{free_mutex}; + free_cv.wait(lk, [&] { return gpu_tick.load(std::memory_order_relaxed) >= tick; }); return; } @@ -197,11 +192,13 @@ void MasterSemaphore::WaitThread(std::stop_token token) { fence.Wait(); fence.Reset(); - gpu_tick.store(host_tick); - gpu_tick.notify_all(); - std::scoped_lock lock{free_mutex}; - free_queue.push_front(std::move(fence)); + { + std::scoped_lock lock{free_mutex}; + free_queue.push_front(std::move(fence)); + gpu_tick.store(host_tick); + } + free_cv.notify_one(); } } diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.h b/src/video_core/renderer_vulkan/vk_master_semaphore.h index 1e7c902157..3f599d7bd8 100644 --- a/src/video_core/renderer_vulkan/vk_master_semaphore.h +++ b/src/video_core/renderer_vulkan/vk_master_semaphore.h @@ -72,6 +72,7 @@ private: std::atomic<u64> current_tick{1}; ///< Current logical tick. std::mutex wait_mutex; std::mutex free_mutex; + std::condition_variable free_cv; std::condition_variable_any wait_cv; std::queue<Waitable> wait_queue; ///< Queue for the fences to be waited on by the wait thread. std::deque<vk::Fence> free_queue; ///< Holds available fences for submission. diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 9482e91b01..9f316113cc 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -167,7 +167,10 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> program info.fixed_state_point_size = point_size; } if (key.state.xfb_enabled) { - info.xfb_varyings = VideoCommon::MakeTransformFeedbackVaryings(key.state.xfb_state); + auto [varyings, count] = + VideoCommon::MakeTransformFeedbackVaryings(key.state.xfb_state); + info.xfb_varyings = varyings; + info.xfb_count = count; } info.convert_depth_mode = gl_ndc; } @@ -214,7 +217,10 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> program info.fixed_state_point_size = point_size; } if (key.state.xfb_enabled != 0) { - info.xfb_varyings = VideoCommon::MakeTransformFeedbackVaryings(key.state.xfb_state); + auto [varyings, count] = + VideoCommon::MakeTransformFeedbackVaryings(key.state.xfb_state); + info.xfb_varyings = varyings; + info.xfb_count = count; } info.convert_depth_mode = gl_ndc; break; @@ -350,6 +356,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device .has_broken_spirv_subgroup_mask_vector_extract_dynamic = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY}; host_info = Shader::HostTranslateInfo{ + .support_float64 = device.IsFloat64Supported(), .support_float16 = device.IsFloat16Supported(), .support_int64 = device.IsShaderInt64Supported(), .needs_demote_reorder = @@ -357,6 +364,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device .support_snorm_render_buffer = true, .support_viewport_index_layer = device.IsExtShaderViewportIndexLayerSupported(), .support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(), + .support_conditional_barrier = device.SupportsConditionalBarriers(), }; if (device.GetMaxVertexInputAttributes() < Maxwell::NumVertexAttributes) { @@ -703,10 +711,7 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline( std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline( ShaderPools& pools, const ComputePipelineCacheKey& key, Shader::Environment& env, PipelineStatistics* statistics, bool build_in_parallel) try { - // TODO: Remove this when Intel fixes their shader compiler. - // https://github.com/IGCIT/Intel-GPU-Community-Issue-Tracker-IGCIT/issues/159 - if (device.GetDriverID() == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS && - !Settings::values.enable_compute_pipelines.GetValue()) { + if (device.HasBrokenCompute()) { LOG_ERROR(Render_Vulkan, "Skipping 0x{:016x}", key.Hash()); return nullptr; } diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h index 15aa7e224d..e323ea0fd4 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h @@ -92,9 +92,9 @@ struct ShaderPools { inst.ReleaseContents(); } - Shader::ObjectPool<Shader::IR::Inst> inst; - Shader::ObjectPool<Shader::IR::Block> block; - Shader::ObjectPool<Shader::Maxwell::Flow::Block> flow_block; + Shader::ObjectPool<Shader::IR::Inst> inst{8192}; + Shader::ObjectPool<Shader::IR::Block> block{32}; + Shader::ObjectPool<Shader::Maxwell::Flow::Block> flow_block{32}; }; class PipelineCache : public VideoCommon::ShaderCache { diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 8711e2a87b..f3cef09dd8 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -330,9 +330,9 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) { }; } -[[maybe_unused]] [[nodiscard]] std::vector<VkBufferCopy> TransformBufferCopies( - std::span<const VideoCommon::BufferCopy> copies, size_t buffer_offset) { - std::vector<VkBufferCopy> result(copies.size()); +[[maybe_unused]] [[nodiscard]] boost::container::small_vector<VkBufferCopy, 16> +TransformBufferCopies(std::span<const VideoCommon::BufferCopy> copies, size_t buffer_offset) { + boost::container::small_vector<VkBufferCopy, 16> result(copies.size()); std::ranges::transform( copies, result.begin(), [buffer_offset](const VideoCommon::BufferCopy& copy) { return VkBufferCopy{ @@ -344,7 +344,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) { return result; } -[[nodiscard]] std::vector<VkBufferImageCopy> TransformBufferImageCopies( +[[nodiscard]] boost::container::small_vector<VkBufferImageCopy, 16> TransformBufferImageCopies( std::span<const BufferImageCopy> copies, size_t buffer_offset, VkImageAspectFlags aspect_mask) { struct Maker { VkBufferImageCopy operator()(const BufferImageCopy& copy) const { @@ -377,14 +377,14 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) { VkImageAspectFlags aspect_mask; }; if (aspect_mask == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { - std::vector<VkBufferImageCopy> result(copies.size() * 2); + boost::container::small_vector<VkBufferImageCopy, 16> result(copies.size() * 2); std::ranges::transform(copies, result.begin(), Maker{buffer_offset, VK_IMAGE_ASPECT_DEPTH_BIT}); std::ranges::transform(copies, result.begin() + copies.size(), Maker{buffer_offset, VK_IMAGE_ASPECT_STENCIL_BIT}); return result; } else { - std::vector<VkBufferImageCopy> result(copies.size()); + boost::container::small_vector<VkBufferImageCopy, 16> result(copies.size()); std::ranges::transform(copies, result.begin(), Maker{buffer_offset, aspect_mask}); return result; } @@ -867,8 +867,8 @@ void TextureCacheRuntime::BarrierFeedbackLoop() { void TextureCacheRuntime::ReinterpretImage(Image& dst, Image& src, std::span<const VideoCommon::ImageCopy> copies) { - std::vector<VkBufferImageCopy> vk_in_copies(copies.size()); - std::vector<VkBufferImageCopy> vk_out_copies(copies.size()); + boost::container::small_vector<VkBufferImageCopy, 16> vk_in_copies(copies.size()); + boost::container::small_vector<VkBufferImageCopy, 16> vk_out_copies(copies.size()); const VkImageAspectFlags src_aspect_mask = src.AspectMask(); const VkImageAspectFlags dst_aspect_mask = dst.AspectMask(); @@ -1157,7 +1157,7 @@ void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, Im void TextureCacheRuntime::CopyImage(Image& dst, Image& src, std::span<const VideoCommon::ImageCopy> copies) { - std::vector<VkImageCopy> vk_copies(copies.size()); + boost::container::small_vector<VkImageCopy, 16> vk_copies(copies.size()); const VkImageAspectFlags aspect_mask = dst.AspectMask(); ASSERT(aspect_mask == src.AspectMask()); @@ -1332,7 +1332,7 @@ void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset, ScaleDown(true); } scheduler->RequestOutsideRenderPassOperationContext(); - std::vector vk_copies = TransformBufferImageCopies(copies, offset, aspect_mask); + auto vk_copies = TransformBufferImageCopies(copies, offset, aspect_mask); const VkBuffer src_buffer = buffer; const VkImage vk_image = *original_image; const VkImageAspectFlags vk_aspect_mask = aspect_mask; @@ -1367,8 +1367,9 @@ void Image::DownloadMemory(std::span<VkBuffer> buffers_span, std::span<VkDeviceS if (is_rescaled) { ScaleDown(); } - boost::container::small_vector<VkBuffer, 1> buffers_vector{}; - boost::container::small_vector<std::vector<VkBufferImageCopy>, 1> vk_copies; + boost::container::small_vector<VkBuffer, 8> buffers_vector{}; + boost::container::small_vector<boost::container::small_vector<VkBufferImageCopy, 16>, 8> + vk_copies; for (size_t index = 0; index < buffers_span.size(); index++) { buffers_vector.emplace_back(buffers_span[index]); vk_copies.emplace_back( @@ -1802,27 +1803,36 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t // Some games have samplers with garbage. Sanitize them here. const f32 max_anisotropy = std::clamp(tsc.MaxAnisotropy(), 1.0f, 16.0f); - sampler = device.GetLogical().CreateSampler(VkSamplerCreateInfo{ - .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, - .pNext = pnext, - .flags = 0, - .magFilter = MaxwellToVK::Sampler::Filter(tsc.mag_filter), - .minFilter = MaxwellToVK::Sampler::Filter(tsc.min_filter), - .mipmapMode = MaxwellToVK::Sampler::MipmapMode(tsc.mipmap_filter), - .addressModeU = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_u, tsc.mag_filter), - .addressModeV = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_v, tsc.mag_filter), - .addressModeW = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_p, tsc.mag_filter), - .mipLodBias = tsc.LodBias(), - .anisotropyEnable = static_cast<VkBool32>(max_anisotropy > 1.0f ? VK_TRUE : VK_FALSE), - .maxAnisotropy = max_anisotropy, - .compareEnable = tsc.depth_compare_enabled, - .compareOp = MaxwellToVK::Sampler::DepthCompareFunction(tsc.depth_compare_func), - .minLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.0f : tsc.MinLod(), - .maxLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.25f : tsc.MaxLod(), - .borderColor = - arbitrary_borders ? VK_BORDER_COLOR_FLOAT_CUSTOM_EXT : ConvertBorderColor(color), - .unnormalizedCoordinates = VK_FALSE, - }); + const auto create_sampler = [&](const f32 anisotropy) { + return device.GetLogical().CreateSampler(VkSamplerCreateInfo{ + .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, + .pNext = pnext, + .flags = 0, + .magFilter = MaxwellToVK::Sampler::Filter(tsc.mag_filter), + .minFilter = MaxwellToVK::Sampler::Filter(tsc.min_filter), + .mipmapMode = MaxwellToVK::Sampler::MipmapMode(tsc.mipmap_filter), + .addressModeU = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_u, tsc.mag_filter), + .addressModeV = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_v, tsc.mag_filter), + .addressModeW = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_p, tsc.mag_filter), + .mipLodBias = tsc.LodBias(), + .anisotropyEnable = static_cast<VkBool32>(anisotropy > 1.0f ? VK_TRUE : VK_FALSE), + .maxAnisotropy = anisotropy, + .compareEnable = tsc.depth_compare_enabled, + .compareOp = MaxwellToVK::Sampler::DepthCompareFunction(tsc.depth_compare_func), + .minLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.0f : tsc.MinLod(), + .maxLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.25f : tsc.MaxLod(), + .borderColor = + arbitrary_borders ? VK_BORDER_COLOR_FLOAT_CUSTOM_EXT : ConvertBorderColor(color), + .unnormalizedCoordinates = VK_FALSE, + }); + }; + + sampler = create_sampler(max_anisotropy); + + const f32 max_anisotropy_default = static_cast<f32>(1U << tsc.max_anisotropy); + if (max_anisotropy > max_anisotropy_default) { + sampler_default_anisotropy = create_sampler(max_anisotropy_default); + } } Framebuffer::Framebuffer(TextureCacheRuntime& runtime, std::span<ImageView*, NUM_RT> color_buffers, @@ -1849,7 +1859,7 @@ Framebuffer::~Framebuffer() = default; void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, std::span<ImageView*, NUM_RT> color_buffers, ImageView* depth_buffer, bool is_rescaled) { - std::vector<VkImageView> attachments; + boost::container::small_vector<VkImageView, NUM_RT + 1> attachments; RenderPassKey renderpass_key{}; s32 num_layers = 1; diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index 0f7a5ffd45..f14525dcb0 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -279,8 +279,17 @@ public: return *sampler; } + [[nodiscard]] VkSampler HandleWithDefaultAnisotropy() const noexcept { + return *sampler_default_anisotropy; + } + + [[nodiscard]] bool HasAddedAnisotropy() const noexcept { + return static_cast<bool>(sampler_default_anisotropy); + } + private: vk::Sampler sampler; + vk::Sampler sampler_default_anisotropy; }; class Framebuffer { diff --git a/src/video_core/shader_cache.cpp b/src/video_core/shader_cache.cpp index c5213875b7..4db948b6d4 100644 --- a/src/video_core/shader_cache.cpp +++ b/src/video_core/shader_cache.cpp @@ -151,11 +151,9 @@ void ShaderCache::RemovePendingShaders() { marked_for_removal.erase(std::unique(marked_for_removal.begin(), marked_for_removal.end()), marked_for_removal.end()); - std::vector<ShaderInfo*> removed_shaders; - removed_shaders.reserve(marked_for_removal.size()); + boost::container::small_vector<ShaderInfo*, 16> removed_shaders; std::scoped_lock lock{lookup_mutex}; - for (Entry* const entry : marked_for_removal) { removed_shaders.push_back(entry->data); diff --git a/src/video_core/texture_cache/image_base.h b/src/video_core/texture_cache/image_base.h index 1b8a17ee83..55d49d0178 100644 --- a/src/video_core/texture_cache/image_base.h +++ b/src/video_core/texture_cache/image_base.h @@ -6,6 +6,7 @@ #include <array> #include <optional> #include <vector> +#include <boost/container/small_vector.hpp> #include "common/common_funcs.h" #include "common/common_types.h" @@ -108,8 +109,8 @@ struct ImageBase { std::vector<ImageViewInfo> image_view_infos; std::vector<ImageViewId> image_view_ids; - std::vector<u32> slice_offsets; - std::vector<SubresourceBase> slice_subresources; + boost::container::small_vector<u32, 16> slice_offsets; + boost::container::small_vector<SubresourceBase, 16> slice_subresources; std::vector<AliasedImage> aliased_images; std::vector<ImageId> overlapping_images; diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp index e8ddde6911..b72788c6d5 100644 --- a/src/video_core/texture_cache/image_info.cpp +++ b/src/video_core/texture_cache/image_info.cpp @@ -22,6 +22,9 @@ using Tegra::Texture::TICEntry; using VideoCore::Surface::PixelFormat; using VideoCore::Surface::SurfaceType; +constexpr u32 RescaleHeightThreshold = 288; +constexpr u32 DownscaleHeightThreshold = 512; + ImageInfo::ImageInfo(const TICEntry& config) noexcept { forced_flushed = config.IsPitchLinear() && !Settings::values.use_reactive_flushing.GetValue(); dma_downloaded = forced_flushed; @@ -113,8 +116,9 @@ ImageInfo::ImageInfo(const TICEntry& config) noexcept { layer_stride = CalculateLayerStride(*this); maybe_unaligned_layer_stride = CalculateLayerSize(*this); rescaleable &= (block.depth == 0) && resources.levels == 1; - rescaleable &= size.height > 256 || GetFormatType(format) != SurfaceType::ColorTexture; - downscaleable = size.height > 512; + rescaleable &= size.height > RescaleHeightThreshold || + GetFormatType(format) != SurfaceType::ColorTexture; + downscaleable = size.height > DownscaleHeightThreshold; } } @@ -152,8 +156,8 @@ ImageInfo::ImageInfo(const Maxwell3D::Regs::RenderTargetConfig& ct, size.depth = ct.depth; } else { rescaleable = block.depth == 0; - rescaleable &= size.height > 256; - downscaleable = size.height > 512; + rescaleable &= size.height > RescaleHeightThreshold; + downscaleable = size.height > DownscaleHeightThreshold; type = ImageType::e2D; resources.layers = ct.depth; } @@ -232,8 +236,8 @@ ImageInfo::ImageInfo(const Fermi2D::Surface& config) noexcept { .height = config.height, .depth = 1, }; - rescaleable = block.depth == 0 && size.height > 256; - downscaleable = size.height > 512; + rescaleable = block.depth == 0 && size.height > RescaleHeightThreshold; + downscaleable = size.height > DownscaleHeightThreshold; } } @@ -275,8 +279,8 @@ ImageInfo::ImageInfo(const Tegra::DMA::ImageOperand& config) noexcept { resources.layers = 1; layer_stride = CalculateLayerStride(*this); maybe_unaligned_layer_stride = CalculateLayerSize(*this); - rescaleable = block.depth == 0 && size.height > 256; - downscaleable = size.height > 512; + rescaleable = block.depth == 0 && size.height > RescaleHeightThreshold; + downscaleable = size.height > DownscaleHeightThreshold; } } // namespace VideoCommon diff --git a/src/video_core/texture_cache/image_view_base.cpp b/src/video_core/texture_cache/image_view_base.cpp index d134b6738c..0c5f4450d1 100644 --- a/src/video_core/texture_cache/image_view_base.cpp +++ b/src/video_core/texture_cache/image_view_base.cpp @@ -45,4 +45,56 @@ ImageViewBase::ImageViewBase(const ImageInfo& info, const ImageViewInfo& view_in ImageViewBase::ImageViewBase(const NullImageViewParams&) : image_id{NULL_IMAGE_ID} {} +bool ImageViewBase::SupportsAnisotropy() const noexcept { + const bool has_mips = range.extent.levels > 1; + const bool is_2d = type == ImageViewType::e2D || type == ImageViewType::e2DArray; + if (!has_mips || !is_2d) { + return false; + } + + switch (format) { + case PixelFormat::R8_UNORM: + case PixelFormat::R8_SNORM: + case PixelFormat::R8_SINT: + case PixelFormat::R8_UINT: + case PixelFormat::BC4_UNORM: + case PixelFormat::BC4_SNORM: + case PixelFormat::BC5_UNORM: + case PixelFormat::BC5_SNORM: + case PixelFormat::R32G32_FLOAT: + case PixelFormat::R32G32_SINT: + case PixelFormat::R32_FLOAT: + case PixelFormat::R16_FLOAT: + case PixelFormat::R16_UNORM: + case PixelFormat::R16_SNORM: + case PixelFormat::R16_UINT: + case PixelFormat::R16_SINT: + case PixelFormat::R16G16_UNORM: + case PixelFormat::R16G16_FLOAT: + case PixelFormat::R16G16_UINT: + case PixelFormat::R16G16_SINT: + case PixelFormat::R16G16_SNORM: + case PixelFormat::R8G8_UNORM: + case PixelFormat::R8G8_SNORM: + case PixelFormat::R8G8_SINT: + case PixelFormat::R8G8_UINT: + case PixelFormat::R32G32_UINT: + case PixelFormat::R32_UINT: + case PixelFormat::R32_SINT: + case PixelFormat::G4R4_UNORM: + // Depth formats + case PixelFormat::D32_FLOAT: + case PixelFormat::D16_UNORM: + // Stencil formats + case PixelFormat::S8_UINT: + // DepthStencil formats + case PixelFormat::D24_UNORM_S8_UINT: + case PixelFormat::S8_UINT_D24_UNORM: + case PixelFormat::D32_FLOAT_S8_UINT: + return false; + default: + return true; + } +} + } // namespace VideoCommon diff --git a/src/video_core/texture_cache/image_view_base.h b/src/video_core/texture_cache/image_view_base.h index a25ae1d4ae..87549ffff7 100644 --- a/src/video_core/texture_cache/image_view_base.h +++ b/src/video_core/texture_cache/image_view_base.h @@ -33,6 +33,8 @@ struct ImageViewBase { return type == ImageViewType::Buffer; } + [[nodiscard]] bool SupportsAnisotropy() const noexcept; + ImageId image_id{}; GPUVAddr gpu_addr = 0; PixelFormat format{}; diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index c7f7448e9d..d3f03a9957 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -186,6 +186,10 @@ void TextureCache<P>::FillComputeImageViews(std::span<ImageViewInOut> views) { template <class P> void TextureCache<P>::CheckFeedbackLoop(std::span<const ImageViewInOut> views) { + if (!Settings::values.barrier_feedback_loops.GetValue()) { + return; + } + const bool requires_barrier = [&] { for (const auto& view : views) { if (!view.id) { @@ -222,30 +226,50 @@ void TextureCache<P>::CheckFeedbackLoop(std::span<const ImageViewInOut> views) { template <class P> typename P::Sampler* TextureCache<P>::GetGraphicsSampler(u32 index) { + return &slot_samplers[GetGraphicsSamplerId(index)]; +} + +template <class P> +typename P::Sampler* TextureCache<P>::GetComputeSampler(u32 index) { + return &slot_samplers[GetComputeSamplerId(index)]; +} + +template <class P> +SamplerId TextureCache<P>::GetGraphicsSamplerId(u32 index) { if (index > channel_state->graphics_sampler_table.Limit()) { LOG_DEBUG(HW_GPU, "Invalid sampler index={}", index); - return &slot_samplers[NULL_SAMPLER_ID]; + return NULL_SAMPLER_ID; } const auto [descriptor, is_new] = channel_state->graphics_sampler_table.Read(index); SamplerId& id = channel_state->graphics_sampler_ids[index]; if (is_new) { id = FindSampler(descriptor); } - return &slot_samplers[id]; + return id; } template <class P> -typename P::Sampler* TextureCache<P>::GetComputeSampler(u32 index) { +SamplerId TextureCache<P>::GetComputeSamplerId(u32 index) { if (index > channel_state->compute_sampler_table.Limit()) { LOG_DEBUG(HW_GPU, "Invalid sampler index={}", index); - return &slot_samplers[NULL_SAMPLER_ID]; + return NULL_SAMPLER_ID; } const auto [descriptor, is_new] = channel_state->compute_sampler_table.Read(index); SamplerId& id = channel_state->compute_sampler_ids[index]; if (is_new) { id = FindSampler(descriptor); } - return &slot_samplers[id]; + return id; +} + +template <class P> +const typename P::Sampler& TextureCache<P>::GetSampler(SamplerId id) const noexcept { + return slot_samplers[id]; +} + +template <class P> +typename P::Sampler& TextureCache<P>::GetSampler(SamplerId id) noexcept { + return slot_samplers[id]; } template <class P> @@ -280,7 +304,7 @@ void TextureCache<P>::SynchronizeComputeDescriptors() { } template <class P> -bool TextureCache<P>::RescaleRenderTargets(bool is_clear) { +bool TextureCache<P>::RescaleRenderTargets() { auto& flags = maxwell3d->dirty.flags; u32 scale_rating = 0; bool rescaled = false; @@ -318,13 +342,13 @@ bool TextureCache<P>::RescaleRenderTargets(bool is_clear) { ImageViewId& color_buffer_id = render_targets.color_buffer_ids[index]; if (flags[Dirty::ColorBuffer0 + index] || force) { flags[Dirty::ColorBuffer0 + index] = false; - BindRenderTarget(&color_buffer_id, FindColorBuffer(index, is_clear)); + BindRenderTarget(&color_buffer_id, FindColorBuffer(index)); } check_rescale(color_buffer_id, tmp_color_images[index]); } if (flags[Dirty::ZetaBuffer] || force) { flags[Dirty::ZetaBuffer] = false; - BindRenderTarget(&render_targets.depth_buffer_id, FindDepthBuffer(is_clear)); + BindRenderTarget(&render_targets.depth_buffer_id, FindDepthBuffer()); } check_rescale(render_targets.depth_buffer_id, tmp_depth_image); @@ -389,7 +413,7 @@ void TextureCache<P>::UpdateRenderTargets(bool is_clear) { return; } - const bool rescaled = RescaleRenderTargets(is_clear); + const bool rescaled = RescaleRenderTargets(); if (is_rescaling != rescaled) { flags[Dirty::RescaleViewports] = true; flags[Dirty::RescaleScissors] = true; @@ -502,7 +526,7 @@ void TextureCache<P>::WriteMemory(VAddr cpu_addr, size_t size) { template <class P> void TextureCache<P>::DownloadMemory(VAddr cpu_addr, size_t size) { - std::vector<ImageId> images; + boost::container::small_vector<ImageId, 16> images; ForEachImageInRegion(cpu_addr, size, [&images](ImageId image_id, ImageBase& image) { if (!image.IsSafeDownload()) { return; @@ -555,7 +579,7 @@ std::optional<VideoCore::RasterizerDownloadArea> TextureCache<P>::GetFlushArea(V template <class P> void TextureCache<P>::UnmapMemory(VAddr cpu_addr, size_t size) { - std::vector<ImageId> deleted_images; + boost::container::small_vector<ImageId, 16> deleted_images; ForEachImageInRegion(cpu_addr, size, [&](ImageId id, Image&) { deleted_images.push_back(id); }); for (const ImageId id : deleted_images) { Image& image = slot_images[id]; @@ -569,7 +593,7 @@ void TextureCache<P>::UnmapMemory(VAddr cpu_addr, size_t size) { template <class P> void TextureCache<P>::UnmapGPUMemory(size_t as_id, GPUVAddr gpu_addr, size_t size) { - std::vector<ImageId> deleted_images; + boost::container::small_vector<ImageId, 16> deleted_images; ForEachImageInRegionGPU(as_id, gpu_addr, size, [&](ImageId id, Image&) { deleted_images.push_back(id); }); for (const ImageId id : deleted_images) { @@ -1077,7 +1101,7 @@ ImageId TextureCache<P>::FindImage(const ImageInfo& info, GPUVAddr gpu_addr, const bool native_bgr = runtime.HasNativeBgr(); const bool flexible_formats = True(options & RelaxedOptions::Format); ImageId image_id{}; - boost::container::small_vector<ImageId, 1> image_ids; + boost::container::small_vector<ImageId, 8> image_ids; const auto lambda = [&](ImageId existing_image_id, ImageBase& existing_image) { if (True(existing_image.flags & ImageFlagBits::Remapped)) { return false; @@ -1598,7 +1622,7 @@ ImageId TextureCache<P>::FindDMAImage(const ImageInfo& info, GPUVAddr gpu_addr) } } ImageId image_id{}; - boost::container::small_vector<ImageId, 1> image_ids; + boost::container::small_vector<ImageId, 8> image_ids; const auto lambda = [&](ImageId existing_image_id, ImageBase& existing_image) { if (True(existing_image.flags & ImageFlagBits::Remapped)) { return false; @@ -1658,7 +1682,7 @@ SamplerId TextureCache<P>::FindSampler(const TSCEntry& config) { } template <class P> -ImageViewId TextureCache<P>::FindColorBuffer(size_t index, bool is_clear) { +ImageViewId TextureCache<P>::FindColorBuffer(size_t index) { const auto& regs = maxwell3d->regs; if (index >= regs.rt_control.count) { return ImageViewId{}; @@ -1672,11 +1696,11 @@ ImageViewId TextureCache<P>::FindColorBuffer(size_t index, bool is_clear) { return ImageViewId{}; } const ImageInfo info(regs.rt[index], regs.anti_alias_samples_mode); - return FindRenderTargetView(info, gpu_addr, is_clear); + return FindRenderTargetView(info, gpu_addr); } template <class P> -ImageViewId TextureCache<P>::FindDepthBuffer(bool is_clear) { +ImageViewId TextureCache<P>::FindDepthBuffer() { const auto& regs = maxwell3d->regs; if (!regs.zeta_enable) { return ImageViewId{}; @@ -1686,18 +1710,16 @@ ImageViewId TextureCache<P>::FindDepthBuffer(bool is_clear) { return ImageViewId{}; } const ImageInfo info(regs.zeta, regs.zeta_size, regs.anti_alias_samples_mode); - return FindRenderTargetView(info, gpu_addr, is_clear); + return FindRenderTargetView(info, gpu_addr); } template <class P> -ImageViewId TextureCache<P>::FindRenderTargetView(const ImageInfo& info, GPUVAddr gpu_addr, - bool is_clear) { - const auto options = is_clear ? RelaxedOptions::Samples : RelaxedOptions{}; +ImageViewId TextureCache<P>::FindRenderTargetView(const ImageInfo& info, GPUVAddr gpu_addr) { ImageId image_id{}; bool delete_state = has_deleted_images; do { has_deleted_images = false; - image_id = FindOrInsertImage(info, gpu_addr, options); + image_id = FindOrInsertImage(info, gpu_addr); delete_state |= has_deleted_images; } while (has_deleted_images); has_deleted_images = delete_state; @@ -1920,7 +1942,7 @@ void TextureCache<P>::RegisterImage(ImageId image_id) { image.map_view_id = map_id; return; } - std::vector<ImageViewId> sparse_maps{}; + boost::container::small_vector<ImageViewId, 16> sparse_maps; ForEachSparseSegment( image, [this, image_id, &sparse_maps](GPUVAddr gpu_addr, VAddr cpu_addr, size_t size) { auto map_id = slot_map_views.insert(gpu_addr, cpu_addr, size, image_id); @@ -2195,7 +2217,7 @@ void TextureCache<P>::MarkModification(ImageBase& image) noexcept { template <class P> void TextureCache<P>::SynchronizeAliases(ImageId image_id) { - boost::container::small_vector<const AliasedImage*, 1> aliased_images; + boost::container::small_vector<const AliasedImage*, 8> aliased_images; Image& image = slot_images[image_id]; bool any_rescaled = True(image.flags & ImageFlagBits::Rescaled); bool any_modified = True(image.flags & ImageFlagBits::GpuModified); diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h index 3bfa921549..e9ec91265c 100644 --- a/src/video_core/texture_cache/texture_cache_base.h +++ b/src/video_core/texture_cache/texture_cache_base.h @@ -56,7 +56,7 @@ struct ImageViewInOut { struct AsyncDecodeContext { ImageId image_id; Common::ScratchBuffer<u8> decoded_data; - std::vector<BufferImageCopy> copies; + boost::container::small_vector<BufferImageCopy, 16> copies; std::mutex mutex; std::atomic_bool complete; }; @@ -159,6 +159,18 @@ public: /// Get the sampler from the compute descriptor table in the specified index Sampler* GetComputeSampler(u32 index); + /// Get the sampler id from the graphics descriptor table in the specified index + SamplerId GetGraphicsSamplerId(u32 index); + + /// Get the sampler id from the compute descriptor table in the specified index + SamplerId GetComputeSamplerId(u32 index); + + /// Return a constant reference to the given sampler id + [[nodiscard]] const Sampler& GetSampler(SamplerId id) const noexcept; + + /// Return a reference to the given sampler id + [[nodiscard]] Sampler& GetSampler(SamplerId id) noexcept; + /// Refresh the state for graphics image view and sampler descriptors void SynchronizeGraphicsDescriptors(); @@ -166,9 +178,8 @@ public: void SynchronizeComputeDescriptors(); /// Updates the Render Targets if they can be rescaled - /// @param is_clear True when the render targets are being used for clears /// @retval True if the Render Targets have been rescaled. - bool RescaleRenderTargets(bool is_clear); + bool RescaleRenderTargets(); /// Update bound render targets and upload memory if necessary /// @param is_clear True when the render targets are being used for clears @@ -324,14 +335,13 @@ private: [[nodiscard]] SamplerId FindSampler(const TSCEntry& config); /// Find or create an image view for the given color buffer index - [[nodiscard]] ImageViewId FindColorBuffer(size_t index, bool is_clear); + [[nodiscard]] ImageViewId FindColorBuffer(size_t index); /// Find or create an image view for the depth buffer - [[nodiscard]] ImageViewId FindDepthBuffer(bool is_clear); + [[nodiscard]] ImageViewId FindDepthBuffer(); /// Find or create a view for a render target with the given image parameters - [[nodiscard]] ImageViewId FindRenderTargetView(const ImageInfo& info, GPUVAddr gpu_addr, - bool is_clear); + [[nodiscard]] ImageViewId FindRenderTargetView(const ImageInfo& info, GPUVAddr gpu_addr); /// Iterates over all the images in a region calling func template <typename Func> @@ -419,7 +429,7 @@ private: std::unordered_map<u64, std::vector<ImageMapId>, Common::IdentityHash<u64>> page_table; std::unordered_map<u64, std::vector<ImageId>, Common::IdentityHash<u64>> sparse_page_table; - std::unordered_map<ImageId, std::vector<ImageViewId>> sparse_views; + std::unordered_map<ImageId, boost::container::small_vector<ImageViewId, 16>> sparse_views; VAddr virtual_invalid_space{}; diff --git a/src/video_core/texture_cache/util.cpp b/src/video_core/texture_cache/util.cpp index 95a5b47d8c..f781cb7a07 100644 --- a/src/video_core/texture_cache/util.cpp +++ b/src/video_core/texture_cache/util.cpp @@ -329,13 +329,13 @@ template <u32 GOB_EXTENT> [[nodiscard]] std::optional<SubresourceExtent> ResolveOverlapRightAddress3D( const ImageInfo& new_info, GPUVAddr gpu_addr, const ImageBase& overlap, bool strict_size) { - const std::vector<u32> slice_offsets = CalculateSliceOffsets(new_info); + const auto slice_offsets = CalculateSliceOffsets(new_info); const u32 diff = static_cast<u32>(overlap.gpu_addr - gpu_addr); const auto it = std::ranges::find(slice_offsets, diff); if (it == slice_offsets.end()) { return std::nullopt; } - const std::vector subresources = CalculateSliceSubresources(new_info); + const auto subresources = CalculateSliceSubresources(new_info); const SubresourceBase base = subresources[std::distance(slice_offsets.begin(), it)]; const ImageInfo& info = overlap.info; if (!IsBlockLinearSizeCompatible(new_info, info, base.level, 0, strict_size)) { @@ -655,9 +655,9 @@ LevelArray CalculateMipLevelSizes(const ImageInfo& info) noexcept { return sizes; } -std::vector<u32> CalculateSliceOffsets(const ImageInfo& info) { +boost::container::small_vector<u32, 16> CalculateSliceOffsets(const ImageInfo& info) { ASSERT(info.type == ImageType::e3D); - std::vector<u32> offsets; + boost::container::small_vector<u32, 16> offsets; offsets.reserve(NumSlices(info)); const LevelInfo level_info = MakeLevelInfo(info); @@ -679,9 +679,10 @@ std::vector<u32> CalculateSliceOffsets(const ImageInfo& info) { return offsets; } -std::vector<SubresourceBase> CalculateSliceSubresources(const ImageInfo& info) { +boost::container::small_vector<SubresourceBase, 16> CalculateSliceSubresources( + const ImageInfo& info) { ASSERT(info.type == ImageType::e3D); - std::vector<SubresourceBase> subresources; + boost::container::small_vector<SubresourceBase, 16> subresources; subresources.reserve(NumSlices(info)); for (s32 level = 0; level < info.resources.levels; ++level) { const s32 depth = AdjustMipSize(info.size.depth, level); @@ -723,8 +724,10 @@ ImageViewType RenderTargetImageViewType(const ImageInfo& info) noexcept { } } -std::vector<ImageCopy> MakeShrinkImageCopies(const ImageInfo& dst, const ImageInfo& src, - SubresourceBase base, u32 up_scale, u32 down_shift) { +boost::container::small_vector<ImageCopy, 16> MakeShrinkImageCopies(const ImageInfo& dst, + const ImageInfo& src, + SubresourceBase base, + u32 up_scale, u32 down_shift) { ASSERT(dst.resources.levels >= src.resources.levels); const bool is_dst_3d = dst.type == ImageType::e3D; @@ -733,7 +736,7 @@ std::vector<ImageCopy> MakeShrinkImageCopies(const ImageInfo& dst, const ImageIn ASSERT(src.resources.levels == 1); } const bool both_2d{src.type == ImageType::e2D && dst.type == ImageType::e2D}; - std::vector<ImageCopy> copies; + boost::container::small_vector<ImageCopy, 16> copies; copies.reserve(src.resources.levels); for (s32 level = 0; level < src.resources.levels; ++level) { ImageCopy& copy = copies.emplace_back(); @@ -770,9 +773,10 @@ std::vector<ImageCopy> MakeShrinkImageCopies(const ImageInfo& dst, const ImageIn return copies; } -std::vector<ImageCopy> MakeReinterpretImageCopies(const ImageInfo& src, u32 up_scale, - u32 down_shift) { - std::vector<ImageCopy> copies; +boost::container::small_vector<ImageCopy, 16> MakeReinterpretImageCopies(const ImageInfo& src, + u32 up_scale, + u32 down_shift) { + boost::container::small_vector<ImageCopy, 16> copies; copies.reserve(src.resources.levels); const bool is_3d = src.type == ImageType::e3D; for (s32 level = 0; level < src.resources.levels; ++level) { @@ -824,9 +828,11 @@ bool IsValidEntry(const Tegra::MemoryManager& gpu_memory, const TICEntry& config return gpu_memory.GpuToCpuAddress(address, guest_size_bytes).has_value(); } -std::vector<BufferImageCopy> UnswizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, - const ImageInfo& info, std::span<const u8> input, - std::span<u8> output) { +boost::container::small_vector<BufferImageCopy, 16> UnswizzleImage(Tegra::MemoryManager& gpu_memory, + GPUVAddr gpu_addr, + const ImageInfo& info, + std::span<const u8> input, + std::span<u8> output) { const size_t guest_size_bytes = input.size_bytes(); const u32 bpp_log2 = BytesPerBlockLog2(info.format); const Extent3D size = info.size; @@ -861,7 +867,7 @@ std::vector<BufferImageCopy> UnswizzleImage(Tegra::MemoryManager& gpu_memory, GP info.tile_width_spacing); size_t guest_offset = 0; u32 host_offset = 0; - std::vector<BufferImageCopy> copies(num_levels); + boost::container::small_vector<BufferImageCopy, 16> copies(num_levels); for (s32 level = 0; level < num_levels; ++level) { const Extent3D level_size = AdjustMipSize(size, level); @@ -978,7 +984,7 @@ void ConvertImage(std::span<const u8> input, const ImageInfo& info, std::span<u8 } } -std::vector<BufferImageCopy> FullDownloadCopies(const ImageInfo& info) { +boost::container::small_vector<BufferImageCopy, 16> FullDownloadCopies(const ImageInfo& info) { const Extent3D size = info.size; const u32 bytes_per_block = BytesPerBlock(info.format); if (info.type == ImageType::Linear) { @@ -1006,7 +1012,7 @@ std::vector<BufferImageCopy> FullDownloadCopies(const ImageInfo& info) { u32 host_offset = 0; - std::vector<BufferImageCopy> copies(num_levels); + boost::container::small_vector<BufferImageCopy, 16> copies(num_levels); for (s32 level = 0; level < num_levels; ++level) { const Extent3D level_size = AdjustMipSize(size, level); const u32 num_blocks_per_layer = NumBlocks(level_size, tile_size); @@ -1042,10 +1048,10 @@ Extent3D MipBlockSize(const ImageInfo& info, u32 level) { return AdjustMipBlockSize(num_tiles, level_info.block, level); } -std::vector<SwizzleParameters> FullUploadSwizzles(const ImageInfo& info) { +boost::container::small_vector<SwizzleParameters, 16> FullUploadSwizzles(const ImageInfo& info) { const Extent2D tile_size = DefaultBlockSize(info.format); if (info.type == ImageType::Linear) { - return std::vector{SwizzleParameters{ + return {SwizzleParameters{ .num_tiles = AdjustTileSize(info.size, tile_size), .block = {}, .buffer_offset = 0, @@ -1057,7 +1063,7 @@ std::vector<SwizzleParameters> FullUploadSwizzles(const ImageInfo& info) { const s32 num_levels = info.resources.levels; u32 guest_offset = 0; - std::vector<SwizzleParameters> params(num_levels); + boost::container::small_vector<SwizzleParameters, 16> params(num_levels); for (s32 level = 0; level < num_levels; ++level) { const Extent3D level_size = AdjustMipSize(size, level); const Extent3D num_tiles = AdjustTileSize(level_size, tile_size); diff --git a/src/video_core/texture_cache/util.h b/src/video_core/texture_cache/util.h index 84aa6880d8..ab45a43c47 100644 --- a/src/video_core/texture_cache/util.h +++ b/src/video_core/texture_cache/util.h @@ -5,6 +5,7 @@ #include <optional> #include <span> +#include <boost/container/small_vector.hpp> #include "common/common_types.h" #include "common/scratch_buffer.h" @@ -40,9 +41,10 @@ struct OverlapResult { [[nodiscard]] LevelArray CalculateMipLevelSizes(const ImageInfo& info) noexcept; -[[nodiscard]] std::vector<u32> CalculateSliceOffsets(const ImageInfo& info); +[[nodiscard]] boost::container::small_vector<u32, 16> CalculateSliceOffsets(const ImageInfo& info); -[[nodiscard]] std::vector<SubresourceBase> CalculateSliceSubresources(const ImageInfo& info); +[[nodiscard]] boost::container::small_vector<SubresourceBase, 16> CalculateSliceSubresources( + const ImageInfo& info); [[nodiscard]] u32 CalculateLevelStrideAlignment(const ImageInfo& info, u32 level); @@ -51,21 +53,18 @@ struct OverlapResult { [[nodiscard]] ImageViewType RenderTargetImageViewType(const ImageInfo& info) noexcept; -[[nodiscard]] std::vector<ImageCopy> MakeShrinkImageCopies(const ImageInfo& dst, - const ImageInfo& src, - SubresourceBase base, u32 up_scale = 1, - u32 down_shift = 0); +[[nodiscard]] boost::container::small_vector<ImageCopy, 16> MakeShrinkImageCopies( + const ImageInfo& dst, const ImageInfo& src, SubresourceBase base, u32 up_scale = 1, + u32 down_shift = 0); -[[nodiscard]] std::vector<ImageCopy> MakeReinterpretImageCopies(const ImageInfo& src, - u32 up_scale = 1, - u32 down_shift = 0); +[[nodiscard]] boost::container::small_vector<ImageCopy, 16> MakeReinterpretImageCopies( + const ImageInfo& src, u32 up_scale = 1, u32 down_shift = 0); [[nodiscard]] bool IsValidEntry(const Tegra::MemoryManager& gpu_memory, const TICEntry& config); -[[nodiscard]] std::vector<BufferImageCopy> UnswizzleImage(Tegra::MemoryManager& gpu_memory, - GPUVAddr gpu_addr, const ImageInfo& info, - std::span<const u8> input, - std::span<u8> output); +[[nodiscard]] boost::container::small_vector<BufferImageCopy, 16> UnswizzleImage( + Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, const ImageInfo& info, + std::span<const u8> input, std::span<u8> output); [[nodiscard]] BufferCopy UploadBufferCopy(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, const ImageBase& image, std::span<u8> output); @@ -73,13 +72,15 @@ struct OverlapResult { void ConvertImage(std::span<const u8> input, const ImageInfo& info, std::span<u8> output, std::span<BufferImageCopy> copies); -[[nodiscard]] std::vector<BufferImageCopy> FullDownloadCopies(const ImageInfo& info); +[[nodiscard]] boost::container::small_vector<BufferImageCopy, 16> FullDownloadCopies( + const ImageInfo& info); [[nodiscard]] Extent3D MipSize(Extent3D size, u32 level); [[nodiscard]] Extent3D MipBlockSize(const ImageInfo& info, u32 level); -[[nodiscard]] std::vector<SwizzleParameters> FullUploadSwizzles(const ImageInfo& info); +[[nodiscard]] boost::container::small_vector<SwizzleParameters, 16> FullUploadSwizzles( + const ImageInfo& info); void SwizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, const ImageInfo& info, std::span<const BufferImageCopy> copies, std::span<const u8> memory, diff --git a/src/video_core/textures/texture.cpp b/src/video_core/textures/texture.cpp index 4a80a59f94..d8b88d9bc2 100644 --- a/src/video_core/textures/texture.cpp +++ b/src/video_core/textures/texture.cpp @@ -62,7 +62,12 @@ std::array<float, 4> TSCEntry::BorderColor() const noexcept { } float TSCEntry::MaxAnisotropy() const noexcept { - if (max_anisotropy == 0 && mipmap_filter != TextureMipmapFilter::Linear) { + const bool is_suitable_mipmap_filter = mipmap_filter != TextureMipmapFilter::None; + const bool has_regular_lods = min_lod_clamp == 0 && max_lod_clamp >= 256; + const bool is_bilinear_filter = min_filter == TextureFilter::Linear && + reduction_filter == SamplerReduction::WeightedAverage; + if (max_anisotropy == 0 && (!is_suitable_mipmap_filter || !has_regular_lods || + !is_bilinear_filter || depth_compare_enabled)) { return 1.0f; } const auto anisotropic_settings = Settings::values.max_anisotropy.GetValue(); diff --git a/src/video_core/transform_feedback.cpp b/src/video_core/transform_feedback.cpp index 155599316b..1f353d2df0 100644 --- a/src/video_core/transform_feedback.cpp +++ b/src/video_core/transform_feedback.cpp @@ -13,7 +13,7 @@ namespace VideoCommon { -std::vector<Shader::TransformFeedbackVarying> MakeTransformFeedbackVaryings( +std::pair<std::array<Shader::TransformFeedbackVarying, 256>, u32> MakeTransformFeedbackVaryings( const TransformFeedbackState& state) { static constexpr std::array VECTORS{ 28U, // gl_Position @@ -62,7 +62,8 @@ std::vector<Shader::TransformFeedbackVarying> MakeTransformFeedbackVaryings( 216U, // gl_TexCoord[6] 220U, // gl_TexCoord[7] }; - std::vector<Shader::TransformFeedbackVarying> xfb(256); + std::array<Shader::TransformFeedbackVarying, 256> xfb{}; + u32 count{0}; for (size_t buffer = 0; buffer < state.layouts.size(); ++buffer) { const auto& locations = state.varyings[buffer]; const auto& layout = state.layouts[buffer]; @@ -103,11 +104,12 @@ std::vector<Shader::TransformFeedbackVarying> MakeTransformFeedbackVaryings( } } xfb[attribute] = varying; + count = std::max(count, attribute); highest = std::max(highest, (base_offset + varying.components) * 4); } UNIMPLEMENTED_IF(highest != layout.stride); } - return xfb; + return {xfb, count + 1}; } } // namespace VideoCommon diff --git a/src/video_core/transform_feedback.h b/src/video_core/transform_feedback.h index d13eb16c39..401b1352a2 100644 --- a/src/video_core/transform_feedback.h +++ b/src/video_core/transform_feedback.h @@ -24,7 +24,7 @@ struct TransformFeedbackState { varyings; }; -std::vector<Shader::TransformFeedbackVarying> MakeTransformFeedbackVaryings( +std::pair<std::array<Shader::TransformFeedbackVarying, 256>, u32> MakeTransformFeedbackVaryings( const TransformFeedbackState& state); } // namespace VideoCommon diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 0158b6b0d3..b11abe311c 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -316,6 +316,7 @@ NvidiaArchitecture GetNvidiaArchitecture(vk::PhysicalDevice physical, std::vector<const char*> ExtensionListForVulkan( const std::set<std::string, std::less<>>& extensions) { std::vector<const char*> output; + output.reserve(extensions.size()); for (const auto& extension : extensions) { output.push_back(extension.c_str()); } @@ -344,6 +345,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR const bool is_qualcomm = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY; const bool is_turnip = driver_id == VK_DRIVER_ID_MESA_TURNIP; const bool is_s8gen2 = device_id == 0x43050a01; + const bool is_arm = driver_id == VK_DRIVER_ID_ARM_PROPRIETARY; if ((is_mvk || is_qualcomm || is_turnip) && !is_suitable) { LOG_WARNING(Render_Vulkan, "Unsuitable driver, continuing anyway"); @@ -386,10 +388,11 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR IsFormatSupported(VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT, FormatType::Optimal); + supports_conditional_barriers = !(is_intel_anv || is_intel_windows); + CollectPhysicalMemoryInfo(); CollectToolingInfo(); -#ifdef ANDROID if (is_qualcomm || is_turnip) { LOG_WARNING(Render_Vulkan, "Qualcomm and Turnip drivers have broken VK_EXT_custom_border_color"); @@ -409,7 +412,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR extensions.push_descriptor = false; loaded_extensions.erase(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME); -#ifdef ARCHITECTURE_arm64 +#if defined(ANDROID) && defined(ARCHITECTURE_arm64) // Patch the driver to enable BCn textures. const auto major = (properties.properties.driverVersion >> 24) << 2; const auto minor = (properties.properties.driverVersion >> 12) & 0xFFFU; @@ -429,18 +432,23 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR } else { LOG_WARNING(Render_Vulkan, "Adreno driver can't be patched to enable BCn textures"); } -#endif // ARCHITECTURE_arm64 +#endif } - const bool is_arm = driver_id == VK_DRIVER_ID_ARM_PROPRIETARY; if (is_arm) { must_emulate_scaled_formats = true; LOG_WARNING(Render_Vulkan, "ARM drivers have broken VK_EXT_extended_dynamic_state"); extensions.extended_dynamic_state = false; loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); + + LOG_WARNING(Render_Vulkan, "ARM drivers have broken VK_EXT_extended_dynamic_state2"); + features.extended_dynamic_state2.extendedDynamicState2 = false; + features.extended_dynamic_state2.extendedDynamicState2LogicOp = false; + features.extended_dynamic_state2.extendedDynamicState2PatchControlPoints = false; + extensions.extended_dynamic_state2 = false; + loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); } -#endif // ANDROID if (is_nvidia) { const u32 nv_major_version = (properties.properties.driverVersion >> 22) & 0x3ff; @@ -555,6 +563,9 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR LOG_WARNING(Render_Vulkan, "Intel proprietary drivers do not support MSAA image blits"); cant_blit_msaa = true; } + has_broken_compute = + CheckBrokenCompute(properties.driver.driverID, properties.properties.driverVersion) && + !Settings::values.enable_compute_pipelines.GetValue(); if (is_intel_anv || (is_qualcomm && !is_s8gen2)) { LOG_WARNING(Render_Vulkan, "Driver does not support native BGR format"); must_emulate_bgr565 = true; @@ -776,9 +787,6 @@ bool Device::GetSuitability(bool requires_swapchain) { FOR_EACH_VK_FEATURE_EXT(FEATURE_EXTENSION); FOR_EACH_VK_EXTENSION(EXTENSION); -#ifdef _WIN32 - FOR_EACH_VK_EXTENSION_WIN32(EXTENSION); -#endif #undef FEATURE_EXTENSION #undef EXTENSION @@ -797,11 +805,6 @@ bool Device::GetSuitability(bool requires_swapchain) { FOR_EACH_VK_RECOMMENDED_EXTENSION(LOG_EXTENSION); FOR_EACH_VK_MANDATORY_EXTENSION(CHECK_EXTENSION); -#ifdef _WIN32 - FOR_EACH_VK_MANDATORY_EXTENSION_WIN32(CHECK_EXTENSION); -#else - FOR_EACH_VK_MANDATORY_EXTENSION_GENERIC(CHECK_EXTENSION); -#endif if (requires_swapchain) { CHECK_EXTENSION(VK_KHR_SWAPCHAIN_EXTENSION_NAME); diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index d62a103a1b..0b634a8764 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -10,6 +10,7 @@ #include <vector> #include "common/common_types.h" +#include "common/logging/log.h" #include "common/settings.h" #include "video_core/vulkan_common/vulkan_wrapper.h" @@ -68,7 +69,6 @@ EXTENSION(EXT, VERTEX_ATTRIBUTE_DIVISOR, vertex_attribute_divisor) \ EXTENSION(KHR, DRAW_INDIRECT_COUNT, draw_indirect_count) \ EXTENSION(KHR, DRIVER_PROPERTIES, driver_properties) \ - EXTENSION(KHR, EXTERNAL_MEMORY_FD, external_memory_fd) \ EXTENSION(KHR, PUSH_DESCRIPTOR, push_descriptor) \ EXTENSION(KHR, SAMPLER_MIRROR_CLAMP_TO_EDGE, sampler_mirror_clamp_to_edge) \ EXTENSION(KHR, SHADER_FLOAT_CONTROLS, shader_float_controls) \ @@ -80,9 +80,6 @@ EXTENSION(NV, VIEWPORT_ARRAY2, viewport_array2) \ EXTENSION(NV, VIEWPORT_SWIZZLE, viewport_swizzle) -#define FOR_EACH_VK_EXTENSION_WIN32(EXTENSION) \ - EXTENSION(KHR, EXTERNAL_MEMORY_WIN32, external_memory_win32) - // Define extensions which must be supported. #define FOR_EACH_VK_MANDATORY_EXTENSION(EXTENSION_NAME) \ EXTENSION_NAME(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME) \ @@ -90,12 +87,6 @@ EXTENSION_NAME(VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME) \ EXTENSION_NAME(VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME) -#define FOR_EACH_VK_MANDATORY_EXTENSION_GENERIC(EXTENSION_NAME) \ - EXTENSION_NAME(VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME) - -#define FOR_EACH_VK_MANDATORY_EXTENSION_WIN32(EXTENSION_NAME) \ - EXTENSION_NAME(VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME) - // Define extensions where the absence of the extension may result in a degraded experience. #define FOR_EACH_VK_RECOMMENDED_EXTENSION(EXTENSION_NAME) \ EXTENSION_NAME(VK_EXT_CONSERVATIVE_RASTERIZATION_EXTENSION_NAME) \ @@ -300,6 +291,11 @@ public: return GetDriverID() != VK_DRIVER_ID_QUALCOMM_PROPRIETARY; } + /// Returns true if the device suppors float64 natively. + bool IsFloat64Supported() const { + return features.features.shaderFloat64; + } + /// Returns true if the device supports float16 natively. bool IsFloat16Supported() const { return features.shader_float16_int8.shaderFloat16; @@ -523,6 +519,11 @@ public: return has_renderdoc || has_nsight_graphics || Settings::values.renderer_debug.GetValue(); } + /// @returns True if compute pipelines can cause crashing. + bool HasBrokenCompute() const { + return has_broken_compute; + } + /// Returns true when the device does not properly support cube compatibility. bool HasBrokenCubeImageCompability() const { return has_broken_cube_compatibility; @@ -580,6 +581,26 @@ public: return properties.properties.limits.maxVertexInputBindings; } + bool SupportsConditionalBarriers() const { + return supports_conditional_barriers; + } + + [[nodiscard]] static constexpr bool CheckBrokenCompute(VkDriverId driver_id, + u32 driver_version) { + if (driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) { + const u32 major = VK_API_VERSION_MAJOR(driver_version); + const u32 minor = VK_API_VERSION_MINOR(driver_version); + const u32 patch = VK_API_VERSION_PATCH(driver_version); + if (major == 0 && minor == 405 && patch < 286) { + LOG_WARNING( + Render_Vulkan, + "Intel proprietary drivers 0.405.0 until 0.405.286 have broken compute"); + return true; + } + } + return false; + } + private: /// Checks if the physical device is suitable and configures the object state /// with all necessary info about its properties. @@ -627,7 +648,6 @@ private: FOR_EACH_VK_FEATURE_1_3(FEATURE); FOR_EACH_VK_FEATURE_EXT(FEATURE); FOR_EACH_VK_EXTENSION(EXTENSION); - FOR_EACH_VK_EXTENSION_WIN32(EXTENSION); #undef EXTENSION #undef FEATURE @@ -674,6 +694,7 @@ private: bool is_integrated{}; ///< Is GPU an iGPU. bool is_virtual{}; ///< Is GPU a virtual GPU. bool is_non_gpu{}; ///< Is SoftwareRasterizer, FPGA, non-GPU device. + bool has_broken_compute{}; ///< Compute shaders can cause crashes bool has_broken_cube_compatibility{}; ///< Has broken cube compatibility bit bool has_renderdoc{}; ///< Has RenderDoc attached bool has_nsight_graphics{}; ///< Has Nsight Graphics attached @@ -683,6 +704,7 @@ private: bool must_emulate_bgr565{}; ///< Emulates BGR565 by swizzling RGB565 format. bool dynamic_state3_blending{}; ///< Has all blending features of dynamic_state3. bool dynamic_state3_enables{}; ///< Has all enables features of dynamic_state3. + bool supports_conditional_barriers{}; ///< Allows barriers in conditional control flow. u64 device_access_memory{}; ///< Total size of device local memory in bytes. u32 sets_per_pool{}; ///< Sets per Description Pool diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 8676bfd8aa..fe98e3605e 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -213,6 +213,8 @@ add_executable(yuzu util/url_request_interceptor.h util/util.cpp util/util.h + vk_device_info.cpp + vk_device_info.h compatdb.cpp compatdb.h yuzu.qrc diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index cc6b6a25ab..bdd1497b52 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -105,14 +105,12 @@ void EmuThread::run() { std::unique_lock lk{m_should_run_mutex}; if (m_should_run) { m_system.Run(); - m_is_running.store(true); - m_is_running.notify_all(); + m_stopped.Reset(); Common::CondvarWait(m_should_run_cv, lk, stop_token, [&] { return !m_should_run; }); } else { m_system.Pause(); - m_is_running.store(false); - m_is_running.notify_all(); + m_stopped.Set(); EmulationPaused(lk); Common::CondvarWait(m_should_run_cv, lk, stop_token, [&] { return m_should_run; }); diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index b7b9d41416..87b23df123 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h @@ -3,7 +3,6 @@ #pragma once -#include <atomic> #include <condition_variable> #include <cstddef> #include <memory> @@ -88,7 +87,7 @@ public: // Wait until paused, if pausing. if (!should_run) { - m_is_running.wait(true); + m_stopped.Wait(); } } @@ -97,7 +96,7 @@ public: * @return True if the emulation thread is running, otherwise false */ bool IsRunning() const { - return m_is_running.load() || m_should_run; + return m_should_run; } /** @@ -118,7 +117,7 @@ private: std::stop_source m_stop_source; std::mutex m_should_run_mutex; std::condition_variable_any m_should_run_cv; - std::atomic<bool> m_is_running{false}; + Common::Event m_stopped; bool m_should_run{true}; signals: diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index b58a1e9d1c..87ab88cfac 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -774,6 +774,7 @@ void Config::ReadRendererValues() { ReadGlobalSetting(Settings::values.use_vulkan_driver_pipeline_cache); ReadGlobalSetting(Settings::values.enable_compute_pipelines); ReadGlobalSetting(Settings::values.use_video_framerate); + ReadGlobalSetting(Settings::values.barrier_feedback_loops); ReadGlobalSetting(Settings::values.bg_red); ReadGlobalSetting(Settings::values.bg_green); ReadGlobalSetting(Settings::values.bg_blue); @@ -1444,6 +1445,7 @@ void Config::SaveRendererValues() { WriteGlobalSetting(Settings::values.use_vulkan_driver_pipeline_cache); WriteGlobalSetting(Settings::values.enable_compute_pipelines); WriteGlobalSetting(Settings::values.use_video_framerate); + WriteGlobalSetting(Settings::values.barrier_feedback_loops); WriteGlobalSetting(Settings::values.bg_red); WriteGlobalSetting(Settings::values.bg_green); WriteGlobalSetting(Settings::values.bg_blue); diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp index 8e76a819a9..bdf83ebfe3 100644 --- a/src/yuzu/configuration/configure_dialog.cpp +++ b/src/yuzu/configuration/configure_dialog.cpp @@ -6,6 +6,7 @@ #include "common/settings.h" #include "core/core.h" #include "ui_configure.h" +#include "vk_device_info.h" #include "yuzu/configuration/config.h" #include "yuzu/configuration/configure_audio.h" #include "yuzu/configuration/configure_cpu.h" @@ -28,6 +29,7 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_, InputCommon::InputSubsystem* input_subsystem, + std::vector<VkDeviceInfo::Record>& vk_device_records, Core::System& system_, bool enable_web_config) : QDialog(parent), ui{std::make_unique<Ui::ConfigureDialog>()}, registry(registry_), system{system_}, audio_tab{std::make_unique<ConfigureAudio>(system_, @@ -38,7 +40,8 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_, general_tab{std::make_unique<ConfigureGeneral>(system_, this)}, graphics_advanced_tab{std::make_unique<ConfigureGraphicsAdvanced>(system_, this)}, graphics_tab{std::make_unique<ConfigureGraphics>( - system_, [&]() { graphics_advanced_tab->ExposeComputeOption(); }, this)}, + system_, vk_device_records, [&]() { graphics_advanced_tab->ExposeComputeOption(); }, + this)}, hotkeys_tab{std::make_unique<ConfigureHotkeys>(system_.HIDCore(), this)}, input_tab{std::make_unique<ConfigureInput>(system_, this)}, network_tab{std::make_unique<ConfigureNetwork>(system_, this)}, diff --git a/src/yuzu/configuration/configure_dialog.h b/src/yuzu/configuration/configure_dialog.h index a086a07c4e..2a08b7feec 100644 --- a/src/yuzu/configuration/configure_dialog.h +++ b/src/yuzu/configuration/configure_dialog.h @@ -4,7 +4,9 @@ #pragma once #include <memory> +#include <vector> #include <QDialog> +#include "yuzu/vk_device_info.h" namespace Core { class System; @@ -40,8 +42,9 @@ class ConfigureDialog : public QDialog { public: explicit ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_, - InputCommon::InputSubsystem* input_subsystem, Core::System& system_, - bool enable_web_config = true); + InputCommon::InputSubsystem* input_subsystem, + std::vector<VkDeviceInfo::Record>& vk_device_records, + Core::System& system_, bool enable_web_config = true); ~ConfigureDialog() override; void ApplyConfiguration(); diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index 4315852160..a4965524ab 100644 --- a/src/yuzu/configuration/configure_graphics.cpp +++ b/src/yuzu/configuration/configure_graphics.cpp @@ -1,10 +1,6 @@ // SPDX-FileCopyrightText: 2016 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -// Include this early to include Vulkan headers how we want to -#include "video_core/vulkan_common/vulkan_device.h" -#include "video_core/vulkan_common/vulkan_wrapper.h" - #include <algorithm> #include <functional> #include <iosfwd> @@ -34,13 +30,11 @@ #include "common/settings.h" #include "core/core.h" #include "ui_configure_graphics.h" -#include "video_core/vulkan_common/vulkan_instance.h" -#include "video_core/vulkan_common/vulkan_library.h" -#include "video_core/vulkan_common/vulkan_surface.h" #include "yuzu/configuration/configuration_shared.h" #include "yuzu/configuration/configure_graphics.h" #include "yuzu/qt_common.h" #include "yuzu/uisettings.h" +#include "yuzu/vk_device_info.h" static const std::vector<VkPresentModeKHR> default_present_modes{VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_FIFO_KHR}; @@ -77,9 +71,10 @@ static constexpr Settings::VSyncMode PresentModeToSetting(VkPresentModeKHR mode) } ConfigureGraphics::ConfigureGraphics(const Core::System& system_, + std::vector<VkDeviceInfo::Record>& records_, const std::function<void()>& expose_compute_option_, QWidget* parent) - : QWidget(parent), ui{std::make_unique<Ui::ConfigureGraphics>()}, + : QWidget(parent), ui{std::make_unique<Ui::ConfigureGraphics>()}, records{records_}, expose_compute_option{expose_compute_option_}, system{system_} { vulkan_device = Settings::values.vulkan_device.GetValue(); RetrieveVulkanDevices(); @@ -504,47 +499,19 @@ void ConfigureGraphics::UpdateAPILayout() { } } -void ConfigureGraphics::RetrieveVulkanDevices() try { - if (UISettings::values.has_broken_vulkan) { - return; - } - - using namespace Vulkan; - - auto* window = this->window()->windowHandle(); - auto wsi = QtCommon::GetWindowSystemInfo(window); - - vk::InstanceDispatch dld; - const auto library = OpenLibrary(); - const vk::Instance instance = CreateInstance(*library, dld, VK_API_VERSION_1_1, wsi.type); - const std::vector<VkPhysicalDevice> physical_devices = instance.EnumeratePhysicalDevices(); - vk::SurfaceKHR surface = CreateSurface(instance, wsi); - +void ConfigureGraphics::RetrieveVulkanDevices() { vulkan_devices.clear(); - vulkan_devices.reserve(physical_devices.size()); + vulkan_devices.reserve(records.size()); device_present_modes.clear(); - device_present_modes.reserve(physical_devices.size()); - for (const VkPhysicalDevice device : physical_devices) { - const auto physical_device = vk::PhysicalDevice(device, dld); - const std::string name = physical_device.GetProperties().deviceName; - const std::vector<VkPresentModeKHR> present_modes = - physical_device.GetSurfacePresentModesKHR(*surface); - vulkan_devices.push_back(QString::fromStdString(name)); - device_present_modes.push_back(present_modes); - - VkPhysicalDeviceDriverProperties driver_properties{}; - driver_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES; - driver_properties.pNext = nullptr; - VkPhysicalDeviceProperties2 properties{}; - properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR; - properties.pNext = &driver_properties; - dld.vkGetPhysicalDeviceProperties2(physical_device, &properties); - if (driver_properties.driverID == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) { + device_present_modes.reserve(records.size()); + for (const auto& record : records) { + vulkan_devices.push_back(QString::fromStdString(record.name)); + device_present_modes.push_back(record.vsync_support); + + if (record.has_broken_compute) { expose_compute_option(); } } -} catch (const Vulkan::vk::Exception& exception) { - LOG_ERROR(Frontend, "Failed to enumerate devices with error: {}", exception.what()); } Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const { diff --git a/src/yuzu/configuration/configure_graphics.h b/src/yuzu/configuration/configure_graphics.h index 364b1cac2d..be9310b748 100644 --- a/src/yuzu/configuration/configure_graphics.h +++ b/src/yuzu/configuration/configure_graphics.h @@ -12,6 +12,7 @@ #include <qobjectdefs.h> #include <vulkan/vulkan_core.h> #include "common/common_types.h" +#include "vk_device_info.h" class QEvent; class QObject; @@ -39,6 +40,7 @@ class ConfigureGraphics : public QWidget { public: explicit ConfigureGraphics(const Core::System& system_, + std::vector<VkDeviceInfo::Record>& records, const std::function<void()>& expose_compute_option_, QWidget* parent = nullptr); ~ConfigureGraphics() override; @@ -77,6 +79,7 @@ private: ConfigurationShared::CheckState use_disk_shader_cache; ConfigurationShared::CheckState use_asynchronous_gpu_emulation; + std::vector<VkDeviceInfo::Record>& records; std::vector<QString> vulkan_devices; std::vector<std::vector<VkPresentModeKHR>> device_present_modes; std::vector<VkPresentModeKHR> diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp index 0463ac8b9f..c0a044767a 100644 --- a/src/yuzu/configuration/configure_graphics_advanced.cpp +++ b/src/yuzu/configuration/configure_graphics_advanced.cpp @@ -43,6 +43,8 @@ void ConfigureGraphicsAdvanced::SetConfiguration() { ui->enable_compute_pipelines_checkbox->setChecked( Settings::values.enable_compute_pipelines.GetValue()); ui->use_video_framerate_checkbox->setChecked(Settings::values.use_video_framerate.GetValue()); + ui->barrier_feedback_loops_checkbox->setChecked( + Settings::values.barrier_feedback_loops.GetValue()); if (Settings::IsConfiguringGlobal()) { ui->gpu_accuracy->setCurrentIndex( @@ -94,6 +96,9 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() { enable_compute_pipelines); ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_video_framerate, ui->use_video_framerate_checkbox, use_video_framerate); + ConfigurationShared::ApplyPerGameSetting(&Settings::values.barrier_feedback_loops, + ui->barrier_feedback_loops_checkbox, + barrier_feedback_loops); } void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) { @@ -130,6 +135,8 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() { Settings::values.enable_compute_pipelines.UsingGlobal()); ui->use_video_framerate_checkbox->setEnabled( Settings::values.use_video_framerate.UsingGlobal()); + ui->barrier_feedback_loops_checkbox->setEnabled( + Settings::values.barrier_feedback_loops.UsingGlobal()); return; } @@ -157,6 +164,9 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() { ConfigurationShared::SetColoredTristate(ui->use_video_framerate_checkbox, Settings::values.use_video_framerate, use_video_framerate); + ConfigurationShared::SetColoredTristate(ui->barrier_feedback_loops_checkbox, + Settings::values.barrier_feedback_loops, + barrier_feedback_loops); ConfigurationShared::SetColoredComboBox( ui->gpu_accuracy, ui->label_gpu_accuracy, static_cast<int>(Settings::values.gpu_accuracy.GetValue(true))); diff --git a/src/yuzu/configuration/configure_graphics_advanced.h b/src/yuzu/configuration/configure_graphics_advanced.h index a4dc8ceb09..369a7c83e2 100644 --- a/src/yuzu/configuration/configure_graphics_advanced.h +++ b/src/yuzu/configuration/configure_graphics_advanced.h @@ -48,6 +48,7 @@ private: ConfigurationShared::CheckState use_vulkan_driver_pipeline_cache; ConfigurationShared::CheckState enable_compute_pipelines; ConfigurationShared::CheckState use_video_framerate; + ConfigurationShared::CheckState barrier_feedback_loops; const Core::System& system; }; diff --git a/src/yuzu/configuration/configure_graphics_advanced.ui b/src/yuzu/configuration/configure_graphics_advanced.ui index e7f0ef6be4..d527a6f38b 100644 --- a/src/yuzu/configuration/configure_graphics_advanced.ui +++ b/src/yuzu/configuration/configure_graphics_advanced.ui @@ -202,6 +202,16 @@ Compute pipelines are always enabled on all other drivers.</string> </widget> </item> <item> + <widget class="QCheckBox" name="barrier_feedback_loops_checkbox"> + <property name="toolTip"> + <string>Improves rendering of transparency effects in specific games.</string> + </property> + <property name="text"> + <string>Barrier feedback loops</string> + </property> + </widget> + </item> + <item> <widget class="QWidget" name="af_layout" native="true"> <layout class="QHBoxLayout" name="horizontalLayout_1"> <property name="leftMargin"> diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp index 7ac1625869..eb96e6068c 100644 --- a/src/yuzu/configuration/configure_per_game.cpp +++ b/src/yuzu/configuration/configure_per_game.cpp @@ -6,6 +6,7 @@ #include <memory> #include <string> #include <utility> +#include <vector> #include <fmt/format.h> @@ -34,8 +35,10 @@ #include "yuzu/configuration/configure_system.h" #include "yuzu/uisettings.h" #include "yuzu/util/util.h" +#include "yuzu/vk_device_info.h" ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::string& file_name, + std::vector<VkDeviceInfo::Record>& vk_device_records, Core::System& system_) : QDialog(parent), ui(std::make_unique<Ui::ConfigurePerGame>()), title_id{title_id_}, system{system_} { @@ -50,7 +53,7 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::st general_tab = std::make_unique<ConfigureGeneral>(system_, this); graphics_advanced_tab = std::make_unique<ConfigureGraphicsAdvanced>(system_, this); graphics_tab = std::make_unique<ConfigureGraphics>( - system_, [&]() { graphics_advanced_tab->ExposeComputeOption(); }, this); + system_, vk_device_records, [&]() { graphics_advanced_tab->ExposeComputeOption(); }, this); input_tab = std::make_unique<ConfigureInputPerGame>(system_, game_config.get(), this); system_tab = std::make_unique<ConfigureSystem>(system_, this); diff --git a/src/yuzu/configuration/configure_per_game.h b/src/yuzu/configuration/configure_per_game.h index 85752f1fa7..7ec1ded062 100644 --- a/src/yuzu/configuration/configure_per_game.h +++ b/src/yuzu/configuration/configure_per_game.h @@ -5,11 +5,13 @@ #include <memory> #include <string> +#include <vector> #include <QDialog> #include <QList> #include "core/file_sys/vfs_types.h" +#include "vk_device_info.h" #include "yuzu/configuration/config.h" namespace Core { @@ -45,6 +47,7 @@ class ConfigurePerGame : public QDialog { public: // Cannot use std::filesystem::path due to https://bugreports.qt.io/browse/QTBUG-73263 explicit ConfigurePerGame(QWidget* parent, u64 title_id_, const std::string& file_name, + std::vector<VkDeviceInfo::Record>& vk_device_records, Core::System& system_); ~ConfigurePerGame() override; diff --git a/src/yuzu/configuration/configure_ringcon.ui b/src/yuzu/configuration/configure_ringcon.ui index 514dff372a..38ecccc3dc 100644 --- a/src/yuzu/configuration/configure_ringcon.ui +++ b/src/yuzu/configuration/configure_ringcon.ui @@ -23,7 +23,7 @@ </size> </property> <property name="text"> - <string>If you want to use this controller configure player 1 as right controller and player 2 as dual joycon before starting the game to allow this controller to be detected properly.</string> + <string>To use Ring-Con, configure player 1 as right Joy-Con (both physical and emulated), and player 2 as left Joy-Con (left physical and dual emulated) before starting the game.</string> </property> <property name="wordWrap"> <bool>true</bool> diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp index 286ccc5cd8..f1ae312c65 100644 --- a/src/yuzu/configuration/configure_system.cpp +++ b/src/yuzu/configuration/configure_system.cpp @@ -144,8 +144,7 @@ void ConfigureSystem::ApplyConfiguration() { if (ui->custom_rtc_checkbox->isChecked()) { Settings::values.custom_rtc = ui->custom_rtc_edit->dateTime().toSecsSinceEpoch(); if (system.IsPoweredOn()) { - const s64 posix_time{*Settings::values.custom_rtc + - Service::Time::TimeManager::GetExternalTimeZoneOffset()}; + const s64 posix_time{*Settings::values.custom_rtc}; system.GetTimeManager().UpdateLocalSystemClockTime(posix_time); } } else { diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 9d06b21b63..2133f7343c 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -147,6 +147,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include "yuzu/startup_checks.h" #include "yuzu/uisettings.h" #include "yuzu/util/clickable_label.h" +#include "yuzu/vk_device_info.h" #ifdef YUZU_DBGHELP #include "yuzu/mini_dump.h" @@ -440,10 +441,20 @@ GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan renderer_status_button->setDisabled(true); renderer_status_button->setChecked(false); + } else { + VkDeviceInfo::PopulateRecords(vk_device_records, this->window()->windowHandle()); } #if defined(HAVE_SDL2) && !defined(_WIN32) SDL_InitSubSystem(SDL_INIT_VIDEO); + + // Set a screensaver inhibition reason string. Currently passed to DBus by SDL and visible to + // the user through their desktop environment. + //: TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the + //: computer from sleeping + QByteArray wakelock_reason = tr("Running a game").toLatin1(); + SDL_SetHint(SDL_HINT_SCREENSAVER_INHIBIT_ACTIVITY_NAME, wakelock_reason.data()); + // SDL disables the screen saver by default, and setting the hint // SDL_HINT_VIDEO_ALLOW_SCREENSAVER doesn't seem to work, so we just enable the screen saver // for now. @@ -1620,45 +1631,6 @@ void GMainWindow::OnPrepareForSleep(bool prepare_sleep) { } #ifdef __unix__ -static std::optional<QDBusObjectPath> HoldWakeLockLinux(u32 window_id = 0) { - if (!QDBusConnection::sessionBus().isConnected()) { - return {}; - } - // reference: https://flatpak.github.io/xdg-desktop-portal/#gdbus-org.freedesktop.portal.Inhibit - QDBusInterface xdp(QString::fromLatin1("org.freedesktop.portal.Desktop"), - QString::fromLatin1("/org/freedesktop/portal/desktop"), - QString::fromLatin1("org.freedesktop.portal.Inhibit")); - if (!xdp.isValid()) { - LOG_WARNING(Frontend, "Couldn't connect to XDP D-Bus endpoint"); - return {}; - } - QVariantMap options = {}; - //: TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the - //: computer from sleeping - options.insert(QString::fromLatin1("reason"), - QCoreApplication::translate("GMainWindow", "yuzu is running a game")); - // 0x4: Suspend lock; 0x8: Idle lock - QDBusReply<QDBusObjectPath> reply = - xdp.call(QString::fromLatin1("Inhibit"), - QString::fromLatin1("x11:") + QString::number(window_id, 16), 12U, options); - - if (reply.isValid()) { - return reply.value(); - } - LOG_WARNING(Frontend, "Couldn't read Inhibit reply from XDP: {}", - reply.error().message().toStdString()); - return {}; -} - -static void ReleaseWakeLockLinux(QDBusObjectPath lock) { - if (!QDBusConnection::sessionBus().isConnected()) { - return; - } - QDBusInterface unlocker(QString::fromLatin1("org.freedesktop.portal.Desktop"), lock.path(), - QString::fromLatin1("org.freedesktop.portal.Request")); - unlocker.call(QString::fromLatin1("Close")); -} - std::array<int, 3> GMainWindow::sig_interrupt_fds{0, 0, 0}; void GMainWindow::SetupSigInterrupts() { @@ -1711,12 +1683,6 @@ void GMainWindow::PreventOSSleep() { SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED); #elif defined(HAVE_SDL2) SDL_DisableScreenSaver(); -#ifdef __unix__ - auto reply = HoldWakeLockLinux(winId()); - if (reply) { - wake_lock = std::move(reply.value()); - } -#endif #endif } @@ -1725,11 +1691,6 @@ void GMainWindow::AllowOSSleep() { SetThreadExecutionState(ES_CONTINUOUS); #elif defined(HAVE_SDL2) SDL_EnableScreenSaver(); -#ifdef __unix__ - if (!wake_lock.path().isEmpty()) { - ReleaseWakeLockLinux(wake_lock); - } -#endif #endif } @@ -3067,7 +3028,7 @@ InstallResult GMainWindow::InstallNSPXCI(const QString& filename) { return false; } - std::array<u8, 0x1000> buffer{}; + std::vector<u8> buffer(1_MiB); for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) { if (install_progress->wasCanceled()) { @@ -3494,7 +3455,8 @@ void GMainWindow::OnConfigure() { const auto old_language_index = Settings::values.language_index.GetValue(); Settings::SetConfiguringGlobal(true); - ConfigureDialog configure_dialog(this, hotkey_registry, input_subsystem.get(), *system, + ConfigureDialog configure_dialog(this, hotkey_registry, input_subsystem.get(), + vk_device_records, *system, !multiplayer_state->IsHostingPublicRoom()); connect(&configure_dialog, &ConfigureDialog::LanguageChanged, this, &GMainWindow::OnLanguageChanged); @@ -3765,7 +3727,7 @@ void GMainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file const auto v_file = Core::GetGameFileFromPath(vfs, file_name); Settings::SetConfiguringGlobal(false); - ConfigurePerGame dialog(this, title_id, file_name, *system); + ConfigurePerGame dialog(this, title_id, file_name, vk_device_records, *system); dialog.LoadFromFile(v_file); const auto result = dialog.exec(); @@ -3836,7 +3798,7 @@ void GMainWindow::OnLoadAmiibo() { auto* virtual_amiibo = input_subsystem->GetVirtualAmiibo(); // Remove amiibo if one is connected - if (virtual_amiibo->GetCurrentState() == InputCommon::VirtualAmiibo::State::AmiiboIsOpen) { + if (virtual_amiibo->GetCurrentState() == InputCommon::VirtualAmiibo::State::TagNearby) { virtual_amiibo->CloseAmiibo(); QMessageBox::warning(this, tr("Amiibo"), tr("The current amiibo has been removed")); return; @@ -3864,7 +3826,7 @@ void GMainWindow::LoadAmiibo(const QString& filename) { auto* virtual_amiibo = input_subsystem->GetVirtualAmiibo(); const QString title = tr("Error loading Amiibo data"); // Remove amiibo if one is connected - if (virtual_amiibo->GetCurrentState() == InputCommon::VirtualAmiibo::State::AmiiboIsOpen) { + if (virtual_amiibo->GetCurrentState() == InputCommon::VirtualAmiibo::State::TagNearby) { virtual_amiibo->CloseAmiibo(); QMessageBox::warning(this, tr("Amiibo"), tr("The current amiibo has been removed")); return; diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 6bb70972f9..2cfb962576 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -118,6 +118,10 @@ enum class ReinitializeKeyBehavior { Warning, }; +namespace VkDeviceInfo { +class Record; +} + class GMainWindow : public QMainWindow { Q_OBJECT @@ -418,6 +422,8 @@ private: GameListPlaceholder* game_list_placeholder; + std::vector<VkDeviceInfo::Record> vk_device_records; + // Status bar elements QLabel* message_label = nullptr; QLabel* shader_building_label = nullptr; @@ -498,8 +504,6 @@ private: #ifdef __unix__ QSocketNotifier* sig_interrupt_notifier; static std::array<int, 3> sig_interrupt_fds; - - QDBusObjectPath wake_lock{}; #endif protected: diff --git a/src/yuzu/vk_device_info.cpp b/src/yuzu/vk_device_info.cpp new file mode 100644 index 0000000000..7c26a3dc71 --- /dev/null +++ b/src/yuzu/vk_device_info.cpp @@ -0,0 +1,61 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <utility> +#include <vector> +#include "common/dynamic_library.h" +#include "common/logging/log.h" +#include "video_core/vulkan_common/vulkan_device.h" +#include "video_core/vulkan_common/vulkan_instance.h" +#include "video_core/vulkan_common/vulkan_library.h" +#include "video_core/vulkan_common/vulkan_surface.h" +#include "video_core/vulkan_common/vulkan_wrapper.h" +#include "vulkan/vulkan_core.h" +#include "yuzu/qt_common.h" +#include "yuzu/vk_device_info.h" + +class QWindow; + +namespace VkDeviceInfo { +Record::Record(std::string_view name_, const std::vector<VkPresentModeKHR>& vsync_modes_, + bool has_broken_compute_) + : name{name_}, vsync_support{vsync_modes_}, has_broken_compute{has_broken_compute_} {} + +Record::~Record() = default; + +void PopulateRecords(std::vector<Record>& records, QWindow* window) try { + using namespace Vulkan; + + auto wsi = QtCommon::GetWindowSystemInfo(window); + + vk::InstanceDispatch dld; + const auto library = OpenLibrary(); + const vk::Instance instance = CreateInstance(*library, dld, VK_API_VERSION_1_1, wsi.type); + const std::vector<VkPhysicalDevice> physical_devices = instance.EnumeratePhysicalDevices(); + vk::SurfaceKHR surface = CreateSurface(instance, wsi); + + records.clear(); + records.reserve(physical_devices.size()); + for (const VkPhysicalDevice device : physical_devices) { + const auto physical_device = vk::PhysicalDevice(device, dld); + const std::string name = physical_device.GetProperties().deviceName; + const std::vector<VkPresentModeKHR> present_modes = + physical_device.GetSurfacePresentModesKHR(*surface); + + VkPhysicalDeviceDriverProperties driver_properties{}; + driver_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES; + driver_properties.pNext = nullptr; + VkPhysicalDeviceProperties2 properties{}; + properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR; + properties.pNext = &driver_properties; + dld.vkGetPhysicalDeviceProperties2(physical_device, &properties); + + bool has_broken_compute{Vulkan::Device::CheckBrokenCompute( + driver_properties.driverID, properties.properties.driverVersion)}; + + records.push_back(VkDeviceInfo::Record(name, present_modes, has_broken_compute)); + } +} catch (const Vulkan::vk::Exception& exception) { + LOG_ERROR(Frontend, "Failed to enumerate devices with error: {}", exception.what()); +} +} // namespace VkDeviceInfo diff --git a/src/yuzu/vk_device_info.h b/src/yuzu/vk_device_info.h new file mode 100644 index 0000000000..bda8262f4e --- /dev/null +++ b/src/yuzu/vk_device_info.h @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <algorithm> +#include <iterator> +#include <memory> +#include <string> +#include <string_view> +#include <vector> +#include "common/common_types.h" +#include "vulkan/vulkan_core.h" + +class QWindow; + +namespace Settings { +enum class VSyncMode : u32; +} +// #include "common/settings.h" + +namespace VkDeviceInfo { +// Short class to record Vulkan driver information for configuration purposes +class Record { +public: + explicit Record(std::string_view name, const std::vector<VkPresentModeKHR>& vsync_modes, + bool has_broken_compute); + ~Record(); + + const std::string name; + const std::vector<VkPresentModeKHR> vsync_support; + const bool has_broken_compute; +}; + +void PopulateRecords(std::vector<Record>& records, QWindow* window); +} // namespace VkDeviceInfo diff --git a/vcpkg.json b/vcpkg.json index 26f545c6c2..7d9e631a15 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -1,7 +1,7 @@ { "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg.schema.json", "name": "yuzu", - "builtin-baseline": "656fcc6ab2b05c6d999b7eaca717027ac3738f71", + "builtin-baseline": "cbf56573a987527b39272e88cbdd11389b78c6e4", "version": "1.0", "dependencies": [ "boost-algorithm", |