summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.codespellrc4
-rw-r--r--.gitignore2
-rw-r--r--.gitmodules3
-rw-r--r--CMakeLists.txt2
-rw-r--r--externals/CMakeLists.txt6
-rw-r--r--externals/nx_tzdb/CMakeLists.txt101
-rw-r--r--externals/nx_tzdb/ListFilesInDirectory.cmake8
-rw-r--r--externals/nx_tzdb/NxTzdbCreateHeader.cmake46
-rw-r--r--externals/nx_tzdb/include/nx_tzdb.h27
-rw-r--r--externals/nx_tzdb/tzdb_template.h.in18
m---------externals/nx_tzdb/tzdb_to_nx0
m---------externals/vcpkg0
-rw-r--r--src/android/app/build.gradle.kts73
-rw-r--r--src/android/app/src/main/AndroidManifest.xml6
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt56
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt10
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt205
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt13
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt11
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/applets/keyboard/SoftwareKeyboard.kt29
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress.kt5
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ui/ShaderProgressDialogFragment.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/DocumentProvider.kt89
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt5
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt12
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt7
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt1
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/HeaderSetting.kt8
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SingleChoiceSetting.kt1
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt6
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt21
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt37
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityPresenter.kt12
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt3
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt9
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt104
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt8
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt8
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt35
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt10
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EarlyAccessFragment.kt10
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt242
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt172
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ImportExportSavesFragment.kt27
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt1
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LicensesFragment.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt27
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt23
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/layout/AutofitGridLayoutManager.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt26
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt16
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt345
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableDpad.kt5
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt28
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt8
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt104
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ControllerMappingHelper.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DocumentsTree.kt17
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/EmulationMenuSettings.kt18
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt26
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ForegroundService.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt6
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt6
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverMetadata.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt19
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InsetsHelper.kt12
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NfcReader.kt21
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/SerializableHelper.kt20
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt14
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/views/FixedRatioSurfaceView.kt6
-rw-r--r--src/android/app/src/main/jni/config.cpp8
-rw-r--r--src/android/app/src/main/jni/default_ini.h6
-rw-r--r--src/android/app/src/main/jni/native.cpp153
-rw-r--r--src/android/app/src/main/res/drawable/ic_pip_pause.xml9
-rw-r--r--src/android/app/src/main/res/drawable/ic_pip_play.xml9
-rw-r--r--src/android/app/src/main/res/drawable/ic_system_update_alt.xml9
-rw-r--r--src/android/app/src/main/res/layout/activity_emulation.xml16
-rw-r--r--src/android/app/src/main/res/layout/fragment_emulation.xml82
-rw-r--r--src/android/app/src/main/res/layout/list_item_setting_switch.xml55
-rw-r--r--src/android/app/src/main/res/layout/list_item_settings_header.xml28
-rw-r--r--src/android/app/src/main/res/navigation/emulation_navigation.xml18
-rw-r--r--src/android/app/src/main/res/navigation/home_navigation.xml14
-rw-r--r--src/android/app/src/main/res/values-de/strings.xml332
-rw-r--r--src/android/app/src/main/res/values-es/strings.xml337
-rw-r--r--src/android/app/src/main/res/values-fr/strings.xml337
-rw-r--r--src/android/app/src/main/res/values-it/strings.xml337
-rw-r--r--src/android/app/src/main/res/values-ja/strings.xml335
-rw-r--r--src/android/app/src/main/res/values-ko/strings.xml337
-rw-r--r--src/android/app/src/main/res/values-nb/strings.xml337
-rw-r--r--src/android/app/src/main/res/values-pl/strings.xml337
-rw-r--r--src/android/app/src/main/res/values-pt-rBR/strings.xml337
-rw-r--r--src/android/app/src/main/res/values-pt-rPT/strings.xml337
-rw-r--r--src/android/app/src/main/res/values-ru/strings.xml337
-rw-r--r--src/android/app/src/main/res/values-uk/strings.xml337
-rw-r--r--src/android/app/src/main/res/values-zh-rCN/strings.xml337
-rw-r--r--src/android/app/src/main/res/values-zh-rTW/strings.xml336
-rw-r--r--src/android/app/src/main/res/values/arrays.xml23
-rw-r--r--src/android/app/src/main/res/values/integers.xml64
-rw-r--r--src/android/app/src/main/res/values/strings.xml50
-rw-r--r--src/android/app/src/main/res/xml/locales_config.xml17
-rw-r--r--src/android/build.gradle.kts9
-rw-r--r--src/audio_core/audio_core.cpp8
-rw-r--r--src/audio_core/audio_core.h14
-rw-r--r--src/common/fs/fs.cpp27
-rw-r--r--src/common/fs/fs_android.h5
-rw-r--r--src/common/fs/fs_paths.h1
-rw-r--r--src/common/fs/path_util.cpp1
-rw-r--r--src/common/fs/path_util.h1
-rw-r--r--src/common/settings.cpp32
-rw-r--r--src/common/settings.h2
-rw-r--r--src/common/time_zone.cpp63
-rw-r--r--src/common/time_zone.h6
-rw-r--r--src/common/uuid.cpp2
-rw-r--r--src/core/CMakeLists.txt11
-rw-r--r--src/core/arm/arm_interface.cpp84
-rw-r--r--src/core/arm/arm_interface.h37
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.h29
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp64
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.h16
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp64
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.h18
-rw-r--r--src/core/arm/dynarmic/dynarmic_cp15.cpp (renamed from src/core/arm/dynarmic/arm_dynarmic_cp15.cpp)2
-rw-r--r--src/core/arm/dynarmic/dynarmic_cp15.h (renamed from src/core/arm/dynarmic/arm_dynarmic_cp15.h)0
-rw-r--r--src/core/arm/dynarmic/dynarmic_exclusive_monitor.cpp (renamed from src/core/arm/dynarmic/arm_exclusive_monitor.cpp)2
-rw-r--r--src/core/arm/dynarmic/dynarmic_exclusive_monitor.h (renamed from src/core/arm/dynarmic/arm_exclusive_monitor.h)0
-rw-r--r--src/core/arm/exclusive_monitor.cpp2
-rw-r--r--src/core/core.cpp44
-rw-r--r--src/core/core.h11
-rw-r--r--src/core/file_sys/submission_package.h1
-rw-r--r--src/core/file_sys/system_archive/time_zone_binary.cpp706
-rw-r--r--src/core/file_sys/vfs_concat.cpp14
-rw-r--r--src/core/file_sys/vfs_real.cpp187
-rw-r--r--src/core/file_sys/vfs_real.h30
-rw-r--r--src/core/hle/kernel/k_thread.cpp15
-rw-r--r--src/core/hle/kernel/k_thread.h4
-rw-r--r--src/core/hle/service/nfc/common/amiibo_crypto.cpp44
-rw-r--r--src/core/hle/service/nfc/common/amiibo_crypto.h9
-rw-r--r--src/core/hle/service/nfc/common/device.cpp300
-rw-r--r--src/core/hle/service/nfc/common/device.h15
-rw-r--r--src/core/hle/service/nfc/common/device_manager.cpp14
-rw-r--r--src/core/hle/service/nfc/mifare_result.h2
-rw-r--r--src/core/hle/service/nfc/nfc_interface.cpp21
-rw-r--r--src/core/hle/service/nfc/nfc_result.h21
-rw-r--r--src/core/hle/service/nfc/nfc_types.h37
-rw-r--r--src/core/hle/service/nfp/nfp_interface.cpp6
-rw-r--r--src/core/hle/service/nfp/nfp_result.h2
-rw-r--r--src/core/hle/service/nfp/nfp_types.h26
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp4
-rw-r--r--src/core/hle/service/nvnflinger/nvnflinger.cpp4
-rw-r--r--src/core/hle/service/time/time_manager.cpp34
-rw-r--r--src/core/hle/service/time/time_manager.h4
-rw-r--r--src/core/hle/service/time/time_zone_content_manager.cpp26
-rw-r--r--src/core/hle/service/time/time_zone_manager.cpp161
-rw-r--r--src/core/hle/service/time/time_zone_manager.h8
-rw-r--r--src/core/hle/service/time/time_zone_service.cpp67
-rw-r--r--src/core/hle/service/time/time_zone_service.h3
-rw-r--r--src/core/loader/nro.cpp13
-rw-r--r--src/core/loader/nro.h2
-rw-r--r--src/input_common/drivers/virtual_amiibo.cpp2
-rw-r--r--src/input_common/drivers/virtual_amiibo.h1
-rw-r--r--src/shader_recompiler/CMakeLists.txt2
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate_program.cpp6
-rw-r--r--src/shader_recompiler/host_translate_info.h3
-rw-r--r--src/shader_recompiler/ir_opt/conditional_barrier_pass.cpp44
-rw-r--r--src/shader_recompiler/ir_opt/lower_fp64_to_fp32.cpp185
-rw-r--r--src/shader_recompiler/ir_opt/passes.h2
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h75
-rw-r--r--src/video_core/buffer_cache/buffer_cache_base.h12
-rw-r--r--src/video_core/engines/draw_manager.cpp10
-rw-r--r--src/video_core/memory_manager.cpp4
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.cpp17
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.h6
-rw-r--r--src/video_core/renderer_opengl/gl_compute_pipeline.cpp23
-rw-r--r--src/video_core/renderer_opengl/gl_device.cpp1
-rw-r--r--src/video_core/renderer_opengl/gl_device.h5
-rw-r--r--src/video_core/renderer_opengl/gl_graphics_pipeline.cpp27
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_shader_context.h6
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.cpp72
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.h11
-rw-r--r--src/video_core/renderer_vulkan/pipeline_helper.h11
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.cpp55
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.cpp48
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.h5
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pipeline.cpp8
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp8
-rw-r--r--src/video_core/renderer_vulkan/vk_master_semaphore.cpp1
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp7
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.h6
-rw-r--r--src/video_core/renderer_vulkan/vk_swapchain.cpp5
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp51
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.h9
-rw-r--r--src/video_core/texture_cache/image_info.cpp20
-rw-r--r--src/video_core/texture_cache/image_view_base.cpp52
-rw-r--r--src/video_core/texture_cache/image_view_base.h2
-rw-r--r--src/video_core/texture_cache/texture_cache.h56
-rw-r--r--src/video_core/texture_cache/texture_cache_base.h22
-rw-r--r--src/video_core/textures/texture.cpp7
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp28
-rw-r--r--src/video_core/vulkan_common/vulkan_device.h44
-rw-r--r--src/yuzu/CMakeLists.txt2
-rw-r--r--src/yuzu/configuration/config.cpp10
-rw-r--r--src/yuzu/configuration/config.h1
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp5
-rw-r--r--src/yuzu/configuration/configure_dialog.h7
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp55
-rw-r--r--src/yuzu/configuration/configure_graphics.h3
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.cpp18
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.h2
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.ui20
-rw-r--r--src/yuzu/configuration/configure_per_game.cpp5
-rw-r--r--src/yuzu/configuration/configure_per_game.h3
-rw-r--r--src/yuzu/configuration/configure_system.cpp3
-rw-r--r--src/yuzu/main.cpp18
-rw-r--r--src/yuzu/main.h6
-rw-r--r--src/yuzu/vk_device_info.cpp61
-rw-r--r--src/yuzu/vk_device_info.h36
-rw-r--r--vcpkg.json2
221 files changed, 8876 insertions, 2176 deletions
diff --git a/.codespellrc b/.codespellrc
index 786a991eb..01ddd2362 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
-ignore-words-list = aci,allright,ba,deques,froms,hda,inout,lod,masia,nam,nax,nd,pullrequests,pullrequest,te,transfered,unstall,uscaled,zink
+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 a5f7248c7..fbadb208b 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 95eae8109..89f2ad924 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 3d03bbf94..6d3146c9e 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
diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt
index 500eb21e3..7cce27d51 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/nx_tzdb/CMakeLists.txt b/externals/nx_tzdb/CMakeLists.txt
new file mode 100644
index 000000000..593786250
--- /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 000000000..35a9e726a
--- /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 000000000..8c29e1167
--- /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 000000000..1f7c6069a
--- /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 000000000..289d002ea
--- /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 8c272f21d19c6e821345fd055f41b9640f9189d
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 13bb227ff..bab4f4d0f 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")
@@ -74,16 +82,7 @@ android {
// Signed by release key, allowing for upload to Play Store.
release {
- signingConfig = signingConfigs.getByName("debug")
- isMinifyEnabled = true
- isDebuggable = false
- proguardFiles(
- getDefaultProguardFile("proguard-android.txt"),
- "proguard-rules.pro"
- )
- }
-
- register("relWithVersionCode") {
+ resValue("string", "app_name_suffixed", "yuzu")
signingConfig = signingConfigs.getByName("debug")
isMinifyEnabled = true
isDebuggable = false
@@ -96,6 +95,7 @@ android {
// builds a release build that doesn't need signing
// Attaches 'debug' suffix to version and package name, allowing installation alongside the release build.
register("relWithDebInfo") {
+ resValue("string", "app_name_suffixed", "yuzu Debug Release")
signingConfig = signingConfigs.getByName("debug")
isMinifyEnabled = true
isDebuggable = true
@@ -103,16 +103,19 @@ android {
getDefaultProguardFile("proguard-android.txt"),
"proguard-rules.pro"
)
- versionNameSuffix = "-debug"
+ versionNameSuffix = "-relWithDebInfo"
+ applicationIdSuffix = ".relWithDebInfo"
isJniDebuggable = true
}
// Signed by debug key disallowing distribution on Play Store.
// Attaches 'debug' suffix to version and package name, allowing installation alongside the release build.
debug {
+ resValue("string", "app_name_suffixed", "yuzu Debug")
isDebuggable = true
isJniDebuggable = true
versionNameSuffix = "-debug"
+ applicationIdSuffix = ".debug"
}
}
@@ -157,24 +160,42 @@ 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")
implementation("androidx.recyclerview:recyclerview:1.3.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
- implementation("androidx.fragment:fragment-ktx:1.5.7")
+ implementation("androidx.fragment:fragment-ktx:1.6.0")
implementation("androidx.documentfile:documentfile:1.0.1")
implementation("com.google.android.material:material:1.9.0")
implementation("androidx.preference:preference:1.2.0")
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1")
implementation("io.coil-kt:coil:2.2.2")
implementation("androidx.core:core-splashscreen:1.0.1")
- implementation("androidx.window:window:1.0.0")
+ implementation("androidx.window:window:1.1.0")
implementation("org.ini4j:ini4j:0.5.4")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
- implementation("androidx.navigation:navigation-fragment-ktx:2.5.3")
- implementation("androidx.navigation:navigation-ui-ktx:2.5.3")
+ implementation("androidx.navigation:navigation-fragment-ktx:2.6.0")
+ implementation("androidx.navigation:navigation-ui-ktx:2.6.0")
implementation("info.debatty:java-string-similarity:2.0.0")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0")
}
diff --git a/src/android/app/src/main/AndroidManifest.xml b/src/android/app/src/main/AndroidManifest.xml
index eef566042..e31ad69e2 100644
--- a/src/android/app/src/main/AndroidManifest.xml
+++ b/src/android/app/src/main/AndroidManifest.xml
@@ -18,12 +18,13 @@ SPDX-License-Identifier: GPL-3.0-or-later
<application
android:name="org.yuzu.yuzu_emu.YuzuApplication"
- android:label="@string/app_name"
+ android:label="@string/app_name_suffixed"
android:icon="@drawable/ic_launcher"
android:allowBackup="true"
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 c11b6bc16..f860cdd4b 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)
+ }
}
/**
@@ -223,8 +249,12 @@ object NativeLibrary {
external fun getCompany(filename: String): String
+ external fun isHomebrew(filename: String): Boolean
+
external fun setAppDirectory(directory: String)
+ external fun installFileToNand(filename: String): Int
+
external fun initializeGpuDriver(
hookLibDir: String?,
customDriverDir: String?,
@@ -279,6 +309,11 @@ object NativeLibrary {
external fun isRunning(): Boolean
/**
+ * Returns true if emulation is paused.
+ */
+ external fun isPaused(): Boolean
+
+ /**
* Returns the performance stats for the current game
*/
external fun getPerfStats(): DoubleArray
@@ -427,7 +462,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()
@@ -505,4 +542,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 4c947b786..04ab6a220 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 94d5156cf..f0a6753a9 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,17 +4,23 @@
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.hardware.display.DisplayManager
+import android.os.Build
import android.os.Bundle
-import android.view.Display
+import android.util.Rational
import android.view.InputDevice
import android.view.KeyEvent
import android.view.MotionEvent
@@ -23,35 +29,30 @@ import android.view.View
import android.view.inputmethod.InputMethodManager
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
-import androidx.core.content.getSystemService
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 kotlin.math.roundToInt
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.EmulationMenuSettings
import org.yuzu.yuzu_emu.utils.ForegroundService
import org.yuzu.yuzu_emu.utils.InputHandler
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
@@ -60,7 +61,8 @@ 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 settingsViewModel: SettingsViewModel by viewModels()
@@ -75,47 +77,31 @@ 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) }
- }
- }
-
// Start a foreground service to prevent the app from getting killed in the background
val startIntent = Intent(this, ForegroundService::class.java)
startForegroundService(startIntent)
@@ -149,10 +135,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
nfcReader.startScanning()
startMotionSensorListener()
- NativeLibrary.notifyOrientationChange(
- EmulationMenuSettings.landscapeScreenLayout,
- getAdjustedRotation()
- )
+ buildPictureInPictureParams()
}
override fun onPause() {
@@ -161,17 +144,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
@@ -258,34 +246,107 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
override fun onAccuracyChanged(sensor: Sensor, i: Int) {}
- private fun getAdjustedRotation():Int {
- val rotation = getSystemService<DisplayManager>()!!.getDisplay(Display.DEFAULT_DISPLAY).rotation
- val config: Configuration = resources.configuration
+ private fun enableFullscreenImmersive() {
+ WindowCompat.setDecorFitsSystemWindows(window, false)
- if ((config.screenLayout and Configuration.SCREENLAYOUT_LONG_YES) != 0 ||
- (config.screenLayout and Configuration.SCREENLAYOUT_LONG_NO) == 0) {
- return rotation
+ WindowInsetsControllerCompat(window, window.decorView).let { controller ->
+ controller.hide(WindowInsetsCompat.Type.systemBars())
+ controller.systemBarsBehavior =
+ WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
}
- when (rotation) {
- Surface.ROTATION_0 -> return Surface.ROTATION_90
- Surface.ROTATION_90 -> return Surface.ROTATION_0
- Surface.ROTATION_180 -> return Surface.ROTATION_270
- Surface.ROTATION_270 -> return Surface.ROTATION_180
+ }
+
+ 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 rotation
+ return this.apply { aspectRatio?.let { setAspectRatio(it) } }
}
- private fun restoreState(savedInstanceState: Bundle) {
- game = savedInstanceState.parcelable(EXTRA_SELECTED_GAME)!!
+ 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)
+ }
+
+ return this.apply { setActions(pictureInPictureActions) }
}
- private fun enableFullscreenImmersive() {
- WindowCompat.setDecorFitsSystemWindows(window, false)
+ 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())
+ }
- WindowInsetsControllerCompat(window, window.decorView).let { controller ->
- controller.hide(WindowInsetsCompat.Type.systemBars())
- controller.systemBarsBehavior =
- WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
+ 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()
+ }
+ buildPictureInPictureParams()
+ }
+ }
+
+ override fun onPictureInPictureModeChanged(
+ isInPictureInPictureMode: Boolean,
+ newConfig: Configuration
+ ) {
+ super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)
+ if (isInPictureInPictureMode) {
+ IntentFilter().apply {
+ addAction(actionPause)
+ addAction(actionPlay)
+ }.also {
+ registerReceiver(pictureInPictureReceiver, it)
+ }
+ } else {
+ try {
+ unregisterReceiver(pictureInPictureReceiver)
+ } catch (ignored: Exception) {
+ }
}
}
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 7f9e2e2d4..e91277d35 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 b719dd539..d3df3bc81 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 82a6712b6..e058067c9 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 3b1559c80..a18efef19 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 2c68c9ac3..8a8e0a6e8 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 4c3a9ca80..f3be156b5 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 3dfd66779..d41933766 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 c5722a5a1..4427a7d9d 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 8df20b928..88afb2223 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 63f95690c..6621289fd 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 0f8edbfb0..a67001311 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 9eac9904e..7306ec458 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 842648ce4..92d0167ae 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 9e9b00d10..3b6731dcd 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 a3ef59c2f..8a9d13a92 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 72e2cce2a..a5af5a7ae 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 4361d95fb..93e677b21 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 1eb4899fc..ce0b92c90 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 867147950..70a74c4dd 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 061046b2e..59c1d9d54 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 04c045e77..7955532ee 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 de764a27f..e4e321bd3 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 b163bd6ca..54f531795 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 e29bca11d..70a52df5d 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 c92e2755c..2ff827c6b 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 d8bbc1ce4..dbc16da4a 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 9523381cd..4643418c1 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."
+ )
}
}
}
@@ -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 bdc337501..5a36ffad4 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 36e63bb9e..e1495ee8c 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 c7880d8cc..739b26f99 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 59141e823..b6e9129f7 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/SearchFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt
index ebc0f164a..dd6c895fd 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
@@ -29,8 +30,6 @@ 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
@@ -127,24 +126,18 @@ class SearchFragment : Fragment() {
}
}
- R.id.chip_homebrew -> {
- baseList.filter {
- Log.error("Guh - ${it.path}")
- FileUtil.hasExtension(it.path, "nro")
- || FileUtil.hasExtension(it.path, "nso")
- }
- }
+ R.id.chip_homebrew -> baseList.filter { it.isHomebrew }
R.id.chip_retail -> baseList.filter {
- FileUtil.hasExtension(it.path, "xci")
- || FileUtil.hasExtension(it.path, "nsp")
+ FileUtil.hasExtension(it.path, "xci") ||
+ FileUtil.hasExtension(it.path, "nsp")
}
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
@@ -179,14 +172,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 258773380..6c4ddaf6b 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 be5e4c86c..bdd6ea628 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 2a17653b2..6a048e39f 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
@@ -16,21 +16,29 @@ class Game(
val regions: String,
val path: String,
val gameId: String,
- val company: String
+ val company: String,
+ val isHomebrew: Boolean
) : Parcelable {
val keyAddedToLibraryTime get() = "${gameId}_AddedToLibraryTime"
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
+ 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 {
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 7059856f1..1fe42f922 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,16 +10,19 @@ 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
+import kotlinx.serialization.ExperimentalSerializationApi
+import kotlinx.serialization.MissingFieldException
import kotlinx.serialization.decodeFromString
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() {
private val _games = MutableLiveData<List<Game>>(emptyList())
val games: LiveData<List<Game>> get() = _games
@@ -49,7 +52,13 @@ class GamesViewModel : ViewModel() {
if (storedGames!!.isNotEmpty()) {
val deserializedGames = mutableSetOf<Game>()
storedGames.forEach {
- val game: Game = Json.decodeFromString(it)
+ val game: Game
+ try {
+ game = Json.decodeFromString(it)
+ } catch (e: MissingFieldException) {
+ return@forEach
+ }
+
val gameExists =
DocumentFile.fromSingleUri(YuzuApplication.appContext, Uri.parse(game.path))
?.exists()
@@ -90,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 c9f5797ac..6251ec783 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
@@ -765,18 +874,26 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
// If we have API access, calculate the safe area to draw the overlay
var cutoutLeft = 0
var cutoutBottom = 0
- val insets = context.windowManager.currentWindowMetrics.windowInsets.displayCutout
- if (insets != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
- 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
-
- minX = insets.boundingRectLeft.right - insets.boundingRectLeft.left
- minY = insets.boundingRectBottom.top - insets.boundingRectBottom.bottom
-
- cutoutLeft = insets.boundingRectRight.right - insets.boundingRectRight.left
- cutoutBottom = insets.boundingRectTop.top - insets.boundingRectTop.bottom
+ 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
+ ) {
+ 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
+
+ cutoutLeft = insets.boundingRectRight.right - insets.boundingRectRight.left
+ cutoutBottom = insets.boundingRectTop.top - insets.boundingRectTop.bottom
+ }
}
// This makes sure that if we have an inset on one side of the screen, we mirror it on
@@ -876,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()
@@ -957,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
@@ -1024,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 43d664d21..8aef6f5a5 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 f1d32192a..fb48f584d 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 97eef40d2..b0156dca5 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 124f62f08..cc1d87f1b 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
@@ -26,6 +26,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
@@ -43,9 +46,6 @@ 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 +86,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 +174,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 +193,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 +240,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 +264,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
val getGamesDirectory =
registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result ->
- if (result == null)
+ if (result == null) {
return@registerForActivityResult
+ }
contentResolver.takePersistableUriPermission(
result,
@@ -281,13 +290,14 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
val getProdKey =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
- if (result == null)
+ if (result == null) {
return@registerForActivityResult
+ }
- if (!FileUtil.hasExtension(result.toString(), "keys")) {
+ if (!FileUtil.hasExtension(result, "keys")) {
MessageDialogFragment.newInstance(
R.string.reading_keys_failure,
- R.string.install_keys_failure_extension_description
+ R.string.install_prod_keys_failure_extension_description
).show(supportFragmentManager, MessageDialogFragment.TAG)
return@registerForActivityResult
}
@@ -324,8 +334,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,13 +387,14 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
val getAmiiboKey =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
- if (result == null)
+ if (result == null) {
return@registerForActivityResult
+ }
- if (!FileUtil.hasExtension(result.toString(), "bin")) {
+ if (!FileUtil.hasExtension(result, "bin")) {
MessageDialogFragment.newInstance(
R.string.reading_keys_failure,
- R.string.install_keys_failure_extension_description
+ R.string.install_amiibo_keys_failure_extension_description
).show(supportFragmentManager, MessageDialogFragment.TAG)
return@registerForActivityResult
}
@@ -418,8 +430,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 +480,63 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
}
}
}
+
+ val installGameUpdate =
+ registerForActivityResult(ActivityResultContracts.OpenDocument()) {
+ if (it == null) {
+ return@registerForActivityResult
+ }
+
+ IndeterminateProgressDialogFragment.newInstance(
+ this@MainActivity,
+ R.string.install_game_content
+ ) {
+ val result = NativeLibrary.installFileToNand(it.toString())
+ lifecycleScope.launch {
+ withContext(Dispatchers.Main) {
+ when (result) {
+ NativeLibrary.InstallFileToNandResult.Success -> {
+ Toast.makeText(
+ applicationContext,
+ R.string.install_game_content_success,
+ Toast.LENGTH_SHORT
+ ).show()
+ }
+
+ NativeLibrary.InstallFileToNandResult.SuccessFileOverwritten -> {
+ Toast.makeText(
+ applicationContext,
+ R.string.install_game_content_success_overwrite,
+ Toast.LENGTH_SHORT
+ ).show()
+ }
+
+ NativeLibrary.InstallFileToNandResult.ErrorBaseGame -> {
+ MessageDialogFragment.newInstance(
+ R.string.install_game_content_failure,
+ R.string.install_game_content_failure_base
+ ).show(supportFragmentManager, MessageDialogFragment.TAG)
+ }
+
+ NativeLibrary.InstallFileToNandResult.ErrorFilenameExtension -> {
+ MessageDialogFragment.newInstance(
+ R.string.install_game_content_failure,
+ R.string.install_game_content_failure_file_extension,
+ R.string.install_game_content_help_link
+ ).show(supportFragmentManager, MessageDialogFragment.TAG)
+ }
+
+ else -> {
+ MessageDialogFragment.newInstance(
+ R.string.install_game_content_failure,
+ R.string.install_game_content_failure_description,
+ R.string.install_game_content_help_link
+ ).show(supportFragmentManager, MessageDialogFragment.TAG)
+ }
+ }
+ }
+ }
+ return@newInstance result
+ }.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 791cea904..eeefcdf20 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 36c479e6c..2ee63697e 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 cc8ea6b9d..cf226ad94 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 e1e7a59d7..7e8f058c1 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 593dad8d3..9f3bbe56f 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,8 +7,8 @@ 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.model.MinimalDocumentFile
import java.io.BufferedInputStream
import java.io.File
import java.io.FileOutputStream
@@ -17,6 +17,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"
@@ -324,7 +326,25 @@ object FileUtil {
}
}
- fun hasExtension(path: String, extension: String): Boolean {
- return path.substring(path.lastIndexOf(".") + 1).contains(extension)
+ 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)
}
}
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 dc9b7c744..086d17606 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 ba6b5783e..ee9f3e570 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
@@ -6,13 +6,12 @@ package org.yuzu.yuzu_emu.utils
import android.content.SharedPreferences
import android.net.Uri
import androidx.preference.PreferenceManager
-import kotlinx.serialization.decodeFromString
+import java.util.*
import kotlinx.serialization.encodeToString
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"
@@ -83,7 +82,8 @@ object GameHelper {
NativeLibrary.getRegions(filePath),
filePath,
gameId,
- NativeLibrary.getCompany(filePath)
+ NativeLibrary.getCompany(filePath),
+ NativeLibrary.isHomebrew(filePath)
)
val addedTime = preferences.getLong(newGame.keyAddedToLibraryTime, 0L)
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 528011d7f..1d4695a2a 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 70bdb4097..a4e64070a 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 24e999b29..e963dfbc1 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 19c53c481..595f0d284 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/NfcReader.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NfcReader.kt
index 344dd8a0a..68ed66565 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 87ee7f2e6..00e58faec 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 e55767c0f..f312e24cf 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 c8ef8c1fd..685ccaa76 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/config.cpp b/src/android/app/src/main/jni/config.cpp
index 2d622a048..43e8aa72a 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 c5dfaff54..d81422a74 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 b87e04b3d..632aa50b3 100644
--- a/src/android/app/src/main/jni/native.cpp
+++ b/src/android/app/src/main/jni/native.cpp
@@ -13,6 +13,7 @@
#include <android/api-level.h>
#include <android/native_window_jni.h>
+#include <core/loader/nro.h>
#include "common/detached_tasks.h"
#include "common/dynamic_library.h"
@@ -27,7 +28,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"
@@ -93,12 +97,72 @@ public:
m_native_window = native_window;
}
- u32 ScreenRotation() const {
- return m_screen_rotation;
- }
+ int InstallFileToNand(std::string filename) {
+ const auto 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;
+ std::vector<u8> buffer(1_MiB);
+
+ for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) {
+ const auto 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);
+
+ 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")) {
+ const auto xci =
+ std::make_shared<FileSys::XCI>(m_vfs->OpenFile(filename, FileSys::Mode::Read));
+ nsp = xci->GetSecurePartitionNSP();
+ } else {
+ return ErrorFilenameExtension;
+ }
- void SetScreenRotation(u32 screen_rotation) {
- m_screen_rotation = screen_rotation;
+ if (!nsp) {
+ return InstallError;
+ }
+
+ if (nsp->GetStatus() != Loader::ResultStatus::Success) {
+ return InstallError;
+ }
+
+ const auto 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,
@@ -138,6 +202,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;
@@ -161,14 +230,14 @@ 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>();
m_software_keyboard = android_keyboard.get();
m_system.SetShuttingDown(false);
m_system.ApplySettings();
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
@@ -180,7 +249,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>();
@@ -222,11 +292,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() {
@@ -281,6 +353,10 @@ public:
return GetRomMetadata(path).icon;
}
+ bool GetIsHomebrew(const std::string& path) {
+ return GetRomMetadata(path).isHomebrew;
+ }
+
void ResetRomMetadata() {
m_rom_metadata_cache.clear();
}
@@ -348,6 +424,7 @@ private:
struct RomMetadata {
std::string title;
std::vector<u8> icon;
+ bool isHomebrew;
};
RomMetadata GetRomMetadata(const std::string& path) {
@@ -360,11 +437,17 @@ private:
RomMetadata CacheRomMetadata(const std::string& path) {
const auto file = Core::GetGameFileFromPath(m_vfs, path);
- const auto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0);
+ auto 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());
+ entry.isHomebrew = loader_nro->IsHomebrew();
+ } else {
+ entry.isHomebrew = false;
+ }
m_rom_metadata_cache[path] = entry;
@@ -388,16 +471,16 @@ private:
// Window management
std::unique_ptr<EmuWindow_Android> m_window;
ANativeWindow* m_native_window{};
- u32 m_screen_rotation{};
// Core emulation
Core::System m_system;
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;
@@ -414,10 +497,6 @@ private:
} // Anonymous namespace
-u32 GetAndroidScreenRotation() {
- return EmulationSession::GetInstance().ScreenRotation();
-}
-
static Core::SystemResultStatus RunEmulation(const std::string& filepath) {
Common::Log::Initialize();
Common::Log::SetColorConsoleBackendEnabled(true);
@@ -461,19 +540,18 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceDestroyed(JNIEnv* env,
EmulationSession::GetInstance().SurfaceChanged();
}
-void Java_org_yuzu_yuzu_1emu_NativeLibrary_notifyOrientationChange(JNIEnv* env,
- [[maybe_unused]] jclass clazz,
- jint layout_option,
- jint rotation) {
- return EmulationSession::GetInstance().SetScreenRotation(static_cast<u32>(rotation));
-}
-
void Java_org_yuzu_yuzu_1emu_NativeLibrary_setAppDirectory(JNIEnv* env,
[[maybe_unused]] jclass clazz,
jstring j_directory) {
Common::FS::SetAppDirectory(GetJString(env, j_directory));
}
+int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env,
+ [[maybe_unused]] jclass clazz,
+ jstring j_file) {
+ return EmulationSession::GetInstance().InstallFileToNand(GetJString(env, j_file));
+}
+
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) {
@@ -482,6 +560,26 @@ void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeGpuDriver(
GetJString(env, custom_driver_name), GetJString(env, file_redirect_dir));
}
+[[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(
+ [[maybe_unused]] JNIEnv* env, [[maybe_unused]] 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,
[[maybe_unused]] jclass clazz) {
Core::Crypto::KeyManager::Instance().ReloadKeys();
@@ -513,6 +611,11 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isRunning([[maybe_unused]] JNIEnv
return static_cast<jboolean>(EmulationSession::GetInstance().IsRunning());
}
+jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isPaused([[maybe_unused]] JNIEnv* env,
+ [[maybe_unused]] jclass clazz) {
+ return static_cast<jboolean>(EmulationSession::GetInstance().IsPaused());
+}
+
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHandheldOnly([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz) {
return EmulationSession::GetInstance().IsHandheldOnly();
@@ -662,6 +765,12 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCompany([[maybe_unused]] JNIEnv
return env->NewStringUTF("");
}
+jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHomebrew([[maybe_unused]] JNIEnv* env,
+ [[maybe_unused]] jclass clazz,
+ [[maybe_unused]] 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) {
// Create the default config.ini.
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 000000000..4a7d4ea03
--- /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 000000000..2303a4623
--- /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_system_update_alt.xml b/src/android/app/src/main/res/drawable/ic_system_update_alt.xml
new file mode 100644
index 000000000..0f6adfdb8
--- /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 f6360a65b..139065d3d 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 09b789b6b..e54a10e8f 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 599d845ad..a5767adee 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 abd24df6f..cf85bc0da 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 000000000..8208f4c2c
--- /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 48072683e..fcebba726 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 000000000..969223ef8
--- /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.&lt;br /&gt;&lt;br /&gt;Bevor du beginnst, bitte halte deine <![CDATA[<b> prod.keys </b>]]> auf deinem Gerät bereit. .&lt;br /&gt;&lt;br /&gt;<![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 &lt;b>yuzu&lt;/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 &lt;b>prod.keys&lt;/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 &lt;b>Spiele&lt;/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 000000000..986e80e50
--- /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.&lt;br /&gt;&lt;br /&gt;Antes de empezar, por favor, localice el archivo <![CDATA[<b> prod.keys </b>]]>en el almacenamiento de su dispositivo..&lt;br /&gt;&lt;br /&gt;<![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 &lt;b>yuzu&lt;/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 &lt;b>prod.keys&lt;/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 &lt;b>Games&lt;/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 000000000..14a9b2d5c
--- /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.&lt;br /&gt;&lt;br /&gt;Avant de commencer, veuillez localiser votre fichier <![CDATA[<b> prod.keys </b>]]> sur le stockage de votre appareil.&lt;br /&gt;&lt;br /&gt;<![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 &lt;b>yuzu&lt;/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 &lt;b>prod.keys&lt;/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 &lt;b>de Jeux&lt;/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 000000000..47a4cfa31
--- /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.&lt;br /&gt;&lt;br /&gt;Prima di iniziare, perfavore individua il file <![CDATA[<b>prod.keys </b>]]> nella memoria del tuo dispositivo.&lt;br /&gt;&lt;br /&gt;<![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 &lt;b>yuzu&lt;/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 &lt;b>prod.keys&lt;/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 &lt;b>Games&lt;/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 000000000..46eda9ef7
--- /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用のゲームを実行します。 ゲームソフトやキーは含まれません。&lt;br /&gt;&lt;br /&gt;事前に、 <![CDATA[<b> prod.keys </b>]]> ファイルをデバイスのストレージに配置しておいてください。&lt;br /&gt;&lt;br /&gt;<![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">&lt;b>yuzu&lt;/b> のセットアップ方法を学び、エミュレーションに飛び込みましょう。</string>
+ <string name="get_started">はじめる</string>
+ <string name="keys">キー</string>
+ <string name="keys_description">下のボタンから &lt;b>prod.keys&lt;/b> ファイルを選択してください。</string>
+ <string name="select_keys">キーを選択</string>
+ <string name="games">ゲーム</string>
+ <string name="games_description">下のボタンから&lt;b>ゲーム&lt;/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 000000000..5da80ab4b
--- /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는 포함되어 있지 않습니다.&lt;br /&gt;&lt;br /&gt;시작하기 전에 장치 저장소에서 <![CDATA[<b> prod.keys </b>]]> 파일을 찾아주세요.&lt;br /&gt;&lt;br /&gt;<![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">&lt;b>yuzu&lt;/b> 를 설정하고 에뮬레이션으로 이동하는 방법을 알아보세요.</string>
+ <string name="get_started">시작하기</string>
+ <string name="keys">Keys</string>
+ <string name="keys_description">아래 버튼을 사용하여 &lt;b>prod.keys&lt;/b> 파일을 선택합니다.</string>
+ <string name="select_keys">keys 선택</string>
+ <string name="games">게임</string>
+ <string name="games_description">아래 버튼으로 &lt;b>게임&lt;/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 000000000..3e1f9bce5
--- /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.&lt;br /&gt;&lt;br /&gt;Før du begynner, må du finne <![CDATA[<b> prod.keys </b>]]> filen din på enhetslagringen.&lt;br /&gt;&lt;br /&gt;<![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 &lt;b>yuzu&lt;/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 &lt;b>prod.keys&lt;/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 &lt;b>Spill&lt;/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 000000000..1cd1a8f87
--- /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.&lt;br /&gt;&lt;br /&gt;Zanim zaczniesz, wybierz plik kluczy <![CDATA[<b> prod.keys </b>]]> z katalogu w pamięci masowej.&lt;br /&gt;&lt;br /&gt;<![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ć &lt;b>yuzu&lt;/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 &lt;b>prod.keys&lt;/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 &lt;b>Games&lt;/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 000000000..35197c280
--- /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. &lt;br /&gt;&lt;br /&gt;Antes de começares, por favor localiza o ficheiro <![CDATA[1 prod.keys 1]]> no armazenamento do teu dispositivo.&lt;br /&gt;&lt;br /&gt;<![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 &lt;b>yuzu&lt;/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 &lt;b>prod.keys&lt;/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 &lt;b>Games&lt;/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 000000000..8761e2374
--- /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. &lt;br /&gt;&lt;br /&gt;Antes de começares, por favor localiza o ficheiro <![CDATA[1 prod.keys 1]]> no armazenamento do teu dispositivo.&lt;br /&gt;&lt;br /&gt;<![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 &lt;b>yuzu&lt;/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 &lt;b>prod.keys&lt;/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 &lt;b>Games&lt;/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 000000000..0fb4908f7
--- /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. Мы не предоставляем сами игры или ключи.&lt;br /&gt;&lt;br /&gt;Перед началом работы найдите файл <![CDATA[<b> prod.keys </b>]]> в хранилище устройства..&lt;br /&gt;&lt;br /&gt;<![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">Узнайте, как настроить &lt;b>yuzu&lt;/b> и перейти прямиком к эмуляции.</string>
+ <string name="get_started">Начать</string>
+ <string name="keys">Ключи</string>
+ <string name="keys_description">Выберите ваш файл &lt;b>prod.keys&lt;/b> с помощью кнопки ниже.</string>
+ <string name="select_keys">Выбрать ключи</string>
+ <string name="games">Игры</string>
+ <string name="games_description">Выберите вашу папку с &lt;b>играми&lt;/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 000000000..0d11eb2d2
--- /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. Ми не надаємо самі ігри або ключі.&lt;br /&gt;&lt;br /&gt;Перед початком роботи знайдіть ваш файл <![CDATA[<b> prod.keys </b>]]> у сховищі пристрою.&lt;br /&gt;&lt;br /&gt;<![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">Дізнайтеся, як налаштувати &lt;b>yuzu&lt;/b> та перейти до емуляції.</string>
+ <string name="get_started">Розпочати</string>
+ <string name="keys">Ключі</string>
+ <string name="keys_description">Виберіть ваш файл &lt;b>prod.keys&lt;/b> за допомогою кнопки нижче.</string>
+ <string name="select_keys">Вибрати ключі</string>
+ <string name="games">Ігри</string>
+ <string name="games_description">Виберіть вашу папку з &lt;b>іграми&lt;/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 000000000..e00bbaa2e
--- /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 游戏,但不包含任何游戏和密钥文件。&lt;br /&gt;&lt;br /&gt;在开始前,请找到放置于设备存储中的 <![CDATA[<b> prod.keys </b>]]> 文件。&lt;br /&gt;&lt;br /&gt;<![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">了解如何设置 &lt;b>yuzu&lt;/b> 并进行模拟。</string>
+ <string name="get_started">开始</string>
+ <string name="keys">密钥文件</string>
+ <string name="keys_description">使用下方的按钮来选择你的 &lt;b>prod.keys&lt;/b> 文件。</string>
+ <string name="select_keys">选择密钥文件</string>
+ <string name="games">游戏</string>
+ <string name="games_description">使用下方的按钮选择你的 &lt;b>游戏&lt;/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 000000000..a54d04248
--- /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 主機遊戲,但不包含任何遊戲和金鑰。&lt;br /&gt;&lt;br /&gt;在您開始前,請找到放置於您的裝置儲存空間的 <![CDATA[<b> prod.keys </b>]]> 檔案。&lt;br /&gt;&lt;br /&gt;<![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">瞭解如何設定 &lt;b>yuzu&lt;/b> 並進入模擬。</string>
+ <string name="get_started">開始使用</string>
+ <string name="keys">金鑰</string>
+ <string name="keys_description">使用下方的按鈕選取您的 &lt;b>prod.keys&lt;/b> 檔案。</string>
+ <string name="select_keys">選取金鑰</string>
+ <string name="games">遊戲</string>
+ <string name="games_description">使用下方的按鈕選取您的&lt;b>遊戲&lt;/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 ea20cb17c..6d092f7a9 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 bc614b81d..2e93b408c 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 0ae69afb4..cc1d8c39d 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>
@@ -65,11 +65,8 @@
<string name="invalid_keys_file">Invalid keys file selected</string>
<string name="install_keys_success">Keys successfully installed</string>
<string name="reading_keys_failure">Error reading encryption keys</string>
- <string name="install_keys_failure_extension_description">
- 1. Verify your keys have the .keys extension.\n\n
- 2. Keys must not be stored in the Downloads folder.\n\n
- Resolve the issue(s) and try again.
- </string>
+ <string name="install_prod_keys_failure_extension_description">Verify your keys file has a .keys extension and try again.</string>
+ <string name="install_amiibo_keys_failure_extension_description">Verify your keys file has a .bin extension and try again.</string>
<string name="invalid_keys_error">Invalid encryption keys</string>
<string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string>
<string name="install_keys_failure_description">The selected file is incorrect or corrupt. Please redump your keys.</string>
@@ -105,6 +102,15 @@
<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 to NAND</string>
+ <string name="install_game_content_failure_description">Game content installation failed. Please ensure content is 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. Please select an update or DLC instead.</string>
+ <string name="install_game_content_failure_file_extension">The selected file type is not supported. Only NSP and XCI content is supported for this action. Please verify the game content is valid.</string>
+ <string name="install_game_content_success">Game content installed successfully</string>
+ <string name="install_game_content_success_overwrite">Game content was 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>
@@ -152,10 +158,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>
@@ -163,12 +169,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>
@@ -187,6 +204,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>
@@ -318,6 +336,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>
@@ -352,10 +375,19 @@
<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>
+
<!-- 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 000000000..51b88d9dc
--- /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 e19e8ce58..80f370c16 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/audio_core.cpp b/src/audio_core/audio_core.cpp
index 07a679c32..703ef4494 100644
--- a/src/audio_core/audio_core.cpp
+++ b/src/audio_core/audio_core.cpp
@@ -47,12 +47,4 @@ AudioRenderer::ADSP::ADSP& AudioCore::GetADSP() {
return *adsp;
}
-void AudioCore::SetNVDECActive(bool active) {
- nvdec_active = active;
-}
-
-bool AudioCore::IsNVDECActive() const {
- return nvdec_active;
-}
-
} // namespace AudioCore
diff --git a/src/audio_core/audio_core.h b/src/audio_core/audio_core.h
index e33e00a3e..ea047773e 100644
--- a/src/audio_core/audio_core.h
+++ b/src/audio_core/audio_core.h
@@ -57,18 +57,6 @@ public:
*/
AudioRenderer::ADSP::ADSP& GetADSP();
- /**
- * Toggle NVDEC state, used to avoid stall in playback.
- *
- * @param active - Set true if nvdec is active, otherwise false.
- */
- void SetNVDECActive(bool active);
-
- /**
- * Get NVDEC state.
- */
- bool IsNVDECActive() const;
-
private:
/**
* Create the sinks on startup.
@@ -83,8 +71,6 @@ private:
std::unique_ptr<Sink::Sink> input_sink;
/// The ADSP in the sysmodule
std::unique_ptr<AudioRenderer::ADSP::ADSP> adsp;
- /// Is NVDec currently active?
- bool nvdec_active{false};
};
} // namespace AudioCore
diff --git a/src/common/fs/fs.cpp b/src/common/fs/fs.cpp
index e1716c62d..6d66c926d 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"
@@ -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() {
diff --git a/src/common/fs/fs_android.h b/src/common/fs/fs_android.h
index bb8a52648..b441c2a12 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_paths.h b/src/common/fs/fs_paths.h
index c77c112f1..61bac9eba 100644
--- a/src/common/fs/fs_paths.h
+++ b/src/common/fs/fs_paths.h
@@ -10,6 +10,7 @@
// Sub-directories contained within a yuzu data directory
+#define AMIIBO_DIR "amiibo"
#define CACHE_DIR "cache"
#define CONFIG_DIR "config"
#define DUMP_DIR "dump"
diff --git a/src/common/fs/path_util.cpp b/src/common/fs/path_util.cpp
index e026a13d9..d71cfacc6 100644
--- a/src/common/fs/path_util.cpp
+++ b/src/common/fs/path_util.cpp
@@ -114,6 +114,7 @@ public:
#endif
GenerateYuzuPath(YuzuPath::YuzuDir, yuzu_path);
+ GenerateYuzuPath(YuzuPath::AmiiboDir, yuzu_path / AMIIBO_DIR);
GenerateYuzuPath(YuzuPath::CacheDir, yuzu_path_cache);
GenerateYuzuPath(YuzuPath::ConfigDir, yuzu_path_config);
GenerateYuzuPath(YuzuPath::DumpDir, yuzu_path / DUMP_DIR);
diff --git a/src/common/fs/path_util.h b/src/common/fs/path_util.h
index 7cfe85b70..ba28964d0 100644
--- a/src/common/fs/path_util.h
+++ b/src/common/fs/path_util.h
@@ -12,6 +12,7 @@ namespace Common::FS {
enum class YuzuPath {
YuzuDir, // Where yuzu stores its data.
+ AmiiboDir, // Where Amiibo backups are stored.
CacheDir, // Where cached filesystem data is stored.
ConfigDir, // Where config files are stored.
DumpDir, // Where dumped data is stored.
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index ff53e80bb..66dffc9bf 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() {
@@ -235,6 +244,7 @@ void RestoreGlobalState(bool is_powered_on) {
values.bg_green.SetGlobal(true);
values.bg_blue.SetGlobal(true);
values.enable_compute_pipelines.SetGlobal(true);
+ values.use_video_framerate.SetGlobal(true);
// System
values.language_index.SetGlobal(true);
diff --git a/src/common/settings.h b/src/common/settings.h
index 7f865b2a7..3aedf3850 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -482,6 +482,8 @@ struct Values {
SwitchableSetting<AstcRecompression, true> astc_recompression{
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/time_zone.cpp b/src/common/time_zone.cpp
index 126836b01..d8d7896c6 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 99cae6ef2..f574d5c04 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/uuid.cpp b/src/common/uuid.cpp
index 89e1ed225..035df7fe0 100644
--- a/src/common/uuid.cpp
+++ b/src/common/uuid.cpp
@@ -48,7 +48,7 @@ std::array<u8, 0x10> ConstructFromRawString(std::string_view raw_string) {
}
std::array<u8, 0x10> ConstructFromFormattedString(std::string_view formatted_string) {
- std::array<u8, 0x10> uuid;
+ std::array<u8, 0x10> uuid{};
size_t i = 0;
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 3df4094a7..3655b8478 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
@@ -835,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})
@@ -848,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 d30914b7a..beaea64b3 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 8e40702cc..d5f2fa09a 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 000000000..eef7c3116
--- /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 dfdcbe35a..5acf9008d 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 bce695daf..a990845cb 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 bbbcb4f9d..bb97ed5bc 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 e83599e82..af2aa1f1c 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 5a4eba3eb..92c548db0 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 d90b3e568..d90b3e568 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 fa0c48b25..b5c9c43c4 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 57e6dd0d0..57e6dd0d0 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 20550faeb..6d9a862e1 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 4406ae30e..b74fd0a58 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 {
@@ -216,6 +216,14 @@ struct System::Impl {
}
}
+ void SetNVDECActive(bool is_nvdec_active) {
+ nvdec_active = is_nvdec_active;
+ }
+
+ bool GetNVDECActive() {
+ return nvdec_active;
+ }
+
void InitializeDebugger(System& system, u16 port) {
debugger = std::make_unique<Debugger>(system, port);
}
@@ -251,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");
@@ -485,6 +493,8 @@ struct System::Impl {
std::atomic_bool is_powered_on{};
bool exit_lock = false;
+ bool nvdec_active{};
+
Reporter reporter;
std::unique_ptr<Memory::CheatEngine> cheat_engine;
std::unique_ptr<Tools::Freezer> memory_freezer;
@@ -529,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)} {}
@@ -594,6 +604,14 @@ void System::UnstallApplication() {
impl->UnstallApplication();
}
+void System::SetNVDECActive(bool is_nvdec_active) {
+ impl->SetNVDECActive(is_nvdec_active);
+}
+
+bool System::GetNVDECActive() {
+ return impl->GetNVDECActive();
+}
+
void System::InitializeDebugger() {
impl->InitializeDebugger(*this, Settings::values.gdbstub_port.GetValue());
}
@@ -909,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 4f153154f..93afc9303 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -189,6 +189,9 @@ public:
std::unique_lock<std::mutex> StallApplication();
void UnstallApplication();
+ void SetNVDECActive(bool is_nvdec_active);
+ [[nodiscard]] bool GetNVDECActive();
+
/**
* Initialize the debugger.
*/
@@ -409,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/file_sys/submission_package.h b/src/core/file_sys/submission_package.h
index 3226b884a..27f97c725 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 85383998d..7c17bbefa 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 853b893a1..311a59e5f 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 cc0076238..7a15d8438 100644
--- a/src/core/file_sys/vfs_real.cpp
+++ b/src/core/file_sys/vfs_real.cpp
@@ -25,6 +25,8 @@ namespace FS = Common::FS;
namespace {
+constexpr size_t MaxOpenFiles = 512;
+
constexpr FS::FileAccessMode ModeFlagsToFileAccessMode(Mode mode) {
switch (mode) {
case Mode::Read:
@@ -73,28 +75,30 @@ VfsEntryType RealVfsFilesystem::GetEntryType(std::string_view path_) const {
VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) {
const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
- 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 (!FS::Exists(path) || !FS::IsFile(path)) {
return nullptr;
}
- cache.insert_or_assign(path, std::move(backing));
+ auto reference = std::make_unique<FileReference>();
+ this->InsertReferenceIntoList(*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));
+ cache[path] = file;
+
+ return file;
}
VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, Mode perms) {
const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
+ 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 +127,22 @@ 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;
- }
-
- 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(old_path);
+ 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();
- }
- cache.erase(path);
- }
-
+ 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 +151,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 +168,102 @@ 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];
+void RealVfsFilesystem::RefreshReference(const std::string& path, Mode perms,
+ FileReference& reference) {
+ // Temporarily remove from list.
+ this->RemoveReferenceFromList(reference);
- if (cached.expired()) {
- continue;
- }
+ // Restore file if needed.
+ if (!reference.file) {
+ this->EvictSingleReference();
- 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->InsertReferenceIntoList(reference);
}
-bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) {
- const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
+void RealVfsFilesystem::DropReference(std::unique_ptr<FileReference>&& reference) {
+ // Remove from list.
+ this->RemoveReferenceFromList(*reference);
- 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;
- }
+ // Close the file.
+ if (reference->file) {
+ reference->file.reset();
+ num_open_files--;
+ }
+}
- const auto& entry = cache[kv.first];
- if (!entry.expired()) {
- entry.lock()->Close();
- }
+void RealVfsFilesystem::EvictSingleReference() {
+ if (num_open_files < MaxOpenFiles || open_references.empty()) {
+ return;
+ }
+
+ // Get and remove from list.
+ auto& reference = open_references.back();
+ this->RemoveReferenceFromList(reference);
- cache.erase(kv.first);
+ // Close the file.
+ if (reference.file) {
+ reference.file.reset();
+ num_open_files--;
}
- return FS::RemoveDirRecursively(path);
+ // Reinsert into closed list.
+ this->InsertReferenceIntoList(reference);
+}
+
+void RealVfsFilesystem::InsertReferenceIntoList(FileReference& reference) {
+ if (reference.file) {
+ open_references.push_front(reference);
+ } else {
+ closed_references.push_front(reference);
+ }
+}
+
+void RealVfsFilesystem::RemoveReferenceFromList(FileReference& reference) {
+ if (reference.file) {
+ open_references.erase(open_references.iterator_to(reference));
+ } else {
+ closed_references.erase(closed_references.iterator_to(reference));
+ }
}
-RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::shared_ptr<FS::IOFile> backing_,
+RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::unique_ptr<FileReference> reference_,
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_) {}
+ : base(base_), reference(std::move(reference_)), path(path_),
+ parent_path(FS::GetParentPath(path_)), path_components(FS::SplitPathComponents(path_)),
+ perms(perms_) {}
-RealVfsFile::~RealVfsFile() = default;
+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();
+ base.RefreshReference(path, perms, *reference);
+ return reference->file ? reference->file->GetSize() : 0;
}
bool RealVfsFile::Resize(std::size_t new_size) {
- return backing->SetSize(new_size);
+ base.RefreshReference(path, perms, *reference);
+ return reference->file ? reference->file->SetSize(new_size) : false;
}
VirtualDir RealVfsFile::GetContainingDirectory() const {
@@ -276,27 +279,25 @@ 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))) {
+ 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))) {
+ 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.
diff --git a/src/core/file_sys/vfs_real.h b/src/core/file_sys/vfs_real.h
index b92c84316..d8c900e33 100644
--- a/src/core/file_sys/vfs_real.h
+++ b/src/core/file_sys/vfs_real.h
@@ -3,8 +3,9 @@
#pragma once
+#include <map>
#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 +15,11 @@ class IOFile;
namespace FileSys {
+struct FileReference : public Common::IntrusiveListBaseNode<FileReference> {
+ std::shared_ptr<Common::FS::IOFile> file{};
+};
+
+class RealVfsFile;
class RealVfsFilesystem : public VfsFilesystem {
public:
RealVfsFilesystem();
@@ -35,7 +41,21 @@ 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;
+ size_t num_open_files{};
+
+private:
+ friend class RealVfsFile;
+ void RefreshReference(const std::string& path, Mode perms, FileReference& reference);
+ void DropReference(std::unique_ptr<FileReference>&& reference);
+ void EvictSingleReference();
+
+private:
+ void InsertReferenceIntoList(FileReference& reference);
+ void RemoveReferenceFromList(FileReference& reference);
};
// An implementation of VfsFile that represents a file on the user's computer.
@@ -57,13 +77,11 @@ public:
bool Rename(std::string_view name) override;
private:
- RealVfsFile(RealVfsFilesystem& base, std::shared_ptr<Common::FS::IOFile> backing,
+ RealVfsFile(RealVfsFilesystem& base, std::unique_ptr<FileReference> reference,
const std::string& path, Mode perms = Mode::Read);
- void Close();
-
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;
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp
index 70480b725..908811e2c 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>
@@ -1313,7 +1315,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 +1326,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 +1335,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 f9814ac8f..37fe5db77 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -892,7 +892,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/service/nfc/common/amiibo_crypto.cpp b/src/core/hle/service/nfc/common/amiibo_crypto.cpp
index b2bcb68c3..bc232c334 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 bf3044ed9..6a3e0841e 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 0bd7900e1..f4b180b06 100644
--- a/src/core/hle/service/nfc/common/device.cpp
+++ b/src/core/hle/service/nfc/common/device.cpp
@@ -12,6 +12,11 @@
#pragma warning(pop)
#endif
+#include <fmt/format.h>
+
+#include "common/fs/file.h"
+#include "common/fs/fs.h"
+#include "common/fs/path_util.h"
#include "common/input.h"
#include "common/logging/log.h"
#include "common/string_util.h"
@@ -136,7 +141,7 @@ bool NfcDevice::LoadNfcTag(std::span<const u8> data) {
if (!NFP::AmiiboCrypto::IsKeyAvailable()) {
LOG_INFO(Service_NFC, "Loading amiibo without keys");
memcpy(&encrypted_tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File));
- BuildAmiiboWithoutKeys();
+ BuildAmiiboWithoutKeys(tag_data, encrypted_tag_data);
is_plain_amiibo = true;
is_write_protected = true;
return true;
@@ -237,34 +242,39 @@ Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info, bool is_mifare) const {
return ResultWrongDeviceState;
}
- UniqueSerialNumber uuid = encrypted_tag_data.uuid.uid;
-
- // Generate random UUID to bypass amiibo load limits
- if (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];
- }
+ UniqueSerialNumber uuid{};
+ u8 uuid_length{};
+ NfcProtocol protocol{NfcProtocol::TypeA};
+ TagType tag_type{TagType::Type2};
if (is_mifare) {
- tag_info = {
- .uuid = uuid,
- .uuid_extension = {},
- .uuid_length = static_cast<u8>(uuid.size()),
- .protocol = NfcProtocol::TypeA,
- .tag_type = TagType::Type4,
+ tag_type = TagType::Mifare;
+ uuid_length = sizeof(NFP::NtagTagUuid);
+ memcpy(uuid.data(), mifare_data.data(), uuid_length);
+ } else {
+ tag_type = TagType::Type2;
+ uuid_length = sizeof(NFP::NtagTagUuid);
+ NFP::NtagTagUuid nUuid{
+ .part1 = encrypted_tag_data.uuid.part1,
+ .part2 = encrypted_tag_data.uuid.part2,
+ .nintendo_id = encrypted_tag_data.uuid.nintendo_id,
};
- return ResultSuccess;
+ memcpy(uuid.data(), &nUuid, uuid_length);
+
+ // Generate random UUID to bypass amiibo load limits
+ if (Settings::values.random_amiibo_id) {
+ Common::TinyMT rng{};
+ rng.Initialize(static_cast<u32>(GetCurrentPosixTime()));
+ rng.GenerateRandomBytes(uuid.data(), uuid_length);
+ }
}
// 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,
+ .uuid_length = uuid_length,
+ .protocol = protocol,
+ .tag_type = tag_type,
};
return ResultSuccess;
@@ -272,8 +282,38 @@ Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info, bool is_mifare) const {
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;
+ TagInfo tag_info{};
+ result = GetTagInfo(tag_info, true);
+
+ if (result.IsError()) {
+ return result;
+ }
+
+ if (tag_info.protocol != NfcProtocol::TypeA || tag_info.tag_type != TagType::Mifare) {
+ return ResultInvalidTagType;
+ }
+
+ if (parameters.size() == 0) {
+ return ResultInvalidArgument;
+ }
+
+ 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;
+ }
+ }
+
for (std::size_t i = 0; i < parameters.size(); i++) {
result = ReadMifare(parameters[i], read_block_data[i]);
if (result.IsError()) {
@@ -288,17 +328,8 @@ 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 (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;
- }
-
if (mifare_data.size() < sector_index + sizeof(DataBlock)) {
- return Mifare::ResultReadError;
+ return ResultMifareError288;
}
// TODO: Use parameter.sector_key to read encrypted data
@@ -310,6 +341,28 @@ Result NfcDevice::ReadMifare(const MifareReadBlockParameter& parameter,
Result NfcDevice::WriteMifare(std::span<const MifareWriteBlockParameter> parameters) {
Result result = ResultSuccess;
+ TagInfo tag_info{};
+ result = GetTagInfo(tag_info, true);
+
+ if (result.IsError()) {
+ return result;
+ }
+
+ if (tag_info.protocol != NfcProtocol::TypeA || tag_info.tag_type != TagType::Mifare) {
+ return ResultInvalidTagType;
+ }
+
+ if (parameters.size() == 0) {
+ return ResultInvalidArgument;
+ }
+
+ 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;
+ }
+ }
+
for (std::size_t i = 0; i < parameters.size(); i++) {
result = WriteMifare(parameters[i]);
if (result.IsError()) {
@@ -319,7 +372,7 @@ Result NfcDevice::WriteMifare(std::span<const MifareWriteBlockParameter> paramet
if (!npad_device->WriteNfc(mifare_data)) {
LOG_ERROR(Service_NFP, "Error writing to file");
- return Mifare::ResultReadError;
+ return ResultMifareError288;
}
return result;
@@ -337,7 +390,7 @@ Result NfcDevice::WriteMifare(const MifareWriteBlockParameter& parameter) {
}
if (mifare_data.size() < sector_index + sizeof(DataBlock)) {
- return Mifare::ResultReadError;
+ return ResultMifareError288;
}
// TODO: Use parameter.sector_key to encrypt the data
@@ -361,21 +414,30 @@ Result NfcDevice::Mount(NFP::ModelType model_type, NFP::MountTarget mount_target
if (!NFP::AmiiboCrypto::IsAmiiboValid(encrypted_tag_data)) {
LOG_ERROR(Service_NFP, "Not an amiibo");
- return ResultNotAnAmiibo;
+ return ResultInvalidTagType;
}
// The loaded amiibo is not encrypted
if (is_plain_amiibo) {
+ std::vector<u8> data(sizeof(NFP::NTAG215File));
+ memcpy(data.data(), &tag_data, sizeof(tag_data));
+ WriteBackupData(tag_data.uid, data);
+
device_state = DeviceState::TagMounted;
mount_target = mount_target_;
return ResultSuccess;
}
if (!NFP::AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) {
- LOG_ERROR(Service_NFP, "Can't decode amiibo {}", device_state);
- return ResultCorruptedData;
+ 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, data);
+
device_state = DeviceState::TagMounted;
mount_target = mount_target_;
return ResultSuccess;
@@ -470,6 +532,7 @@ Result NfcDevice::FlushWithBreak(NFP::BreakType break_type) {
std::vector<u8> data(sizeof(NFP::EncryptedNTAG215File));
if (is_plain_amiibo) {
memcpy(data.data(), &tag_data, sizeof(tag_data));
+ WriteBackupData(tag_data.uid, data);
} else {
if (!NFP::AmiiboCrypto::EncodeAmiibo(tag_data, encrypted_tag_data)) {
LOG_ERROR(Service_NFP, "Failed to encode data");
@@ -477,6 +540,7 @@ Result NfcDevice::FlushWithBreak(NFP::BreakType break_type) {
}
memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data));
+ WriteBackupData(encrypted_tag_data.uuid, data);
}
if (!npad_device->WriteNfc(data)) {
@@ -488,7 +552,7 @@ Result NfcDevice::FlushWithBreak(NFP::BreakType break_type) {
}
Result NfcDevice::Restore() {
- if (device_state != DeviceState::TagMounted) {
+ if (device_state != DeviceState::TagFound) {
LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
if (device_state == DeviceState::TagRemoved) {
return ResultTagRemoved;
@@ -496,13 +560,59 @@ Result NfcDevice::Restore() {
return ResultWrongDeviceState;
}
- if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) {
- LOG_ERROR(Service_NFC, "Amiibo is read only", device_state);
- return ResultWrongDeviceState;
+ NFC::TagInfo tag_info{};
+ std::array<u8, sizeof(NFP::EncryptedNTAG215File)> data{};
+ Result result = GetTagInfo(tag_info, false);
+
+ if (result.IsError()) {
+ return result;
}
- // TODO: Load amiibo from backup on system
- LOG_ERROR(Service_NFP, "Not Implemented");
+ result = ReadBackupData(tag_info.uuid, tag_info.uuid_length, data);
+
+ if (result.IsError()) {
+ return result;
+ }
+
+ NFP::NTAG215File temporary_tag_data{};
+ NFP::EncryptedNTAG215File temporary_encrypted_tag_data{};
+
+ // Fallback for encrypted amiibos without keys
+ if (is_write_protected) {
+ return ResultWriteAmiiboFailed;
+ }
+
+ // Fallback for plain amiibos
+ if (is_plain_amiibo) {
+ LOG_INFO(Service_NFP, "Restoring backup of plain amiibo");
+ memcpy(&temporary_tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File));
+ temporary_encrypted_tag_data = NFP::AmiiboCrypto::EncodedDataToNfcData(temporary_tag_data);
+ }
+
+ if (!is_plain_amiibo) {
+ LOG_INFO(Service_NFP, "Restoring backup of encrypted amiibo");
+ temporary_tag_data = {};
+ memcpy(&temporary_encrypted_tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File));
+ }
+
+ if (!NFP::AmiiboCrypto::IsAmiiboValid(temporary_encrypted_tag_data)) {
+ return ResultInvalidTagType;
+ }
+
+ if (!is_plain_amiibo) {
+ if (!NFP::AmiiboCrypto::DecodeAmiibo(temporary_encrypted_tag_data, temporary_tag_data)) {
+ LOG_ERROR(Service_NFP, "Can't decode amiibo");
+ return ResultCorruptedData;
+ }
+ }
+
+ // Overwrite tag contents with backup and mount the tag
+ tag_data = temporary_tag_data;
+ encrypted_tag_data = temporary_encrypted_tag_data;
+ device_state = DeviceState::TagMounted;
+ mount_target = NFP::MountTarget::All;
+ is_data_moddified = true;
+
return ResultSuccess;
}
@@ -1132,16 +1242,98 @@ Result NfcDevice::BreakTag(NFP::BreakType break_type) {
return FlushWithBreak(break_type);
}
-Result NfcDevice::ReadBackupData(std::span<u8> data) const {
- // Not implemented
+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.begin(), uid.begin() + uuid_size, ""));
+
+ if (!Common::FS::Exists(yuzu_amiibo_dir / backup_dir / file_name)) {
+ return ResultUnableToAccessBackupFile;
+ }
+
return ResultSuccess;
}
-Result NfcDevice::WriteBackupData(std::span<const u8> data) {
- // Not implemented
+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.begin(), uid.begin() + uuid_size, ""));
+
+ const Common::FS::IOFile keys_file{yuzu_amiibo_dir / backup_dir / file_name,
+ Common::FS::FileAccessMode::Read,
+ Common::FS::FileType::BinaryFile};
+
+ if (!keys_file.IsOpen()) {
+ LOG_ERROR(Service_NFP, "Failed to open amiibo backup");
+ return ResultUnableToAccessBackupFile;
+ }
+
+ if (keys_file.Read(data) != data.size()) {
+ LOG_ERROR(Service_NFP, "Failed to read amiibo backup");
+ return ResultUnableToAccessBackupFile;
+ }
+
+ return ResultSuccess;
+}
+
+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.begin(), uid.begin() + uuid_size, ""));
+
+ if (HasBackup(uid, uuid_size).IsError()) {
+ if (!Common::FS::CreateDir(yuzu_amiibo_dir / backup_dir)) {
+ return ResultBackupPathAlreadyExist;
+ }
+
+ if (!Common::FS::NewFile(yuzu_amiibo_dir / backup_dir / file_name)) {
+ return ResultBackupPathAlreadyExist;
+ }
+ }
+
+ const Common::FS::IOFile keys_file{yuzu_amiibo_dir / backup_dir / file_name,
+ Common::FS::FileAccessMode::ReadWrite,
+ Common::FS::FileType::BinaryFile};
+
+ if (!keys_file.IsOpen()) {
+ LOG_ERROR(Service_NFP, "Failed to open amiibo backup");
+ return ResultUnableToAccessBackupFile;
+ }
+
+ if (keys_file.Write(data) != data.size()) {
+ LOG_ERROR(Service_NFP, "Failed to write amiibo backup");
+ return ResultUnableToAccessBackupFile;
+ }
+
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);
@@ -1177,7 +1369,8 @@ NFP::AmiiboName NfcDevice::GetAmiiboName(const NFP::AmiiboSettings& settings) co
return amiibo_name;
}
-void NfcDevice::SetAmiiboName(NFP::AmiiboSettings& settings, const NFP::AmiiboName& amiibo_name) {
+void NfcDevice::SetAmiiboName(NFP::AmiiboSettings& settings,
+ const NFP::AmiiboName& amiibo_name) const {
std::array<char16_t, NFP::amiibo_name_length> settings_amiibo_name{};
// Convert from utf8 to utf16
@@ -1258,22 +1451,23 @@ void NfcDevice::UpdateRegisterInfoCrc() {
tag_data.register_info_crc = crc.checksum();
}
-void NfcDevice::BuildAmiiboWithoutKeys() {
+void NfcDevice::BuildAmiiboWithoutKeys(NFP::NTAG215File& stubbed_tag_data,
+ const NFP::EncryptedNTAG215File& encrypted_file) const {
Service::Mii::MiiManager manager;
- auto& settings = tag_data.settings;
+ auto& settings = stubbed_tag_data.settings;
- tag_data = NFP::AmiiboCrypto::NfcDataToEncodedData(encrypted_tag_data);
+ stubbed_tag_data = NFP::AmiiboCrypto::NfcDataToEncodedData(encrypted_file);
// Common info
- tag_data.write_counter = 0;
- tag_data.amiibo_version = 0;
+ stubbed_tag_data.write_counter = 0;
+ stubbed_tag_data.amiibo_version = 0;
settings.write_date = GetAmiiboDate(GetCurrentPosixTime());
// Register info
SetAmiiboName(settings, {'y', 'u', 'z', 'u', 'A', 'm', 'i', 'i', 'b', 'o'});
settings.settings.font_region.Assign(0);
settings.init_date = GetAmiiboDate(GetCurrentPosixTime());
- tag_data.owner_mii = manager.BuildFromStoreData(manager.BuildDefault(0));
+ stubbed_tag_data.owner_mii = manager.BuildFromStoreData(manager.BuildDefault(0));
// Admin info
settings.settings.amiibo_initialized.Assign(1);
diff --git a/src/core/hle/service/nfc/common/device.h b/src/core/hle/service/nfc/common/device.h
index 6a37e8458..7560210d6 100644
--- a/src/core/hle/service/nfc/common/device.h
+++ b/src/core/hle/service/nfc/common/device.h
@@ -86,8 +86,14 @@ public:
Result GetAll(NFP::NfpData& data) const;
Result SetAll(const NFP::NfpData& data);
Result BreakTag(NFP::BreakType break_type);
- Result ReadBackupData(std::span<u8> data) const;
- Result WriteBackupData(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;
@@ -103,14 +109,15 @@ private:
void CloseNfcTag();
NFP::AmiiboName GetAmiiboName(const NFP::AmiiboSettings& settings) const;
- void SetAmiiboName(NFP::AmiiboSettings& settings, const NFP::AmiiboName& amiibo_name);
+ void SetAmiiboName(NFP::AmiiboSettings& settings, const NFP::AmiiboName& amiibo_name) const;
NFP::AmiiboDate GetAmiiboDate(s64 posix_time) const;
u64 GetCurrentPosixTime() const;
u64 RemoveVersionByte(u64 application_id) const;
void UpdateSettingsCrc();
void UpdateRegisterInfoCrc();
- void BuildAmiiboWithoutKeys();
+ void BuildAmiiboWithoutKeys(NFP::NTAG215File& stubbed_tag_data,
+ const NFP::EncryptedNTAG215File& encrypted_file) const;
bool is_controller_set{};
int callback_key;
diff --git a/src/core/hle/service/nfc/common/device_manager.cpp b/src/core/hle/service/nfc/common/device_manager.cpp
index d5deaaf27..b0456508e 100644
--- a/src/core/hle/service/nfc/common/device_manager.cpp
+++ b/src/core/hle/service/nfc/common/device_manager.cpp
@@ -543,9 +543,14 @@ Result DeviceManager::ReadBackupData(u64 device_handle, std::span<u8> data) cons
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
+ NFC::TagInfo tag_info{};
if (result.IsSuccess()) {
- result = device->ReadBackupData(data);
+ result = device->GetTagInfo(tag_info, false);
+ }
+
+ if (result.IsSuccess()) {
+ result = device->ReadBackupData(tag_info.uuid, tag_info.uuid_length, data);
result = VerifyDeviceResult(device, result);
}
@@ -557,9 +562,14 @@ Result DeviceManager::WriteBackupData(u64 device_handle, std::span<const u8> dat
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
+ NFC::TagInfo tag_info{};
+
+ if (result.IsSuccess()) {
+ result = device->GetTagInfo(tag_info, false);
+ }
if (result.IsSuccess()) {
- result = device->WriteBackupData(data);
+ result = device->WriteBackupData(tag_info.uuid, tag_info.uuid_length, data);
result = VerifyDeviceResult(device, result);
}
diff --git a/src/core/hle/service/nfc/mifare_result.h b/src/core/hle/service/nfc/mifare_result.h
index 4b60048a5..16a9171e6 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/nfc_interface.cpp b/src/core/hle/service/nfc/nfc_interface.cpp
index 0fa29d398..130fb7f78 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);
@@ -302,7 +306,7 @@ Result NfcInterface::TranslateResultToServiceError(Result result) const {
return TranslateResultToNfp(result);
}
default:
- if (result != ResultUnknown216) {
+ if (result != ResultBackupPathAlreadyExist) {
return result;
}
return ResultUnknown74;
@@ -343,6 +347,9 @@ Result NfcInterface::TranslateResultToNfp(Result result) const {
if (result == ResultApplicationAreaIsNotInitialized) {
return NFP::ResultApplicationAreaIsNotInitialized;
}
+ if (result == ResultCorruptedDataWithBackup) {
+ return NFP::ResultCorruptedDataWithBackup;
+ }
if (result == ResultCorruptedData) {
return NFP::ResultCorruptedData;
}
@@ -352,9 +359,12 @@ Result NfcInterface::TranslateResultToNfp(Result result) const {
if (result == ResultApplicationAreaExist) {
return NFP::ResultApplicationAreaExist;
}
- if (result == ResultNotAnAmiibo) {
+ if (result == ResultInvalidTagType) {
return NFP::ResultNotAnAmiibo;
}
+ if (result == ResultUnableToAccessBackupFile) {
+ return NFP::ResultUnableToAccessBackupFile;
+ }
LOG_WARNING(Service_NFC, "Result conversion not handled");
return result;
}
@@ -375,6 +385,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 917d79ef8..715c0e80c 100644
--- a/src/core/hle/service/nfc/nfc_result.h
+++ b/src/core/hle/service/nfc/nfc_result.h
@@ -9,20 +9,23 @@ namespace Service::NFC {
constexpr Result ResultDeviceNotFound(ErrorModule::NFC, 64);
constexpr Result ResultInvalidArgument(ErrorModule::NFC, 65);
-constexpr Result ResultWrongApplicationAreaSize(ErrorModule::NFP, 68);
+constexpr Result ResultWrongApplicationAreaSize(ErrorModule::NFC, 68);
constexpr Result ResultWrongDeviceState(ErrorModule::NFC, 73);
constexpr Result ResultUnknown74(ErrorModule::NFC, 74);
constexpr Result ResultUnknown76(ErrorModule::NFC, 76);
constexpr Result ResultNfcNotInitialized(ErrorModule::NFC, 77);
constexpr Result ResultNfcDisabled(ErrorModule::NFC, 80);
-constexpr Result ResultWriteAmiiboFailed(ErrorModule::NFP, 88);
+constexpr Result ResultWriteAmiiboFailed(ErrorModule::NFC, 88);
constexpr Result ResultTagRemoved(ErrorModule::NFC, 97);
-constexpr Result ResultRegistrationIsNotInitialized(ErrorModule::NFP, 120);
-constexpr Result ResultApplicationAreaIsNotInitialized(ErrorModule::NFP, 128);
-constexpr Result ResultCorruptedData(ErrorModule::NFP, 144);
-constexpr Result ResultWrongApplicationAreaId(ErrorModule::NFP, 152);
-constexpr Result ResultApplicationAreaExist(ErrorModule::NFP, 168);
-constexpr Result ResultNotAnAmiibo(ErrorModule::NFP, 178);
-constexpr Result ResultUnknown216(ErrorModule::NFC, 216);
+constexpr Result ResultUnableToAccessBackupFile(ErrorModule::NFC, 113);
+constexpr Result ResultRegistrationIsNotInitialized(ErrorModule::NFC, 120);
+constexpr Result ResultApplicationAreaIsNotInitialized(ErrorModule::NFC, 128);
+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 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 c7ebd1fdb..68e724442 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_interface.cpp b/src/core/hle/service/nfp/nfp_interface.cpp
index 21d159154..34ef9d82d 100644
--- a/src/core/hle/service/nfp/nfp_interface.cpp
+++ b/src/core/hle/service/nfp/nfp_interface.cpp
@@ -126,7 +126,7 @@ void Interface::Flush(HLERequestContext& ctx) {
void Interface::Restore(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
- LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}", device_handle);
+ LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
auto result = GetManager()->Restore(device_handle);
result = TranslateResultToServiceError(result);
@@ -394,7 +394,7 @@ void Interface::BreakTag(HLERequestContext& ctx) {
void Interface::ReadBackupData(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
- LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}", device_handle);
+ LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
std::vector<u8> backup_data{};
auto result = GetManager()->ReadBackupData(device_handle, backup_data);
@@ -412,7 +412,7 @@ void Interface::WriteBackupData(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
const auto backup_data_buffer{ctx.ReadBuffer()};
- LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}", device_handle);
+ LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
auto result = GetManager()->WriteBackupData(device_handle, backup_data_buffer);
result = TranslateResultToServiceError(result);
diff --git a/src/core/hle/service/nfp/nfp_result.h b/src/core/hle/service/nfp/nfp_result.h
index 4c126cd81..618533843 100644
--- a/src/core/hle/service/nfp/nfp_result.h
+++ b/src/core/hle/service/nfp/nfp_result.h
@@ -17,9 +17,11 @@ constexpr Result ResultWriteAmiiboFailed(ErrorModule::NFP, 88);
constexpr Result ResultTagRemoved(ErrorModule::NFP, 97);
constexpr Result ResultRegistrationIsNotInitialized(ErrorModule::NFP, 120);
constexpr Result ResultApplicationAreaIsNotInitialized(ErrorModule::NFP, 128);
+constexpr Result ResultCorruptedDataWithBackup(ErrorModule::NFP, 136);
constexpr Result ResultCorruptedData(ErrorModule::NFP, 144);
constexpr Result ResultWrongApplicationAreaId(ErrorModule::NFP, 152);
constexpr Result ResultApplicationAreaExist(ErrorModule::NFP, 168);
constexpr Result ResultNotAnAmiibo(ErrorModule::NFP, 178);
+constexpr Result ResultUnableToAccessBackupFile(ErrorModule::NFP, 200);
} // namespace Service::NFP
diff --git a/src/core/hle/service/nfp/nfp_types.h b/src/core/hle/service/nfp/nfp_types.h
index 7d36d5ee6..aed12a7f8 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/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
index 0c7aee1b8..dc45169ad 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
@@ -69,7 +69,7 @@ NvResult nvhost_nvdec::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> in
void nvhost_nvdec::OnOpen(DeviceFD fd) {
LOG_INFO(Service_NVDRV, "NVDEC video stream started");
- system.AudioCore().SetNVDECActive(true);
+ system.SetNVDECActive(true);
}
void nvhost_nvdec::OnClose(DeviceFD fd) {
@@ -79,7 +79,7 @@ void nvhost_nvdec::OnClose(DeviceFD fd) {
if (iter != host1x_file.fd_to_id.end()) {
system.GPU().ClearCdmaInstance(iter->second);
}
- system.AudioCore().SetNVDECActive(false);
+ system.SetNVDECActive(false);
}
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvnflinger/nvnflinger.cpp b/src/core/hle/service/nvnflinger/nvnflinger.cpp
index aa3356611..b41c6240c 100644
--- a/src/core/hle/service/nvnflinger/nvnflinger.cpp
+++ b/src/core/hle/service/nvnflinger/nvnflinger.cpp
@@ -325,6 +325,10 @@ s64 Nvnflinger::GetNextTicks() const {
speed_scale = 0.01f;
}
}
+ if (system.GetNVDECActive() && settings.use_video_framerate.GetValue()) {
+ // Run at intended presentation rate during video playback.
+ speed_scale = 1.f;
+ }
// As an extension, treat nonpositive swap interval as framerate multiplier.
const f32 effective_fps = swap_interval <= 0 ? 120.f * static_cast<f32>(1 - swap_interval)
diff --git a/src/core/hle/service/time/time_manager.cpp b/src/core/hle/service/time/time_manager.cpp
index 28667710e..fa0fd0531 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 4f046f266..84572dbfa 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_zone_content_manager.cpp b/src/core/hle/service/time/time_zone_content_manager.cpp
index afbfe9715..5d60be67a 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 973f7837a..205371a26 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 5ebd4035e..8664f28d1 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 cda8d8343..8171c82a5 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 ea83b5714..952fcb0e2 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/core/loader/nro.cpp b/src/core/loader/nro.cpp
index 73d04d7ee..7be6cf5f3 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -33,7 +33,8 @@ static_assert(sizeof(NroSegmentHeader) == 0x8, "NroSegmentHeader has incorrect s
struct NroHeader {
INSERT_PADDING_BYTES(0x4);
u32_le module_header_offset;
- INSERT_PADDING_BYTES(0x8);
+ u32 magic_ext1;
+ u32 magic_ext2;
u32_le magic;
INSERT_PADDING_BYTES(0x4);
u32_le file_size;
@@ -124,6 +125,16 @@ FileType AppLoader_NRO::IdentifyType(const FileSys::VirtualFile& nro_file) {
return FileType::Error;
}
+bool AppLoader_NRO::IsHomebrew() {
+ // Read NSO header
+ NroHeader nro_header{};
+ if (sizeof(NroHeader) != file->ReadObject(&nro_header)) {
+ return false;
+ }
+ return nro_header.magic_ext1 == Common::MakeMagic('H', 'O', 'M', 'E') &&
+ nro_header.magic_ext2 == Common::MakeMagic('B', 'R', 'E', 'W');
+}
+
static constexpr u32 PageAlignSize(u32 size) {
return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK);
}
diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h
index ccb77b581..8de6eebc6 100644
--- a/src/core/loader/nro.h
+++ b/src/core/loader/nro.h
@@ -38,6 +38,8 @@ public:
*/
static FileType IdentifyType(const FileSys::VirtualFile& nro_file);
+ bool IsHomebrew();
+
FileType GetFileType() const override {
return IdentifyType(file);
}
diff --git a/src/input_common/drivers/virtual_amiibo.cpp b/src/input_common/drivers/virtual_amiibo.cpp
index f8bafe553..6435b8af8 100644
--- a/src/input_common/drivers/virtual_amiibo.cpp
+++ b/src/input_common/drivers/virtual_amiibo.cpp
@@ -82,6 +82,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,6 +110,7 @@ VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(std::span<u8> data) {
switch (data.size_bytes()) {
case AmiiboSize:
case AmiiboSizeWithoutPassword:
+ case AmiiboSizeWithSignature:
nfc_data.resize(AmiiboSize);
break;
case MifareSize:
diff --git a/src/input_common/drivers/virtual_amiibo.h b/src/input_common/drivers/virtual_amiibo.h
index 34e97cd91..09ca09e68 100644
--- a/src/input_common/drivers/virtual_amiibo.h
+++ b/src/input_common/drivers/virtual_amiibo.h
@@ -57,6 +57,7 @@ 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{};
diff --git a/src/shader_recompiler/CMakeLists.txt b/src/shader_recompiler/CMakeLists.txt
index 525b2363c..07e75f9d8 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/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp
index 17a6d4888..928b35561 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 2aaa6c5ea..7d2ded907 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/conditional_barrier_pass.cpp b/src/shader_recompiler/ir_opt/conditional_barrier_pass.cpp
new file mode 100644
index 000000000..c3ed27f4f
--- /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 000000000..5db7a38ad
--- /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 1f8f2ba95..629d18fa1 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/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index 2f281b370..45977d578 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -715,6 +715,8 @@ void BufferCache<P>::BindHostIndexBuffer() {
template <class P>
void BufferCache<P>::BindHostVertexBuffers() {
+ 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];
@@ -726,9 +728,28 @@ void BufferCache<P>::BindHostVertexBuffers() {
}
flags[Dirty::VertexBuffer0 + index] = false;
- const u32 stride = maxwell3d->regs.vertex_streams[index].stride;
- const u32 offset = buffer.Offset(binding.cpu_addr);
- runtime.BindVertexBuffer(index, buffer, offset, binding.size, stride);
+ 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;
+ }
+
+ if (any_valid) {
+ host_bindings.max_index++;
+ for (u32 index = host_bindings.min_index; index < host_bindings.max_index; index++) {
+ flags[Dirty::VertexBuffer0 + index] = false;
+
+ const Binding& binding = channel_state->vertex_buffers[index];
+ Buffer& buffer = slot_buffers[binding.buffer_id];
+
+ const u32 stride = maxwell3d->regs.vertex_streams[index].stride;
+ const u32 offset = buffer.Offset(binding.cpu_addr);
+
+ 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);
+ }
+ runtime.BindVertexBuffers(host_bindings);
}
}
@@ -882,15 +903,25 @@ void BufferCache<P>::BindHostTransformFeedbackBuffers() {
if (maxwell3d->regs.transform_feedback_enabled == 0) {
return;
}
+ 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 &&
+ maxwell3d->regs.transform_feedback.controls[index].stride == 0) {
+ break;
+ }
Buffer& buffer = slot_buffers[binding.buffer_id];
TouchBuffer(buffer, binding.buffer_id);
const u32 size = binding.size;
SynchronizeBuffer(buffer, binding.cpu_addr, size);
const u32 offset = buffer.Offset(binding.cpu_addr);
- runtime.BindTransformFeedbackBuffer(index, buffer, offset, size);
+ host_bindings.buffers.push_back(&buffer);
+ host_bindings.offsets.push_back(offset);
+ host_bindings.sizes.push_back(binding.size);
+ }
+ if (host_bindings.buffers.size() > 0) {
+ runtime.BindTransformFeedbackBuffers(host_bindings);
}
}
@@ -1616,6 +1647,8 @@ void BufferCache<P>::DownloadBufferMemory(Buffer& buffer, VAddr cpu_addr, u64 si
template <class P>
void BufferCache<P>::DeleteBuffer(BufferId buffer_id, bool do_not_mark) {
+ bool dirty_index{false};
+ boost::container::small_vector<u64, NUM_VERTEX_BUFFERS> dirty_vertex_buffers;
const auto scalar_replace = [buffer_id](Binding& binding) {
if (binding.buffer_id == buffer_id) {
binding.buffer_id = BufferId{};
@@ -1624,8 +1657,19 @@ void BufferCache<P>::DeleteBuffer(BufferId buffer_id, bool do_not_mark) {
const auto replace = [scalar_replace](std::span<Binding> bindings) {
std::ranges::for_each(bindings, scalar_replace);
};
- scalar_replace(channel_state->index_buffer);
- replace(channel_state->vertex_buffers);
+
+ if (channel_state->index_buffer.buffer_id == buffer_id) {
+ channel_state->index_buffer.buffer_id = BufferId{};
+ dirty_index = true;
+ }
+
+ for (u32 index = 0; index < channel_state->vertex_buffers.size(); index++) {
+ auto& binding = channel_state->vertex_buffers[index];
+ if (binding.buffer_id == buffer_id) {
+ binding.buffer_id = BufferId{};
+ dirty_vertex_buffers.push_back(index);
+ }
+ }
std::ranges::for_each(channel_state->uniform_buffers, replace);
std::ranges::for_each(channel_state->storage_buffers, replace);
replace(channel_state->transform_feedback_buffers);
@@ -1642,20 +1686,21 @@ void BufferCache<P>::DeleteBuffer(BufferId buffer_id, bool do_not_mark) {
delayed_destruction_ring.Push(std::move(slot_buffers[buffer_id]));
slot_buffers.erase(buffer_id);
- NotifyBufferDeletion();
-}
-
-template <class P>
-void BufferCache<P>::NotifyBufferDeletion() {
if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) {
channel_state->dirty_uniform_buffers.fill(~u32{0});
channel_state->uniform_buffer_binding_sizes.fill({});
}
+
auto& flags = maxwell3d->dirty.flags;
- flags[Dirty::IndexBuffer] = true;
- flags[Dirty::VertexBuffers] = true;
- for (u32 index = 0; index < NUM_VERTEX_BUFFERS; ++index) {
- flags[Dirty::VertexBuffer0 + index] = true;
+ if (dirty_index) {
+ flags[Dirty::IndexBuffer] = true;
+ }
+
+ if (dirty_vertex_buffers.size() > 0) {
+ flags[Dirty::VertexBuffers] = true;
+ for (auto index : dirty_vertex_buffers) {
+ flags[Dirty::VertexBuffer0 + index] = true;
+ }
}
channel_state->has_deleted_buffers = true;
}
diff --git a/src/video_core/buffer_cache/buffer_cache_base.h b/src/video_core/buffer_cache/buffer_cache_base.h
index 60a1f285e..63a120f7a 100644
--- a/src/video_core/buffer_cache/buffer_cache_base.h
+++ b/src/video_core/buffer_cache/buffer_cache_base.h
@@ -105,6 +105,16 @@ static constexpr Binding NULL_BINDING{
.buffer_id = NULL_BUFFER_ID,
};
+template <typename Buffer>
+struct HostBindings {
+ 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;
+ u32 min_index{NUM_VERTEX_BUFFERS};
+ u32 max_index{0};
+};
+
class BufferCacheChannelInfo : public ChannelInfo {
public:
BufferCacheChannelInfo() = delete;
@@ -519,8 +529,6 @@ private:
void DeleteBuffer(BufferId buffer_id, bool do_not_mark = false);
- void NotifyBufferDeletion();
-
[[nodiscard]] Binding StorageBufferBinding(GPUVAddr ssbo_addr, u32 cbuf_index,
bool is_written) const;
diff --git a/src/video_core/engines/draw_manager.cpp b/src/video_core/engines/draw_manager.cpp
index 0e94c521a..f34090791 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/memory_manager.cpp b/src/video_core/memory_manager.cpp
index 7b2cde7a7..b2f7e160a 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;
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
index c419714d4..38d553d3c 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
@@ -232,6 +232,15 @@ void BufferCacheRuntime::BindVertexBuffer(u32 index, Buffer& buffer, u32 offset,
}
}
+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]));
+ }
+}
+
void BufferCacheRuntime::BindUniformBuffer(size_t stage, u32 binding_index, Buffer& buffer,
u32 offset, u32 size) {
if (use_assembly_shaders) {
@@ -320,6 +329,14 @@ void BufferCacheRuntime::BindTransformFeedbackBuffer(u32 index, Buffer& buffer,
static_cast<GLintptr>(offset), static_cast<GLsizeiptr>(size));
}
+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]));
+ }
+}
+
void BufferCacheRuntime::BindTextureBuffer(Buffer& buffer, u32 offset, u32 size,
PixelFormat format) {
*texture_handles++ = buffer.View(offset, size, format);
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h
index a24991585..41b746f3b 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.h
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.h
@@ -7,7 +7,7 @@
#include <span>
#include "common/common_types.h"
-#include "video_core/buffer_cache/buffer_cache.h"
+#include "video_core/buffer_cache/buffer_cache_base.h"
#include "video_core/buffer_cache/memory_tracker_base.h"
#include "video_core/rasterizer_interface.h"
#include "video_core/renderer_opengl/gl_device.h"
@@ -88,6 +88,8 @@ public:
void BindVertexBuffer(u32 index, Buffer& buffer, u32 offset, u32 size, u32 stride);
+ void BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings);
+
void BindUniformBuffer(size_t stage, u32 binding_index, Buffer& buffer, u32 offset, u32 size);
void BindComputeUniformBuffer(u32 binding_index, Buffer& buffer, u32 offset, u32 size);
@@ -100,6 +102,8 @@ public:
void BindTransformFeedbackBuffer(u32 index, Buffer& buffer, u32 offset, u32 size);
+ 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 1a0cea9b7..3151c0db8 100644
--- a/src/video_core/renderer_opengl/gl_compute_pipeline.cpp
+++ b/src/video_core/renderer_opengl/gl_compute_pipeline.cpp
@@ -87,7 +87,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 +132,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 +142,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 +186,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 +205,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 +241,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_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp
index 400c21981..03d234f2f 100644
--- a/src/video_core/renderer_opengl/gl_device.cpp
+++ b/src/video_core/renderer_opengl/gl_device.cpp
@@ -201,6 +201,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 cc0b95f1a..ad27264e5 100644
--- a/src/video_core/renderer_opengl/gl_device.h
+++ b/src/video_core/renderer_opengl/gl_device.h
@@ -188,6 +188,10 @@ public:
return strict_context_required;
}
+ bool SupportsConditionalBarriers() const {
+ return supports_conditional_barriers;
+ }
+
private:
static bool TestVariableAoffi();
static bool TestPreciseBug();
@@ -233,6 +237,7 @@ private:
bool has_bool_ref_bug{};
bool can_report_memory{};
bool strict_context_required{};
+ bool supports_conditional_barriers{};
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 89000d6e0..c58f760b8 100644
--- a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
+++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
@@ -275,9 +275,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 +337,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 +350,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 +444,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 +467,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 +483,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 +549,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_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 6ecda2984..3f077311e 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -232,12 +232,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 207a75d42..d12cd06fa 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_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index 1c5dbcdd8..3b446be07 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 1148b73d7..3676eaaa9 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 983e1c2e1..71c783709 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_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
index 7cdde992b..acb143fc7 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
@@ -37,10 +37,6 @@
#include "video_core/vulkan_common/vulkan_memory_allocator.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
-#ifdef ANDROID
-extern u32 GetAndroidScreenRotation();
-#endif
-
namespace Vulkan {
namespace {
@@ -78,47 +74,6 @@ struct ScreenRectVertex {
}
};
-#ifdef ANDROID
-
-std::array<f32, 4 * 4> MakeOrthographicMatrix(f32 width, f32 height) {
- constexpr u32 ROTATION_0 = 0;
- constexpr u32 ROTATION_90 = 1;
- constexpr u32 ROTATION_180 = 2;
- constexpr u32 ROTATION_270 = 3;
-
- // clang-format off
- switch (GetAndroidScreenRotation()) {
- case ROTATION_0:
- // Desktop
- return { 2.f / width, 0.f, 0.f, 0.f,
- 0.f, 2.f / height, 0.f, 0.f,
- 0.f, 0.f, 1.f, 0.f,
- -1.f, -1.f, 0.f, 1.f};
- case ROTATION_180:
- // Reverse desktop
- return {-2.f / width, 0.f, 0.f, 0.f,
- 0.f, -2.f / height, 0.f, 0.f,
- 0.f, 0.f, 1.f, 0.f,
- 1.f, 1.f, 0.f, 1.f};
- case ROTATION_270:
- // Reverse landscape
- return { 0.f, -2.f / width, 0.f, 0.f,
- 2.f / height, 0.f, 0.f, 0.f,
- 0.f, 0.f, 1.f, 0.f,
- -1.f, 1.f, 0.f, 1.f};
- case ROTATION_90:
- default:
- // Landscape
- return { 0.f, 2.f / width, 0.f, 0.f,
- -2.f / height, 0.f, 0.f, 0.f,
- 0.f, 0.f, 1.f, 0.f,
- 1.f, -1.f, 0.f, 1.f};
- }
- // clang-format on
-}
-
-#else
-
std::array<f32, 4 * 4> MakeOrthographicMatrix(f32 width, f32 height) {
// clang-format off
return { 2.f / width, 0.f, 0.f, 0.f,
@@ -128,8 +83,6 @@ std::array<f32, 4 * 4> MakeOrthographicMatrix(f32 width, f32 height) {
// clang-format on
}
-#endif
-
u32 GetBytesPerPixel(const Tegra::FramebufferConfig& framebuffer) {
using namespace VideoCore::Surface;
return BytesPerBlock(PixelFormatFromGPUPixelFormat(framebuffer.pixel_format));
@@ -1159,7 +1112,7 @@ void BlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) {
.pNext = nullptr,
.flags = 0,
.imageType = VK_IMAGE_TYPE_2D,
- .format = GetFormat(framebuffer),
+ .format = used_on_framebuffer ? VK_FORMAT_R16G16B16A16_SFLOAT : GetFormat(framebuffer),
.extent =
{
.width = (up_scale * framebuffer.width) >> down_shift,
@@ -1180,14 +1133,14 @@ void BlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) {
const auto create_commit = [&](vk::Image& image) {
return memory_allocator.Commit(image, MemoryUsage::DeviceLocal);
};
- const auto create_image_view = [&](vk::Image& image) {
+ const auto create_image_view = [&](vk::Image& image, bool used_on_framebuffer = false) {
return device.GetLogical().CreateImageView(VkImageViewCreateInfo{
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.image = *image,
.viewType = VK_IMAGE_VIEW_TYPE_2D,
- .format = GetFormat(framebuffer),
+ .format = used_on_framebuffer ? VK_FORMAT_R16G16B16A16_SFLOAT : GetFormat(framebuffer),
.components =
{
.r = VK_COMPONENT_SWIZZLE_IDENTITY,
@@ -1217,7 +1170,7 @@ void BlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) {
const u32 down_shift = Settings::values.resolution_info.down_shift;
aa_image = create_image(true, up_scale, down_shift);
aa_commit = create_commit(aa_image);
- aa_image_view = create_image_view(aa_image);
+ aa_image_view = create_image_view(aa_image, true);
VkExtent2D size{
.width = (up_scale * framebuffer.width) >> down_shift,
.height = (up_scale * framebuffer.height) >> down_shift,
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
index daa128399..e30fcb1ed 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
@@ -7,7 +7,6 @@
#include <span>
#include <vector>
-#include "video_core/buffer_cache/buffer_cache.h"
#include "video_core/renderer_vulkan/maxwell_to_vk.h"
#include "video_core/renderer_vulkan/vk_buffer_cache.h"
#include "video_core/renderer_vulkan/vk_scheduler.h"
@@ -502,6 +501,36 @@ void BufferCacheRuntime::BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset
}
}
+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 handle = bindings.buffers[index]->Handle();
+ if (handle == VK_NULL_HANDLE) {
+ bindings.offsets[index] = 0;
+ bindings.sizes[index] = VK_WHOLE_SIZE;
+ if (!device.HasNullDescriptor()) {
+ ReserveNullBuffer();
+ handle = *null_buffer;
+ }
+ }
+ buffer_handles.push_back(handle);
+ }
+ if (device.IsExtExtendedDynamicStateSupported()) {
+ 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(),
+ bindings.offsets.data(), bindings.sizes.data(), bindings.strides.data());
+ });
+ } else {
+ 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());
+ });
+ }
+}
+
void BufferCacheRuntime::BindTransformFeedbackBuffer(u32 index, VkBuffer buffer, u32 offset,
u32 size) {
if (!device.IsExtTransformFeedbackSupported()) {
@@ -523,6 +552,23 @@ void BufferCacheRuntime::BindTransformFeedbackBuffer(u32 index, VkBuffer buffer,
});
}
+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) {
+ 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() {
if (null_buffer) {
return;
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h
index 92b4f7859..cdeef8846 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.h
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h
@@ -18,6 +18,7 @@ namespace Vulkan {
class Device;
class DescriptorPool;
class Scheduler;
+struct HostVertexBinding;
class BufferCacheRuntime;
@@ -97,8 +98,12 @@ public:
void BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size, u32 stride);
+ void BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings);
+
void BindTransformFeedbackBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size);
+ void BindTransformFeedbackBuffers(VideoCommon::HostBindings<Buffer>& bindings);
+
std::span<u8> BindMappedUniformBuffer([[maybe_unused]] size_t stage,
[[maybe_unused]] u32 binding_index, u32 size) {
const StagingBufferRef ref = staging_pool.Request(size, MemoryUsage::Upload);
diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
index 733e70d9d..73e585c2b 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 506b78f08..c1595642e 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 b128c4f6e..5eeda08d2 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"
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 9482e91b0..a2cfb2105 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -350,6 +350,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 +358,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 +705,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 15aa7e224..e323ea0fd 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_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp
index afcf34fba..d3cddac69 100644
--- a/src/video_core/renderer_vulkan/vk_swapchain.cpp
+++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp
@@ -231,7 +231,12 @@ void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bo
.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr,
+#ifdef ANDROID
+ // On Android, do not allow surface rotation to deviate from the frontend.
+ .preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR,
+#else
.preTransform = capabilities.currentTransform,
+#endif
.compositeAlpha = alpha_flags,
.presentMode = present_mode,
.clipped = VK_FALSE,
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index 8711e2a87..f025f618b 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -1802,27 +1802,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,
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h
index 0f7a5ffd4..f14525dcb 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/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp
index e8ddde691..b72788c6d 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 d134b6738..0c5f4450d 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 a25ae1d4a..87549ffff 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 c7f7448e9..d58bb69ff 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;
@@ -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;
diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h
index 3bfa92154..44232b961 100644
--- a/src/video_core/texture_cache/texture_cache_base.h
+++ b/src/video_core/texture_cache/texture_cache_base.h
@@ -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>
diff --git a/src/video_core/textures/texture.cpp b/src/video_core/textures/texture.cpp
index 4a80a59f9..d8b88d9bc 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/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index 0158b6b0d..fa9cde75b 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -344,6 +344,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 +387,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 +411,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 +431,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 +562,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 +786,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 +804,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 d62a103a1..0b634a876 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 84d9ca796..733c296e4 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -210,6 +210,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/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 6288fef62..edc206a25 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -101,6 +101,12 @@ const std::map<Settings::RendererBackend, QString> Config::renderer_backend_text
{Settings::RendererBackend::Null, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Null"))},
};
+const std::map<Settings::ShaderBackend, QString> Config::shader_backend_texts_map = {
+ {Settings::ShaderBackend::GLSL, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLSL"))},
+ {Settings::ShaderBackend::GLASM, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLASM"))},
+ {Settings::ShaderBackend::SPIRV, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "SPIRV"))},
+};
+
// This shouldn't have anything except static initializers (no functions). So
// QKeySequence(...).toString() is NOT ALLOWED HERE.
// This must be in alphabetical order according to action name as it must have the same order as
@@ -754,6 +760,8 @@ void Config::ReadRendererValues() {
ReadGlobalSetting(Settings::values.use_fast_gpu_time);
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);
@@ -1409,6 +1417,8 @@ void Config::SaveRendererValues() {
WriteGlobalSetting(Settings::values.use_fast_gpu_time);
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/config.h b/src/yuzu/configuration/config.h
index ad590ea9e..0fd4baf6b 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -54,6 +54,7 @@ public:
static const std::map<bool, QString> use_docked_mode_texts_map;
static const std::map<Settings::GPUAccuracy, QString> gpu_accuracy_texts_map;
static const std::map<Settings::RendererBackend, QString> renderer_backend_texts_map;
+ static const std::map<Settings::ShaderBackend, QString> shader_backend_texts_map;
static constexpr UISettings::Theme default_theme{
#ifdef _WIN32
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index 8e76a819a..bdf83ebfe 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 a086a07c4..2a08b7fee 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 431585216..a4965524a 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 364b1cac2..be9310b74 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 896863f87..c0a044767 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.cpp
+++ b/src/yuzu/configuration/configure_graphics_advanced.cpp
@@ -42,6 +42,9 @@ void ConfigureGraphicsAdvanced::SetConfiguration() {
Settings::values.use_vulkan_driver_pipeline_cache.GetValue());
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(
@@ -91,6 +94,11 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() {
ConfigurationShared::ApplyPerGameSetting(&Settings::values.enable_compute_pipelines,
ui->enable_compute_pipelines_checkbox,
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) {
@@ -125,6 +133,10 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
Settings::values.max_anisotropy.UsingGlobal());
ui->enable_compute_pipelines_checkbox->setEnabled(
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;
}
@@ -149,6 +161,12 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
ConfigurationShared::SetColoredTristate(ui->enable_compute_pipelines_checkbox,
Settings::values.enable_compute_pipelines,
enable_compute_pipelines);
+ 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 1c7b636b9..369a7c83e 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.h
+++ b/src/yuzu/configuration/configure_graphics_advanced.h
@@ -47,6 +47,8 @@ private:
ConfigurationShared::CheckState use_fast_gpu_time;
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 37757a918..d527a6f38 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.ui
+++ b/src/yuzu/configuration/configure_graphics_advanced.ui
@@ -192,6 +192,26 @@ Compute pipelines are always enabled on all other drivers.</string>
</widget>
</item>
<item>
+ <widget class="QCheckBox" name="use_video_framerate_checkbox">
+ <property name="toolTip">
+ <string>Run the game at normal speed during video playback, even when the framerate is unlocked.</string>
+ </property>
+ <property name="text">
+ <string>Sync to framerate of video playback</string>
+ </property>
+ </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 7ac162586..eb96e6068 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 85752f1fa..7ec1ded06 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_system.cpp b/src/yuzu/configuration/configure_system.cpp
index 286ccc5cd..f1ae312c6 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 145fea5f1..8768a7c3c 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,6 +441,8 @@ 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)
@@ -3067,7 +3070,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 +3497,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 +3769,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();
@@ -4116,7 +4120,13 @@ void GMainWindow::UpdateDockedButton() {
void GMainWindow::UpdateAPIText() {
const auto api = Settings::values.renderer_backend.GetValue();
const auto renderer_status_text = Config::renderer_backend_texts_map.find(api)->second;
- renderer_status_button->setText(renderer_status_text.toUpper());
+ renderer_status_button->setText(
+ api == Settings::RendererBackend::OpenGL
+ ? tr("%1 %2").arg(
+ renderer_status_text.toUpper(),
+ Config::shader_backend_texts_map.find(Settings::values.shader_backend.GetValue())
+ ->second)
+ : renderer_status_text.toUpper());
}
void GMainWindow::UpdateFilterText() {
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 6bb70972f..e0e775d87 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;
diff --git a/src/yuzu/vk_device_info.cpp b/src/yuzu/vk_device_info.cpp
new file mode 100644
index 000000000..7c26a3dc7
--- /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 000000000..bda8262f4
--- /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 26f545c6c..7d9e631a1 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",